From 9032d173e98fb3d4edeb5a3cfad1fcfd31438107 Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Wed, 17 Feb 2021 11:16:24 -0600
Subject: [PATCH 01/55] chore(deps): taskypi@1.6.0
add taskypi as a general task runner. The `scripts` in the
pyproject.toml file are added as runnable script to the user path when
they install the package. This is not the desirable outcome and can lead
to confusing behviors.
This allows up to implement develomnet tasks similar to npm script with
out effecting the installable package or host environment
---
poetry.lock | 72 +++++++++++++++++++++++++++++++++++++++++++++++++-
pyproject.toml | 1 +
2 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/poetry.lock b/poetry.lock
index 93c6849..6aceb28 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -198,6 +198,14 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "mslex"
+version = "0.3.0"
+description = "shlex for windows"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
[[package]]
name = "packaging"
version = "20.9"
@@ -265,6 +273,17 @@ python-versions = ">=3.6"
[package.dependencies]
wcwidth = "*"
+[[package]]
+name = "psutil"
+version = "5.8.0"
+description = "Cross-platform lib for process and system monitoring in Python."
+category = "dev"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
+
[[package]]
name = "ptyprocess"
version = "0.7.0"
@@ -387,6 +406,19 @@ python-versions = "*"
[package.extras]
yaml = ["more-itertools", "PyYAML (>=5.1)"]
+[[package]]
+name = "taskipy"
+version = "1.6.0"
+description = "tasks runner for python projects"
+category = "dev"
+optional = false
+python-versions = ">=3.6,<4.0"
+
+[package.dependencies]
+mslex = ">=0.3.0,<0.4.0"
+psutil = ">=5.7.2,<6.0.0"
+toml = ">=0.10.0,<0.11.0"
+
[[package]]
name = "toml"
version = "0.10.2"
@@ -463,7 +495,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
-content-hash = "b357baa9d439bc7f7cf618918ae08e8da1f71844547172b4e448193c0f6bbb10"
+content-hash = "f860b4efd8c9effba27c1d04219d50794d74208204b7e3efca8ada9686fafea9"
[metadata.files]
appnope = [
@@ -584,6 +616,10 @@ mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
+mslex = [
+ {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"},
+ {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"},
+]
packaging = [
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
@@ -608,6 +644,36 @@ prompt-toolkit = [
{file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"},
{file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"},
]
+psutil = [
+ {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"},
+ {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"},
+ {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"},
+ {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"},
+ {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"},
+ {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"},
+ {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"},
+ {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"},
+ {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"},
+ {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"},
+ {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"},
+ {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"},
+ {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"},
+ {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"},
+ {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"},
+ {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"},
+ {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"},
+ {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"},
+ {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"},
+ {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"},
+ {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"},
+ {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"},
+ {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"},
+ {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"},
+ {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"},
+ {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"},
+ {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"},
+ {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
+]
ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
@@ -652,6 +718,10 @@ six = [
{file = "tap.py-3.0-py2.py3-none-any.whl", hash = "sha256:a598bfaa2e224d71f2e86147c2ef822c18ff2e1b8ef006397e5056b08f92f699"},
{file = "tap.py-3.0.tar.gz", hash = "sha256:f5eeeeebfd64e53d32661752bb4c288589a3babbb96db3f391a4ec29f1359c70"},
]
+taskipy = [
+ {file = "taskipy-1.6.0-py3-none-any.whl", hash = "sha256:33ee1d52b378cb4af3678fc459b75c3028f594c5e8e42ac0696cbe3e95d47394"},
+ {file = "taskipy-1.6.0.tar.gz", hash = "sha256:ec4d1f2208ae24218950e3a2812e4e8b4397b1f65a6ad7e2b1240b702042fa3e"},
+]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
diff --git a/pyproject.toml b/pyproject.toml
index 1711ecb..15df8c8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,6 +17,7 @@ flake8 = "^3.8.4"
yapf = "^0.30.0"
pytest = "^6.2.2"
pytest-cov = "^2.11.1"
+taskipy = "^1.6.0"
[tool.poetry.scripts]
test = "scripts.test:run"
From f5a8b182941a0c514594cbaa7a3ac5952173d5a8 Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Wed, 17 Feb 2021 12:11:04 -0600
Subject: [PATCH 02/55] feat(tasks): convert run scripts to tasks
This changes the poetry run script to use the single task runner. This
will prevent the scripts from being included and installed with the
package
---
README.md | 10 +++++---
pyproject.toml | 32 ++++++++++++++++++++++----
scripts/json_coverage.py | 49 ++++++++++++++++++++++++++++++++++++++++
scripts/lint.py | 20 ----------------
scripts/test.py | 25 --------------------
setup.cfg | 21 -----------------
setup.py | 9 +++++++-
7 files changed, 91 insertions(+), 75 deletions(-)
create mode 100644 scripts/json_coverage.py
delete mode 100644 scripts/lint.py
delete mode 100644 scripts/test.py
diff --git a/README.md b/README.md
index 431d348..817e7a9 100644
--- a/README.md
+++ b/README.md
@@ -325,13 +325,17 @@ The time in seconds since the epoch to use for the log timestamp. It must be wit
This project makes use of the [poetry][] package manager for local development.
+```shell
+$ poetry isntall
+```
+
### Scripts
**lint**
Run linting rules w/o attempting to fix them
```shell
-$ poetry run lint
+$ poetry run task lint
```
@@ -341,7 +345,7 @@ Run lint rules against all local python files and attempt to fix where possible.
```shell
-$ poetry run lint:fix
+$ poetry run task lint:fix
```
**test**:
@@ -349,7 +353,7 @@ $ poetry run lint:fix
Runs all unit tests and generates coverage reports
```shell
-poetry run test
+poetry run task test
```
## Contributors ✨
diff --git a/pyproject.toml b/pyproject.toml
index 15df8c8..10619cf 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[tool.poetry]
name = "logdna"
version = "1.5.4"
-description = "A python package for sending logs"
+description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
@@ -19,11 +19,33 @@ pytest = "^6.2.2"
pytest-cov = "^2.11.1"
taskipy = "^1.6.0"
-[tool.poetry.scripts]
-test = "scripts.test:run"
-lint = "scripts.lint:run"
-"lint:fix" = "scripts.lint:fix"
+[tool.taskipy.tasks]
+pre_test = "mkdir coverage -p"
+test = "pytest --junitxml=coverage/test.xml --cov=logdna --cov-report=html"
+post_test = "python scripts/json_coverage.py"
+lint = "flake8 --doctests"
+"lint:fix" = "yapf -r -i logdna scripts tests"
+"post_lint:fix" = "task lint"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
+
+[tool.pytest.ini_options]
+minversion = "6.0"
+testpaths = "tests"
+
+[tool.coverage.run]
+branch = true
+source = ["logdna"]
+
+[tool.coverage.report]
+fail_under = 67
+show_missing = true
+
+[tool.coverage.json]
+output = "coverage/coverage.json"
+
+[tool.coverage.html]
+directory = "coverage"
+show_contexts = true
diff --git a/scripts/json_coverage.py b/scripts/json_coverage.py
new file mode 100644
index 0000000..c78174e
--- /dev/null
+++ b/scripts/json_coverage.py
@@ -0,0 +1,49 @@
+import json
+from os import path
+from coverage import Coverage
+
+ROOT = path.realpath(path.abspath(path.join(path.dirname(__file__), '..')))
+COVERAGE_DIR = path.join(ROOT, 'coverage')
+JUNIT_PATH = path.join(COVERAGE_DIR, 'test.xml')
+
+
+def json_coverage():
+ COVERAGE_FILE = path.join(COVERAGE_DIR, 'coverage-final.json')
+ COVERAGE_SUMMARY = path.join(COVERAGE_DIR, 'coverage-summary.json')
+
+ coverage = Coverage(config_file=path.join(ROOT, 'pyproject.toml'))
+ coverage.load()
+ coverage.json_report(outfile=COVERAGE_FILE)
+
+ report = json.load(open(COVERAGE_FILE))
+ totals = report.get('totals')
+ summary = {
+ 'lines': {
+ 'total': totals['covered_lines'] + totals['missing_lines'],
+ 'covered': totals['covered_lines'],
+ 'pct': totals['covered_lines'] / (
+ totals['covered_lines'] + totals['missing_lines']
+ ) * 100
+ },
+ 'statements': {
+ 'total': None,
+ 'covered': None,
+ 'pct': None,
+ },
+ 'functions': {
+ 'total': None,
+ 'covered': None,
+ 'pct': None,
+ },
+ 'branches': {
+ 'total': totals['num_branches'],
+ 'covered': totals['covered_branches'],
+ 'pct': totals['covered_branches'] / totals['num_branches'] * 100
+ }
+ }
+
+ json.dump({'total': summary}, open(COVERAGE_SUMMARY, 'w'))
+
+
+if __name__ == "__main__":
+ json_coverage()
diff --git a/scripts/lint.py b/scripts/lint.py
deleted file mode 100644
index 87bc65f..0000000
--- a/scripts/lint.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from os import path
-from subprocess import check_call
-
-ROOT = path.realpath(path.abspath(path.join(path.dirname(__file__), '..')))
-
-
-def run():
- check_call(['flake8', '--doctests'])
-
-
-def fix():
- check_call(cwd=ROOT,
- args=[
- 'yapf', '-r', '-i',
- path.join(ROOT, 'logdna'),
- path.join(ROOT, 'scripts'),
- path.join(ROOT, 'tests')
- ])
-
- run()
diff --git a/scripts/test.py b/scripts/test.py
deleted file mode 100644
index 7c95bf4..0000000
--- a/scripts/test.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import os
-from os import path
-from subprocess import check_call
-
-ROOT = path.realpath(path.abspath(path.join(path.dirname(__file__), '..')))
-COVERAGE_DIR = path.join(ROOT, 'coverage')
-JUNIT_PATH = path.join(COVERAGE_DIR, 'test.xml')
-print(JUNIT_PATH)
-
-
-def run():
- try:
- os.mkdir(COVERAGE_DIR)
- except FileExistsError:
- pass
-
- check_call([
- 'pytest',
- '--junitxml={0}'.format(JUNIT_PATH),
- '--cov=logdna',
- '--cov-report=html',
- '--cov-report=xml',
- '--cov-config=setup.cfg',
- '--cov-branch'
- ])
diff --git a/setup.cfg b/setup.cfg
index 92b7bc5..a8d4c5a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,3 @@
-[metadata]
-description-file = README.md
-
[flake8]
exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
max-complexity = 10
@@ -10,21 +7,3 @@ based_on_style = pep8
indent_width = 4
use_tabs = False
-[tool:pytest]
-minversion = 6.0
-testpaths = tests
-
-[coverage:run]
-branch = True
-source = logdna/*
-
-[coverage:html]
-directory = coverage
-show_contexts = True
-
-[coverage:xml]
-output = coverage/coverage.xml
-
-[coverage:report]
-fail_under = 67
-
diff --git a/setup.py b/setup.py
index 58289e2..ffafd56 100644
--- a/setup.py
+++ b/setup.py
@@ -25,7 +25,14 @@
install_requires=[
'requests',
],
- classifiers=[],
+ classifiers=[
+ 'Topic :: System :: Logging',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9'
+ ],
long_description=long_description,
long_description_content_type='text/markdown',
)
From 1c5be37ef32776f2d0ba2b68b67cebb58b1f0177 Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Wed, 17 Feb 2021 13:03:08 -0600
Subject: [PATCH 03/55] feat(ci): include jenkins setup
adds a jenkins file to run tests + build on tags. This resembles the
circle ci setup with the exception of the github release setup.
This will be re-implemented with semantic-release
---
.circleci/config.yml | 103 -------------------------------------------
.gitignore | 3 +-
Jenkinsfile | 78 ++++++++++++++++++++++++++++++++
README.md | 9 ++--
setup.cfg | 3 ++
5 files changed, 88 insertions(+), 108 deletions(-)
delete mode 100644 .circleci/config.yml
create mode 100644 Jenkinsfile
diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index 391359e..0000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,103 +0,0 @@
-version: 2.1
-tagged_build_filters: &tagged_build_filters
- branches:
- ignore: /.*/
- tags:
- only: /[0-9]+\.[0-9]+\.[0-9]+/
-test_build_filters: &test_build_filters
- branches:
- only: /.*/
- tags:
- ignore: /[0-9]+\.[0-9]+\.[0-9]+/
-jobs:
- test:
- docker:
- - image: circleci/python:3
- steps:
- - checkout
- - run: poetry install
- - run: poetry run lint
- - run: poetry run test
- build:
- docker:
- - image: circleci/python:3
- steps:
- - checkout
- - run:
- name: Check Tagged Push
- command: |
- PKG_VERSION=$(cat logdna/VERSION)
- if [[ "${CIRCLE_TAG}" != "${PKG_VERSION}" ]]; then
- echo "There is mismatch:"
- echo " TAG_VERSION: ${CIRCLE_TAG}"
- echo " PKG_VERSION: ${PKG_VERSION}"
- exit 1
- fi
- - run: python setup.py sdist
- - persist_to_workspace:
- root: .
- paths:
- - ./dist/logdna-*.tar.gz
- release:
- docker:
- - image: circleci/golang:1.12
- steps:
- - attach_workspace:
- at: .
- - run: go get -u github.com/tcnksm/ghr
- - run:
- name: Create a Release
- command: |
- ghr \
- -n "LogDNA Python Logger v${CIRCLE_TAG}" \
- -t ${GITHUB_TOKEN} \
- -u ${CIRCLE_PROJECT_USERNAME} \
- -r ${CIRCLE_PROJECT_REPONAME} \
- -draft ${CIRCLE_TAG} ./dist/
- - persist_to_workspace:
- root: .
- paths:
- - ./dist/logdna-*.tar.gz
- approve:
- machine: true
- steps:
- - attach_workspace:
- at: .
- - persist_to_workspace:
- root: .
- paths:
- - ./dist/logdna-*.tar.gz
- publish:
- docker:
- - image: circleci/python:3
- steps:
- - attach_workspace:
- at: .
- - run: sudo pip install twine
- - run: twine upload dist/logdna-*.tar.gz
-workflows:
- update:
- jobs:
- - test:
- filters: *test_build_filters
- - build:
- requires:
- - test
- filters: *tagged_build_filters
- - release:
- requires:
- - build
- filters: *tagged_build_filters
- - approve:
- type: approval
- requires:
- - release
- filters: *tagged_build_filters
- - publish:
- requires:
- - approve
- filters: *tagged_build_filters
-test:
- jobs:
- - test:
- filters: *test_build_filters
diff --git a/.gitignore b/.gitignore
index 5d0eed0..fe9e6fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-**/*.pyc
+*.pyc
**/*cache*
build/
develop-eggs
@@ -11,3 +11,4 @@ bin
coverage/
.coverage
Session.vim
+.cache
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..195c815
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,78 @@
+library 'magic-butler-catalogue'
+def PROJECT_NAME = 'logdna-python'
+def TRIGGER_PATTERN = ".*@logdnabot.*"
+
+pipeline {
+ agent none
+
+ options {
+ timestamps()
+ ansiColor 'xterm'
+ }
+
+ triggers {
+ issueCommentTrigger(TRIGGER_PATTERN)
+ }
+
+ stages {
+ stage('Test') {
+ agent {
+ docker {
+ image "us.gcr.io/logdna-k8s/python:3.7-ci"
+ customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
+ }
+ }
+
+ environment {
+ XDG_CONFIG_HOME = pwd()
+ POETRY_VIRTUALENV_IN_PROJECT = true
+ }
+
+ steps {
+ sh 'poetry config --list --local'
+ sh 'poetry install --no-interaction -vvv'
+ sh 'poetry run task lint'
+ sh 'poetry run task test'
+ }
+
+ post {
+ always {
+ junit 'coverage/test.xml'
+ publishHTML target: [
+ allowMissing: false,
+ alwaysLinkToLastBuild: false,
+ keepAll: true,
+ reportDir: 'coverage',
+ reportFiles: 'index.html',
+ reportName: "coverage-${BUILD_TAG}"
+ ]
+ }
+ }
+ }
+
+ stage('Release') {
+ when {
+ tag pattern: "[0-9]+\\.[0-9]+\\.[0-9]+", comparator: "REGEXP"
+ }
+
+ agent {
+ docker {
+ image "us.gcr.io/logdna-k8s/python:3.7-ci"
+ customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
+ }
+ }
+ steps {
+ witCredentials([
+ usernamePassword(
+ credentialsId: 'pypi-username-password',
+ usernameVariable: 'TWINE_USERNAME',
+ passwordVariable: 'TWINE_PASSWORD'
+ )
+ ]){
+ sh 'python setup.py sdist'
+ sh 'twine upload dist/logdna-*.tar.gz'
+ }
+ }
+ }
+ }
+}
diff --git a/README.md b/README.md
index 817e7a9..8aec2fc 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,14 @@
-
-[](#contributors-)
-
Python package for logging to LogDNA
+
+[](#contributors-)
+
+
---
* [Installation](#installation)
@@ -326,7 +327,7 @@ The time in seconds since the epoch to use for the log timestamp. It must be wit
This project makes use of the [poetry][] package manager for local development.
```shell
-$ poetry isntall
+$ poetry install
```
### Scripts
diff --git a/setup.cfg b/setup.cfg
index a8d4c5a..bf13f84 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,3 +1,6 @@
+[metadata]
+description-file = README.md
+
[flake8]
exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
max-complexity = 10
From 1941935597e2250579d2876b2234c648fd236f1b Mon Sep 17 00:00:00 2001
From: Mark Curtis
Date: Tue, 9 Mar 2021 18:57:13 +0000
Subject: [PATCH 04/55] Use os separator instead of hard coded /
* Use os seperator instead of hard coded /
* correct formatting
* tweak import statements
---
logdna/configs.py | 5 +++--
scripts/json_coverage.py | 12 +++++++-----
setup.py | 6 ++++--
tests/test_utils.py | 6 ++----
4 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/logdna/configs.py b/logdna/configs.py
index 3c808c9..a35d32d 100644
--- a/logdna/configs.py
+++ b/logdna/configs.py
@@ -1,6 +1,7 @@
-from os import path
+from os import path, sep
-with open("%s/VERSION" % path.abspath(path.dirname(__file__))) as f:
+with open("{p}{s}VERSION".format(p=path.abspath(path.dirname(__file__)),
+ s=sep)) as f:
version = f.read().strip('\n')
defaults = {
diff --git a/scripts/json_coverage.py b/scripts/json_coverage.py
index c78174e..563d123 100644
--- a/scripts/json_coverage.py
+++ b/scripts/json_coverage.py
@@ -19,11 +19,13 @@ def json_coverage():
totals = report.get('totals')
summary = {
'lines': {
- 'total': totals['covered_lines'] + totals['missing_lines'],
- 'covered': totals['covered_lines'],
- 'pct': totals['covered_lines'] / (
- totals['covered_lines'] + totals['missing_lines']
- ) * 100
+ 'total':
+ totals['covered_lines'] + totals['missing_lines'],
+ 'covered':
+ totals['covered_lines'],
+ 'pct':
+ totals['covered_lines'] /
+ (totals['covered_lines'] + totals['missing_lines']) * 100
},
'statements': {
'total': None,
diff --git a/setup.py b/setup.py
index ffafd56..34386cf 100644
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,5 @@
from setuptools import setup
-from os import path
+from os import path, sep
# read the contents of your README file
this_directory = path.abspath(path.dirname(__file__))
@@ -7,7 +7,9 @@
with open(path.join(this_directory, 'README.md'), 'rb') as f:
long_description = f.read().decode('utf-8')
-with open("%s/logdna/VERSION" % this_directory) as f:
+with open("{p}{s}logdna{s}VERSION"
+ .format(p=this_directory, s=sep)
+ ) as f:
version = f.read().strip('\n')
setup(
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 59fee87..21c990b 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -42,9 +42,7 @@ def test_get_ip_socket_error(self, _):
self.assertEqual(get_ip(), '127.0.0.1',
'default to localhost on error')
- @patch(
- 'socket.socket',
- **{'return_value.getsockname.return_value': [IP, VIP]}
- )
+ @patch('socket.socket',
+ **{'return_value.getsockname.return_value': [IP, VIP]})
def test_get_ip_default(self, _):
self.assertEqual(get_ip(), IP, 'default to localhost on error')
From 9e4b1c0a43bc0941ba4fb336ea12a3f497622ee6 Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Mon, 5 Apr 2021 11:44:20 -0500
Subject: [PATCH 05/55] feat(ci): Enable releases through semantic release
Include semantic release as a dev dependency to automate package
releases. Updated Jenkinsfile to run release process through semantic
release
---
.config.mk | 9 +
.gitignore | 2 +
Jenkinsfile | 44 ++---
Makefile | 85 +++++++++
poetry.lock | 493 ++++++++++++++++++++++++++++++++++++++++++++++++-
pyproject.toml | 9 +
setup.cfg | 2 +-
setup.py | 5 +-
8 files changed, 618 insertions(+), 31 deletions(-)
create mode 100644 .config.mk
create mode 100644 Makefile
diff --git a/.config.mk b/.config.mk
new file mode 100644
index 0000000..65038c5
--- /dev/null
+++ b/.config.mk
@@ -0,0 +1,9 @@
+# Below is an example of pulling the current version of a node app.
+
+#VERSION is being deprecated by APP_VERSION - no changes necessary - see Makefile
+#APP_VERSION=$(shell awk '/version/ {gsub(/[",]/,""); print $$2}' package.json)
+
+GIT_AUTHOR_NAME ?= $(shell git config --get user.name)
+GIT_AUTHOR_EMAIL ?= $(shell git config --get user.email)
+GIT_COMMITTER_NAME ?= $(GIT_AUTHOR_NAME)
+GIT_COMMITTER_EMAIL ?= $(GIT_AUTHOR_EMAIL)
diff --git a/.gitignore b/.gitignore
index fe9e6fa..dc6e372 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@ coverage/
.coverage
Session.vim
.cache
+pypoetry/
+pip/
diff --git a/Jenkinsfile b/Jenkinsfile
index 195c815..e97e2ec 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -1,6 +1,7 @@
library 'magic-butler-catalogue'
def PROJECT_NAME = 'logdna-python'
def TRIGGER_PATTERN = ".*@logdnabot.*"
+def DEFAULT_BRANCH = 'master'
pipeline {
agent none
@@ -17,22 +18,14 @@ pipeline {
stages {
stage('Test') {
agent {
- docker {
- image "us.gcr.io/logdna-k8s/python:3.7-ci"
+ node {
+ label 'ec2-fleet'
customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
}
}
- environment {
- XDG_CONFIG_HOME = pwd()
- POETRY_VIRTUALENV_IN_PROJECT = true
- }
-
steps {
- sh 'poetry config --list --local'
- sh 'poetry install --no-interaction -vvv'
- sh 'poetry run task lint'
- sh 'poetry run task test'
+ sh 'make install lint test'
}
post {
@@ -51,26 +44,25 @@ pipeline {
}
stage('Release') {
- when {
- tag pattern: "[0-9]+\\.[0-9]+\\.[0-9]+", comparator: "REGEXP"
- }
-
agent {
- docker {
- image "us.gcr.io/logdna-k8s/python:3.7-ci"
+ node {
+ label 'ec2-fleet'
customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
}
}
+
+ environment {
+ GITHUB_TOKEN = credentials('github-api-token')
+ PYPI_TOKEN = credentials('pypi-token')
+ }
+
steps {
- witCredentials([
- usernamePassword(
- credentialsId: 'pypi-username-password',
- usernameVariable: 'TWINE_USERNAME',
- passwordVariable: 'TWINE_PASSWORD'
- )
- ]){
- sh 'python setup.py sdist'
- sh 'twine upload dist/logdna-*.tar.gz'
+ script {
+ if ("${BRANCH_NAME}" == "${DEFAULT_BRANCH}") {
+ sh 'make release'
+ } else {
+ sh "BRANCH_NAME=${DEFAULT_BRANCH} CHANGE_ID='' make release-dry"
+ }
}
}
}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0b689d1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,85 @@
+# Makefile Version 2021032403
+#
+# Source in repository specific environment variables
+include .config.mk
+
+# Define commands via docker
+DOCKER = docker
+DOCKER_RUN := $(DOCKER) run --rm -i
+WORKDIR :=/workdir
+DOCKER_COMMAND := $(DOCKER_RUN) -v $(PWD):$(WORKDIR):Z -w $(WORKDIR) \
+ -e XDG_CONFIG_HOME=$(WORKDIR) \
+ -e XDG_CACHE_HOME=$(WORKDIR) \
+ -e POETRY_CACHE_DIR=$(WORKDIR)/.cache \
+ -e POETRY_VIRTUALENV_IN_PROJECT=true \
+ -e PYPI_TOKEN \
+ -e GITHUB_TOKEN \
+ -e GIT_AUTHOR_NAME \
+ -e GIT_AUTHOR_EMAIL \
+ -e GIT_COMMITTER_NAME \
+ -e GIT_COMMITTER_EMAIL \
+ us.gcr.io/logdna-k8s/python:3.7-ci
+
+POETRY_COMMAND := $(DOCKER_COMMAND) poetry
+
+# Exports the variables for shell use
+export
+
+# This helper function makes debugging much easier.
+.PHONY:debug-%
+debug-%: ## Debug a variable by calling `make debug-VARIABLE`
+ @echo $(*) = $($(*))
+
+.PHONY:help
+.SILENT:help
+help: ## Show this help, includes list of all actions.
+ @awk 'BEGIN {FS = ":.*?## "}; /^.+: .*?## / && !/awk/ {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' ${MAKEFILE_LIST}
+
+.PHONY:clean
+clean: ## purge build time artifacts
+ rm -rf dist/ build/ coverage/ pypoetry/ pip/ .cache
+
+.PHONY:changelog
+changelog: ## print the next version of the change log to stdout
+ $(POETRY_COMMAND) run semantic-release changelog --unreleased
+
+.PHONY:install
+install: ## install development and build time dependencies
+ $(POETRY_COMMAND) install --no-interaction -vvv
+
+.PHONY:lint
+lint: ## run lint rules and print error report
+ $(POETRY_COMMAND) run task lint
+
+.PHONY:lint-fix
+lint-fix:## attempt to auto fix linting error and report remaining errors
+ $(POETRY_COMMAND) run task lint:fix
+
+.PHONY:package
+package: ## Generate a python sdist and wheel
+ $(POETRY_COMMAND) build
+
+.PHONY:release
+release: clean install ## run semantic release build and publish results to github + pypi based on unreleased commits
+ $(POETRY_COMMAND) run task release
+
+.PHONY:release-dry
+release-dry: clean install changelog ## run semantic release in noop mode
+ $(POETRY_COMMAND) run semantic-release publish --noop --verbosity=DEBUG
+
+.PHONY:release-patch
+release-patch: clean install ## run semantic release build and force a patch release
+ $(POETRY_COMMAND) run semantic-release publish --patch
+
+.PHONY:release-minor
+release-minor: clean install ## run semantic release build and force a minor release
+ $(POETRY_COMMAND) run semantic-release publish --minor
+
+.PHONY:release-major
+release-major: clean install ## run semantic release build and force a major release
+ $(POETRY_COMMAND) run semantic-release publish --major
+
+.PHONY:test
+test: ## run project test suite
+ $(POETRY_COMMAND) run task test
+
diff --git a/poetry.lock b/poetry.lock
index 6aceb28..8490cff 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -36,6 +36,19 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "bleach"
+version = "3.3.0"
+description = "An easy safelist-based HTML-sanitizing tool."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+packaging = "*"
+six = ">=1.9.0"
+webencodings = "*"
+
[[package]]
name = "certifi"
version = "2020.12.5"
@@ -44,6 +57,17 @@ category = "main"
optional = false
python-versions = "*"
+[[package]]
+name = "cffi"
+version = "1.14.5"
+description = "Foreign Function Interface for Python calling C code."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pycparser = "*"
+
[[package]]
name = "chardet"
version = "4.0.0"
@@ -52,6 +76,25 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+[[package]]
+name = "click"
+version = "7.1.2"
+description = "Composable command line interface toolkit"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "click-log"
+version = "0.3.2"
+description = "Logging integration for Click"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+click = "*"
+
[[package]]
name = "colorama"
version = "0.4.4"
@@ -71,6 +114,25 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
[package.extras]
toml = ["toml"]
+[[package]]
+name = "cryptography"
+version = "3.4.7"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+category = "dev"
+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.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
+
[[package]]
name = "decorator"
version = "4.4.2"
@@ -79,6 +141,25 @@ category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
+[[package]]
+name = "docutils"
+version = "0.17"
+description = "Docutils -- Python Documentation Utilities"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "dotty-dict"
+version = "1.3.0"
+description = "Dictionary wrapper for quick access to deeply nested keys."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+setuptools_scm = "*"
+
[[package]]
name = "flake8"
version = "3.8.4"
@@ -93,6 +174,28 @@ mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.6.0a1,<2.7.0"
pyflakes = ">=2.2.0,<2.3.0"
+[[package]]
+name = "gitdb"
+version = "4.0.7"
+description = "Git Object Database"
+category = "dev"
+optional = false
+python-versions = ">=3.4"
+
+[package.dependencies]
+smmap = ">=3.0.1,<5"
+
+[[package]]
+name = "gitpython"
+version = "3.1.14"
+description = "Python Git Library"
+category = "dev"
+optional = false
+python-versions = ">=3.4"
+
+[package.dependencies]
+gitdb = ">=4.0.1,<5"
+
[[package]]
name = "idna"
version = "2.10"
@@ -125,6 +228,14 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "invoke"
+version = "1.5.0"
+description = "Pythonic task execution"
+category = "dev"
+optional = false
+python-versions = "*"
+
[[package]]
name = "ipdb"
version = "0.13.4"
@@ -190,6 +301,35 @@ parso = ">=0.8.0,<0.9.0"
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"]
+[[package]]
+name = "jeepney"
+version = "0.6.0"
+description = "Low-level, pure Python DBus protocol wrapper."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio"]
+
+[[package]]
+name = "keyring"
+version = "22.3.0"
+description = "Store and access your passwords safely."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+importlib-metadata = {version = ">=1", markers = "python_version < \"3.8\""}
+jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""}
+pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""}
+SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""}
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
[[package]]
name = "mccabe"
version = "0.6.1"
@@ -248,6 +388,17 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "pkginfo"
+version = "1.7.0"
+description = "Query metadatdata from sdists / bdists / installed packages."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.extras]
+testing = ["nose", "coverage"]
+
[[package]]
name = "pluggy"
version = "0.13.1"
@@ -308,6 +459,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+[[package]]
+name = "pycparser"
+version = "2.20"
+description = "C parser in Python"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
[[package]]
name = "pyflakes"
version = "2.2.0"
@@ -369,6 +528,72 @@ pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"]
+[[package]]
+name = "python-gitlab"
+version = "2.6.0"
+description = "Interact with GitLab API"
+category = "dev"
+optional = false
+python-versions = ">=3.6.0"
+
+[package.dependencies]
+requests = ">=2.22.0"
+requests-toolbelt = ">=0.9.1"
+
+[package.extras]
+autocompletion = ["argcomplete (>=1.10.0,<2)"]
+yaml = ["PyYaml (>=5.2)"]
+
+[[package]]
+name = "python-semantic-release"
+version = "7.15.3"
+description = "Automatic Semantic Versioning for Python projects"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+click = ">=7,<8"
+click-log = ">=0.3,<1"
+dotty-dict = ">=1.3.0,<2"
+gitpython = ">=3.0.8,<4"
+invoke = ">=1.4.1,<2"
+python-gitlab = ">=1.10,<3"
+requests = ">=2.25,<3"
+semver = ">=2.10,<3"
+tomlkit = ">=0.7.0,<1.0.0"
+twine = ">=3,<4"
+
+[package.extras]
+dev = ["mypy", "tox", "isort", "black"]
+docs = ["Sphinx (==1.3.6)"]
+test = ["coverage (>=5,<6)", "pytest (>=5,<6)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.5.0)", "mock (==1.3.0)"]
+
+[[package]]
+name = "pywin32-ctypes"
+version = "0.2.0"
+description = ""
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "readme-renderer"
+version = "29.0"
+description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+bleach = ">=2.1.0"
+docutils = ">=0.13.1"
+Pygments = ">=2.5.1"
+six = "*"
+
+[package.extras]
+md = ["cmarkgfm (>=0.5.0,<0.6.0)"]
+
[[package]]
name = "requests"
version = "2.25.1"
@@ -387,6 +612,59 @@ urllib3 = ">=1.21.1,<1.27"
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+[[package]]
+name = "requests-toolbelt"
+version = "0.9.1"
+description = "A utility belt for advanced users of python-requests"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+requests = ">=2.0.1,<3.0.0"
+
+[[package]]
+name = "rfc3986"
+version = "1.4.0"
+description = "Validating URI References per RFC 3986"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.extras]
+idna2008 = ["idna"]
+
+[[package]]
+name = "secretstorage"
+version = "3.3.1"
+description = "Python bindings to FreeDesktop.org Secret Service API"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+cryptography = ">=2.0"
+jeepney = ">=0.6"
+
+[[package]]
+name = "semver"
+version = "2.13.0"
+description = "Python helper for Semantic Versioning (http://semver.org/)"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "setuptools-scm"
+version = "6.0.1"
+description = "the blessed package to manage your versions by scm tags"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+toml = ["toml"]
+
[[package]]
name = "six"
version = "1.15.0"
@@ -395,6 +673,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+[[package]]
+name = "smmap"
+version = "4.0.0"
+description = "A pure Python implementation of a sliding window memory map manager"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
[[package]]
name = "tap.py"
version = "3.0"
@@ -427,6 +713,27 @@ category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+[[package]]
+name = "tomlkit"
+version = "0.7.0"
+description = "Style preserving TOML library"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "tqdm"
+version = "4.59.0"
+description = "Fast, Extensible Progress Meter"
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+
+[package.extras]
+dev = ["py-make (>=0.1.0)", "twine", "wheel"]
+notebook = ["ipywidgets (>=6)"]
+telegram = ["requests"]
+
[[package]]
name = "traitlets"
version = "4.3.3"
@@ -443,6 +750,25 @@ six = "*"
[package.extras]
test = ["pytest", "mock"]
+[[package]]
+name = "twine"
+version = "3.3.0"
+description = "Collection of utilities for publishing packages on PyPI"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = ">=0.4.3"
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+keyring = ">=15.1"
+pkginfo = ">=1.4.2"
+readme-renderer = ">=21.0"
+requests = ">=2.20"
+requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0"
+rfc3986 = ">=1.4.0"
+tqdm = ">=4.14"
+
[[package]]
name = "typing-extensions"
version = "3.7.4.3"
@@ -472,6 +798,14 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "webencodings"
+version = "0.5.1"
+description = "Character encoding aliases for legacy web content"
+category = "dev"
+optional = false
+python-versions = "*"
+
[[package]]
name = "yapf"
version = "0.30.0"
@@ -495,7 +829,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
-content-hash = "f860b4efd8c9effba27c1d04219d50794d74208204b7e3efca8ada9686fafea9"
+content-hash = "e32b90a7d407970b1a0211de99d4adda266a46f7e222c8d3bb4dd57dbbec17ca"
[metadata.files]
appnope = [
@@ -514,14 +848,65 @@ backcall = [
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
+bleach = [
+ {file = "bleach-3.3.0-py2.py3-none-any.whl", hash = "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125"},
+ {file = "bleach-3.3.0.tar.gz", hash = "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"},
+]
certifi = [
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
]
+cffi = [
+ {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"},
+ {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"},
+ {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"},
+ {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"},
+ {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"},
+ {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"},
+ {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"},
+ {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"},
+ {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"},
+ {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"},
+ {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"},
+ {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"},
+ {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"},
+ {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"},
+ {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"},
+ {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"},
+ {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"},
+ {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"},
+ {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"},
+ {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"},
+ {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"},
+ {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"},
+ {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"},
+ {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"},
+ {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"},
+ {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"},
+ {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"},
+ {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"},
+ {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"},
+ {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"},
+ {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"},
+ {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"},
+ {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"},
+ {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"},
+ {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"},
+ {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"},
+ {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"},
+]
chardet = [
{file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
{file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
]
+click = [
+ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
+ {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
+]
+click-log = [
+ {file = "click-log-0.3.2.tar.gz", hash = "sha256:16fd1ca3fc6b16c98cea63acf1ab474ea8e676849dc669d86afafb0ed7003124"},
+ {file = "click_log-0.3.2-py2.py3-none-any.whl", hash = "sha256:eee14dc37cdf3072158570f00406572f9e03e414accdccfccd4c538df9ae322c"},
+]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
@@ -577,14 +962,43 @@ coverage = [
{file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"},
{file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"},
]
+cryptography = [
+ {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"},
+ {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"},
+ {file = "cryptography-3.4.7-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2"},
+ {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6"},
+ {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959"},
+ {file = "cryptography-3.4.7-cp36-abi3-win32.whl", hash = "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d"},
+ {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"},
+ {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"},
+ {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"},
+ {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"},
+ {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"},
+ {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"},
+]
decorator = [
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
{file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
]
+docutils = [
+ {file = "docutils-0.17-py2.py3-none-any.whl", hash = "sha256:a71042bb7207c03d5647f280427f14bfbd1a65c9eb84f4b341d85fafb6bb4bdf"},
+ {file = "docutils-0.17.tar.gz", hash = "sha256:e2ffeea817964356ba4470efba7c2f42b6b0de0b04e66378507e3e2504bbff4c"},
+]
+dotty-dict = [
+ {file = "dotty_dict-1.3.0.tar.gz", hash = "sha256:eb0035a3629ecd84397a68f1f42f1e94abd1c34577a19cd3eacad331ee7cbaf0"},
+]
flake8 = [
{file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"},
{file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"},
]
+gitdb = [
+ {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"},
+ {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"},
+]
+gitpython = [
+ {file = "GitPython-3.1.14-py3-none-any.whl", hash = "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b"},
+ {file = "GitPython-3.1.14.tar.gz", hash = "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"},
+]
idna = [
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
@@ -597,6 +1011,11 @@ iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
+invoke = [
+ {file = "invoke-1.5.0-py2-none-any.whl", hash = "sha256:da7c2d0be71be83ffd6337e078ef9643f41240024d6b2659e7b46e0b251e339f"},
+ {file = "invoke-1.5.0-py3-none-any.whl", hash = "sha256:7e44d98a7dc00c91c79bac9e3007276965d2c96884b3c22077a9f04042bd6d90"},
+ {file = "invoke-1.5.0.tar.gz", hash = "sha256:f0c560075b5fb29ba14dad44a7185514e94970d1b9d57dcd3723bec5fed92650"},
+]
ipdb = [
{file = "ipdb-0.13.4.tar.gz", hash = "sha256:c85398b5fb82f82399fc38c44fe3532c0dde1754abee727d8f5cfcc74547b334"},
]
@@ -612,6 +1031,14 @@ jedi = [
{file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"},
{file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"},
]
+jeepney = [
+ {file = "jeepney-0.6.0-py3-none-any.whl", hash = "sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae"},
+ {file = "jeepney-0.6.0.tar.gz", hash = "sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657"},
+]
+keyring = [
+ {file = "keyring-22.3.0-py3-none-any.whl", hash = "sha256:2bc8363ebdd63886126a012057a85c8cb6e143877afa02619ac7dbc9f38a207b"},
+ {file = "keyring-22.3.0.tar.gz", hash = "sha256:16927a444b2c73f983520a48dec79ddab49fe76429ea05b8d528d778c8339522"},
+]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
@@ -636,6 +1063,10 @@ pickleshare = [
{file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
]
+pkginfo = [
+ {file = "pkginfo-1.7.0-py2.py3-none-any.whl", hash = "sha256:9fdbea6495622e022cc72c2e5e1b735218e4ffb2a2a69cde2694a6c1f16afb75"},
+ {file = "pkginfo-1.7.0.tar.gz", hash = "sha256:029a70cb45c6171c329dfc890cde0879f8c52d6f3922794796e06f577bb03db4"},
+]
pluggy = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
@@ -686,6 +1117,10 @@ pycodestyle = [
{file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"},
{file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"},
]
+pycparser = [
+ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
+ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
+]
pyflakes = [
{file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
{file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
@@ -706,14 +1141,54 @@ pytest-cov = [
{file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
{file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
]
+python-gitlab = [
+ {file = "python-gitlab-2.6.0.tar.gz", hash = "sha256:a862c6874524ab585b725a17b2cd2950fc09d6d74205f40a11be2a4e8f2dcaa1"},
+ {file = "python_gitlab-2.6.0-py3-none-any.whl", hash = "sha256:8f6a62b2804021416f13e91f359085f1071fb7adedfb292ef0d0877df777a075"},
+]
+python-semantic-release = [
+ {file = "python-semantic-release-7.15.3.tar.gz", hash = "sha256:0ffcb267923731b3578d4377de67c59c5779c80e514ef9883c47c8877c059d74"},
+ {file = "python_semantic_release-7.15.3-py3-none-any.whl", hash = "sha256:9b5c6e7ff9887b0cc8178004aa101aae2bfec8a464316cf287be8acbdf1e4954"},
+]
+pywin32-ctypes = [
+ {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
+ {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
+]
+readme-renderer = [
+ {file = "readme_renderer-29.0-py2.py3-none-any.whl", hash = "sha256:63b4075c6698fcfa78e584930f07f39e05d46f3ec97f65006e430b595ca6348c"},
+ {file = "readme_renderer-29.0.tar.gz", hash = "sha256:92fd5ac2bf8677f310f3303aa4bce5b9d5f9f2094ab98c29f13791d7b805a3db"},
+]
requests = [
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
]
+requests-toolbelt = [
+ {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"},
+ {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"},
+]
+rfc3986 = [
+ {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
+ {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"},
+]
+secretstorage = [
+ {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"},
+ {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"},
+]
+semver = [
+ {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"},
+ {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"},
+]
+setuptools-scm = [
+ {file = "setuptools_scm-6.0.1-py3-none-any.whl", hash = "sha256:c3bd5f701c8def44a5c0bfe8d407bef3f80342217ef3492b951f3777bd2d915c"},
+ {file = "setuptools_scm-6.0.1.tar.gz", hash = "sha256:d1925a69cb07e9b29416a275b9fadb009a23c148ace905b2fb220649a6c18e92"},
+]
six = [
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
]
+smmap = [
+ {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"},
+ {file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"},
+]
"tap.py" = [
{file = "tap.py-3.0-py2.py3-none-any.whl", hash = "sha256:a598bfaa2e224d71f2e86147c2ef822c18ff2e1b8ef006397e5056b08f92f699"},
{file = "tap.py-3.0.tar.gz", hash = "sha256:f5eeeeebfd64e53d32661752bb4c288589a3babbb96db3f391a4ec29f1359c70"},
@@ -726,10 +1201,22 @@ 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.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"},
+ {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"},
+]
+tqdm = [
+ {file = "tqdm-4.59.0-py2.py3-none-any.whl", hash = "sha256:9fdf349068d047d4cfbe24862c425883af1db29bcddf4b0eeb2524f6fbdb23c7"},
+ {file = "tqdm-4.59.0.tar.gz", hash = "sha256:d666ae29164da3e517fcf125e41d4fe96e5bb375cd87ff9763f6b38b5592fe33"},
+]
traitlets = [
{file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
{file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
]
+twine = [
+ {file = "twine-3.3.0-py3-none-any.whl", hash = "sha256:2f6942ec2a17417e19d2dd372fc4faa424c87ee9ce49b4e20c427eb00a0f3f41"},
+ {file = "twine-3.3.0.tar.gz", hash = "sha256:fcffa8fc37e8083a5be0728371f299598870ee1eccc94e9a25cef7b1dcfa8297"},
+]
typing-extensions = [
{file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
{file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
@@ -743,6 +1230,10 @@ wcwidth = [
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
]
+webencodings = [
+ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
+ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
+]
yapf = [
{file = "yapf-0.30.0-py2.py3-none-any.whl", hash = "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"},
{file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"},
diff --git a/pyproject.toml b/pyproject.toml
index 10619cf..ce2a404 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,6 +5,13 @@ description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
+[tool.semantic_release]
+version_toml = "pyproject.toml:tool.poetry.version"
+version_pattern = "logdna/VERSION:(\\d+\\.\\d+\\.\\d+)"
+branch = "master"
+commit_subject = "release: Version {version} [skip ci]"
+commit_author = "LogDNA Bot "
+
[tool.poetry.dependencies]
python = "^3.6"
requests = "^2.25.1"
@@ -18,6 +25,7 @@ yapf = "^0.30.0"
pytest = "^6.2.2"
pytest-cov = "^2.11.1"
taskipy = "^1.6.0"
+python-semantic-release = "^7.15.3"
[tool.taskipy.tasks]
pre_test = "mkdir coverage -p"
@@ -26,6 +34,7 @@ post_test = "python scripts/json_coverage.py"
lint = "flake8 --doctests"
"lint:fix" = "yapf -r -i logdna scripts tests"
"post_lint:fix" = "task lint"
+release = "semantic-release publish"
[build-system]
requires = ["poetry>=0.12"]
diff --git a/setup.cfg b/setup.cfg
index bf13f84..c24cd82 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,7 +2,7 @@
description-file = README.md
[flake8]
-exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
+exclude = .git,__pycache__,docs/source/conf.py,old,build,dist,.cache,pip,pypoetry
max-complexity = 10
[yapf]
diff --git a/setup.py b/setup.py
index 34386cf..fc16e48 100644
--- a/setup.py
+++ b/setup.py
@@ -7,9 +7,8 @@
with open(path.join(this_directory, 'README.md'), 'rb') as f:
long_description = f.read().decode('utf-8')
-with open("{p}{s}logdna{s}VERSION"
- .format(p=this_directory, s=sep)
- ) as f:
+kwargs = {"dir": this_directory, "sep": sep}
+with open("{dir}{sep}logdna{sep}VERSION".format(**kwargs)) as f:
version = f.read().strip('\n')
setup(
From fce4d5995b31c426f2b66992b57f129f22f9f18f Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Wed, 7 Apr 2021 12:06:58 -0500
Subject: [PATCH 06/55] fix(ci): correct invalid environment variable
The correct variable name for the github auth token is GH_TOKEN, not
GITHUB_TOKEN.
---
Jenkinsfile | 2 +-
Makefile | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Jenkinsfile b/Jenkinsfile
index e97e2ec..4217468 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -52,7 +52,7 @@ pipeline {
}
environment {
- GITHUB_TOKEN = credentials('github-api-token')
+ GH_TOKEN = credentials('github-api-token')
PYPI_TOKEN = credentials('pypi-token')
}
diff --git a/Makefile b/Makefile
index 0b689d1..e27b93e 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ DOCKER_COMMAND := $(DOCKER_RUN) -v $(PWD):$(WORKDIR):Z -w $(WORKDIR) \
-e POETRY_CACHE_DIR=$(WORKDIR)/.cache \
-e POETRY_VIRTUALENV_IN_PROJECT=true \
-e PYPI_TOKEN \
- -e GITHUB_TOKEN \
+ -e GH_TOKEN \
-e GIT_AUTHOR_NAME \
-e GIT_AUTHOR_EMAIL \
-e GIT_COMMITTER_NAME \
From 9ce8353ceb3a3e61819c01caa38294f31e6edd40 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Wed, 7 Apr 2021 17:55:53 +0000
Subject: [PATCH 07/55] release: Version 1.6.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 39 insertions(+), 2 deletions(-)
create mode 100644 CHANGELOG.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..605db4c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,37 @@
+# Changelog
+
+
+
+## v1.6.0 (2021-04-07)
+### Feature
+* **ci:** Enable releases through semantic release ([`9e4b1c0`](https://github.com/logdna/python/commit/9e4b1c0a43bc0941ba4fb336ea12a3f497622ee6))
+* **ci:** Include jenkins setup ([`1c5be37`](https://github.com/logdna/python/commit/1c5be37ef32776f2d0ba2b68b67cebb58b1f0177))
+* **tasks:** Convert run scripts to tasks ([`f5a8b18`](https://github.com/logdna/python/commit/f5a8b182941a0c514594cbaa7a3ac5952173d5a8))
+* **lint:** Setup linting + test harness ([`0a7763a`](https://github.com/logdna/python/commit/0a7763a2befbf598b59d7ab595b19e22b173fda5))
+* **tags:** Allow tags to be configured ([`6adc21e`](https://github.com/logdna/python/commit/6adc21e872fa521be1aaf08309f7f3d0ba3dc5c5))
+* **meta:** Python log info in meta object ([`cf9b505`](https://github.com/logdna/python/commit/cf9b505734df12918a665a8a8c74d4fd74e5bc47))
+* **handlers:** Make available via config file ([`39c5dec`](https://github.com/logdna/python/commit/39c5decd98e8d4feb6c1bbfa487faf35396c8b12))
+
+### Fix
+* **ci:** Correct invalid environment variable ([`fce4d59`](https://github.com/logdna/python/commit/fce4d5995b31c426f2b66992b57f129f22f9f18f))
+
+### Documentation
+* Add @matthiasfru as a contributor ([`3727bc3`](https://github.com/logdna/python/commit/3727bc3386d3dd6465e0b0d15675a091a3743c24))
+* Add @rudyryk as a contributor ([`9ab0e39`](https://github.com/logdna/python/commit/9ab0e3932180c41bff1cd0944c24fb8e208f4391))
+* Add @kurtiss as a contributor ([`1476085`](https://github.com/logdna/python/commit/14760857649b56207240bae907386a33f7f1666b))
+* Update @inkrement as a contributor ([`6a2cede`](https://github.com/logdna/python/commit/6a2cedef6695e18a981a3605176c9cffdd159827))
+* Add @inkrement as a contributor ([`7ba6992`](https://github.com/logdna/python/commit/7ba6992287f98c478da72f5982a0869b5d835df7))
+* Add @btashton as a contributor ([`c1e69bf`](https://github.com/logdna/python/commit/c1e69bfc965e1e1e5717ec7af3271dc0bf6b502d))
+* Add @SpainTrain as a contributor ([`7b9f04b`](https://github.com/logdna/python/commit/7b9f04b86a1e813bccea29ea8db1554808ea48b6))
+* Add @sataloger as a contributor ([`04081f0`](https://github.com/logdna/python/commit/04081f039d6136a66b259f7245ad7845ef1c080a))
+* Add @jdemaeyer as a contributor ([`69e8f7c`](https://github.com/logdna/python/commit/69e8f7cc0782a778d0da230ff1a8feb5f8cee352))
+* Add @dchai76 as a contributor ([`42e9c6b`](https://github.com/logdna/python/commit/42e9c6bddc2bce29714072bba37d85d9a734eac2))
+* Add @danmaas as a contributor ([`23bbab4`](https://github.com/logdna/python/commit/23bbab48cdef4aaef6813459dbdb23dbd9c60374))
+* Add @LYHuang as a contributor ([`3cc9e23`](https://github.com/logdna/python/commit/3cc9e232d0b12b9f0315efb1d618426b486a2604))
+* Add @baronomasia as a contributor ([`8971bd7`](https://github.com/logdna/python/commit/8971bd713d74cd9386c60a50cc75a7fc3591f544))
+* Add @utek as a contributor ([`ea495b4`](https://github.com/logdna/python/commit/ea495b479cc0549ff68a1e816c3f7a174c1c138c))
+* Add @esatterwhite as a contributor ([`0a334bd`](https://github.com/logdna/python/commit/0a334bdb5690049634c64eb0f9c6c1026b9ab001))
+* Add @mikehu as a contributor ([`e7aa7cb`](https://github.com/logdna/python/commit/e7aa7cb2624313c065f7bbc8a129c1a4841f9ec2))
+* Add @vilyapilya as a contributor ([`e3191f5`](https://github.com/logdna/python/commit/e3191f577fb7ddf7590ca1e1bf239f66d2f30fd0))
+* Add @smusali as a contributor ([`4d5022f`](https://github.com/logdna/python/commit/4d5022f93948cca239ebc104e34034c350956f65))
+* Add @respectus as a contributor ([`85a543c`](https://github.com/logdna/python/commit/85a543c9dc27a3c6064e790be0ba1c475187c38d))
diff --git a/logdna/VERSION b/logdna/VERSION
index 94fe62c..dc1e644 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.5.4
+1.6.0
diff --git a/pyproject.toml b/pyproject.toml
index ce2a404..c21c779 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.5.4"
+version = "1.6.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From d9b00c8c450b5f645fd95c851a3322945b2877ad Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Wed, 7 Apr 2021 17:57:17 +0000
Subject: [PATCH 08/55] release: Version 1.7.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 605db4c..182021a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.7.0 (2021-04-07)
+
+
## v1.6.0 (2021-04-07)
### Feature
* **ci:** Enable releases through semantic release ([`9e4b1c0`](https://github.com/logdna/python/commit/9e4b1c0a43bc0941ba4fb336ea12a3f497622ee6))
diff --git a/logdna/VERSION b/logdna/VERSION
index dc1e644..bd8bf88 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.6.0
+1.7.0
diff --git a/pyproject.toml b/pyproject.toml
index c21c779..426b313 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.6.0"
+version = "1.7.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 024aa8202a548e5bb6b2ac9b150f7e8da4e7f921 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:24:40 +0000
Subject: [PATCH 09/55] release: Version 1.8.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 182021a..b3113b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.8.0 (2021-04-15)
+
+
## v1.7.0 (2021-04-07)
diff --git a/logdna/VERSION b/logdna/VERSION
index bd8bf88..27f9cd3 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.7.0
+1.8.0
diff --git a/pyproject.toml b/pyproject.toml
index 426b313..d61effb 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.7.0"
+version = "1.8.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 40822de69beab1cb431a37dc29857ff24cca108f Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:26:28 +0000
Subject: [PATCH 10/55] release: Version 1.9.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b3113b0..8608e59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.9.0 (2021-04-15)
+
+
## v1.8.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index 27f9cd3..f8e233b 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.8.0
+1.9.0
diff --git a/pyproject.toml b/pyproject.toml
index d61effb..5488446 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.8.0"
+version = "1.9.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 11250e79ebf04e4f952dba5b46abc1fc4236a910 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:27:51 +0000
Subject: [PATCH 11/55] release: Version 1.10.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8608e59..2dc14e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.10.0 (2021-04-15)
+
+
## v1.9.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index f8e233b..81c871d 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.9.0
+1.10.0
diff --git a/pyproject.toml b/pyproject.toml
index 5488446..7b26413 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.9.0"
+version = "1.10.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 8690fc0751a4cd836d92ca1336ca2c68003f25be Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:29:17 +0000
Subject: [PATCH 12/55] release: Version 1.11.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2dc14e1..e4cb410 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.11.0 (2021-04-15)
+
+
## v1.10.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index 81c871d..1cac385 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.10.0
+1.11.0
diff --git a/pyproject.toml b/pyproject.toml
index 7b26413..b9555b9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.10.0"
+version = "1.11.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 4b8df3cbedc3771ec811b2894f04bbb3e15620e4 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:30:41 +0000
Subject: [PATCH 13/55] release: Version 1.12.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e4cb410..729a718 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.12.0 (2021-04-15)
+
+
## v1.11.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index 1cac385..0eed1a2 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.11.0
+1.12.0
diff --git a/pyproject.toml b/pyproject.toml
index b9555b9..3e6d680 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.11.0"
+version = "1.12.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 1d143f7b3b6e09156ce1571d06938af4da9c4051 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:32:05 +0000
Subject: [PATCH 14/55] release: Version 1.13.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 729a718..97f5ae6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.13.0 (2021-04-15)
+
+
## v1.12.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index 0eed1a2..feaae22 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.12.0
+1.13.0
diff --git a/pyproject.toml b/pyproject.toml
index 3e6d680..3e2d962 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.12.0"
+version = "1.13.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 70e5fb97fd1e3d2fdf96f31703a37c3c6f6cb817 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:33:30 +0000
Subject: [PATCH 15/55] release: Version 1.14.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97f5ae6..2433ab1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.14.0 (2021-04-15)
+
+
## v1.13.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index feaae22..850e742 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.13.0
+1.14.0
diff --git a/pyproject.toml b/pyproject.toml
index 3e2d962..7e4f16e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.13.0"
+version = "1.14.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 70e28ce20b4820456bb3fcc2e8cc1ebdd2ab19bd Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 15:34:54 +0000
Subject: [PATCH 16/55] release: Version 1.15.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2433ab1..126a5c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.15.0 (2021-04-15)
+
+
## v1.14.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index 850e742..141f2e8 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.14.0
+1.15.0
diff --git a/pyproject.toml b/pyproject.toml
index 7e4f16e..9b99f02 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.14.0"
+version = "1.15.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From e8c2dc4b5308aa57854071ddd3a0ba48ee3cabc8 Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Thu, 15 Apr 2021 09:25:47 -0500
Subject: [PATCH 17/55] ci: prevent double releases w/ skip ci
This make sure the release commit doesn't also trigger a release by
checking for `skip ci` in the commit message
---
Jenkinsfile | 46 +++++++++++++++++++++++++++++++++++++---------
Makefile | 3 +++
2 files changed, 40 insertions(+), 9 deletions(-)
diff --git a/Jenkinsfile b/Jenkinsfile
index 4217468..009701d 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -2,6 +2,7 @@ library 'magic-butler-catalogue'
def PROJECT_NAME = 'logdna-python'
def TRIGGER_PATTERN = ".*@logdnabot.*"
def DEFAULT_BRANCH = 'master'
+def CURRENT_BRANCH = [env.CHANGE_BRANCH, env.BRANCH_NAME]?.find{branch -> branch != null}
pipeline {
agent none
@@ -51,17 +52,44 @@ pipeline {
}
}
- environment {
- GH_TOKEN = credentials('github-api-token')
- PYPI_TOKEN = credentials('pypi-token')
- }
+ stages {
+ stage('dry run') {
+ when {
+ not {
+ branch "${DEFAULT_BRANCH}"
+ }
+ }
- steps {
- script {
- if ("${BRANCH_NAME}" == "${DEFAULT_BRANCH}") {
+ environment {
+ GH_TOKEN = credentials('github-api-token')
+ PYPI_TOKEN = credentials('pypi-token')
+ JENKINS_URL = "${JENKINS_URL}"
+ BRANCH_NAME = "${DEFAULT_BRANCH}"
+ GIT_BRANCH = "${DEFAULT_BRANCH}"
+ CHANGE_ID = ''
+ }
+
+ steps {
+ sh "make release-dry"
+ }
+ }
+
+ stage('publish') {
+
+ environment {
+ GH_TOKEN = credentials('github-api-token')
+ PYPI_TOKEN = credentials('pypi-token')
+ JENKINS_URL = "${JENKINS_URL}"
+ }
+
+ when {
+ branch "${DEFAULT_BRANCH}"
+ not {
+ changelog '\\[skip ci\\]'
+ }
+ }
+ steps {
sh 'make release'
- } else {
- sh "BRANCH_NAME=${DEFAULT_BRANCH} CHANGE_ID='' make release-dry"
}
}
}
diff --git a/Makefile b/Makefile
index e27b93e..9e90ed2 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,9 @@ DOCKER_COMMAND := $(DOCKER_RUN) -v $(PWD):$(WORKDIR):Z -w $(WORKDIR) \
-e POETRY_VIRTUALENV_IN_PROJECT=true \
-e PYPI_TOKEN \
-e GH_TOKEN \
+ -e JENKINS_URL \
+ -e BRANCH_NAME \
+ -e CHANGE_ID \
-e GIT_AUTHOR_NAME \
-e GIT_AUTHOR_EMAIL \
-e GIT_COMMITTER_NAME \
From d206ba4615975a05389c81754875dfa9548cd2b2 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Apr 2021 19:44:45 +0000
Subject: [PATCH 18/55] release: Version 1.16.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 3 +++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 126a5c2..3dd813e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
+## v1.16.0 (2021-04-15)
+
+
## v1.15.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index 141f2e8..15b989e 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.15.0
+1.16.0
diff --git a/pyproject.toml b/pyproject.toml
index 9b99f02..25160eb 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.15.0"
+version = "1.16.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 2bba17c15e97d93dcee771a2267736c80a5886fb Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Thu, 3 Jun 2021 18:02:49 +0400
Subject: [PATCH 19/55] ci: cleaning on automation
Semver: patch
---
Makefile | 2 +-
pyproject.toml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index 9e90ed2..3e4f1b4 100644
--- a/Makefile
+++ b/Makefile
@@ -40,7 +40,7 @@ help: ## Show this help, includes list of all actions.
.PHONY:clean
clean: ## purge build time artifacts
- rm -rf dist/ build/ coverage/ pypoetry/ pip/ .cache
+ rm -rf dist/ build/ coverage/ pypoetry/ pip/ **/__pycache__/ .pytest_cache/ .cache .coverage
.PHONY:changelog
changelog: ## print the next version of the change log to stdout
diff --git a/pyproject.toml b/pyproject.toml
index 25160eb..5a1ce48 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -28,7 +28,7 @@ taskipy = "^1.6.0"
python-semantic-release = "^7.15.3"
[tool.taskipy.tasks]
-pre_test = "mkdir coverage -p"
+pre_test = "mkdir -p coverage"
test = "pytest --junitxml=coverage/test.xml --cov=logdna --cov-report=html"
post_test = "python scripts/json_coverage.py"
lint = "flake8 --doctests"
From 02169d6b336ffd4b0b55a52114ad152e9acbbc2c Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Mon, 7 Jun 2021 14:45:02 +0400
Subject: [PATCH 20/55] refactor: reduce code complexity
Semver: patch
---
logdna/configs.py | 9 ++++----
logdna/logdna.py | 52 ++++++++++++++++++--------------------------
tests/test_logger.py | 4 ++--
3 files changed, 27 insertions(+), 38 deletions(-)
diff --git a/logdna/configs.py b/logdna/configs.py
index a35d32d..1c99347 100644
--- a/logdna/configs.py
+++ b/logdna/configs.py
@@ -6,11 +6,10 @@
defaults = {
'DEFAULT_REQUEST_TIMEOUT': 30,
- 'MAX_LINE_LENGTH': 32000,
- 'FLUSH_INTERVAL_SECS': 5,
- 'FLUSH_BYTE_LIMIT': 2 * 1024 * 1024,
+ 'FLUSH_INTERVAL': 250,
+ 'FLUSH_LIMIT': 5 * 1024 * 1024,
'LOGDNA_URL': 'https://logs.logdna.com/logs/ingest',
- 'BUF_RETENTION_BYTE_LIMIT': 4 * 1024 * 1024,
- 'RETRY_INTERVAL_SECS': 8,
+ 'BUF_RETENTION_LIMIT': 4 * 1024 * 1024,
+ 'RETRY_INTERVAL': 5000,
'USER_AGENT': 'python/%s' % version
}
diff --git a/logdna/logdna.py b/logdna/logdna.py
index eeb6000..4f50c91 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -5,7 +5,6 @@
import sys
import threading
import time
-from functools import reduce
from .configs import defaults
from .utils import sanitize_meta, get_ip
@@ -22,10 +21,10 @@ def __init__(self, key, options={}):
self.key = key
self.buf = []
+ self.buf_size = 0
self.secondary = []
self.exception_flag = False
self.flusher = None
- self.max_length = defaults['MAX_LINE_LENGTH']
self.hostname = options.get('hostname', socket.gethostname())
self.ip = options.get('ip', get_ip())
@@ -40,15 +39,14 @@ def __init__(self, key, options={}):
self.include_standard_meta = options.get('include_standard_meta',
False)
self.index_meta = options.get('index_meta', False)
- self.flush_limit = options.get('flush_limit',
- defaults['FLUSH_BYTE_LIMIT'])
- self.flush_interval_secs = options.get('flush_interval',
- defaults['FLUSH_INTERVAL_SECS'])
- self.retry_interval_secs = options.get('retry_interval_secs',
- defaults['RETRY_INTERVAL_SECS'])
+ self.flush_limit = options.get('flush_limit', defaults['FLUSH_LIMIT'])
+ self.flush_interval = options.get('flush_interval',
+ defaults['FLUSH_INTERVAL'])
+ self.retry_interval = options.get('retry_interval',
+ defaults['RETRY_INTERVAL'])
self.tags = options.get('tags', [])
- self.buf_retention_byte_limit = options.get(
- 'buf_retention_limit', defaults['BUF_RETENTION_BYTE_LIMIT'])
+ self.buf_retention_limit = options.get('buf_retention_limit',
+ defaults['BUF_RETENTION_LIMIT'])
self.user_agent = options.get('user_agent', defaults['USER_AGENT'])
if isinstance(self.tags, str):
@@ -58,43 +56,36 @@ def __init__(self, key, options={}):
self.setLevel(logging.DEBUG)
self.lock = threading.RLock()
- # TODO(esatterwhite): complexity too high (8)
- def buffer_log(self, message):
- if message and message['line']:
- if len(message['line']) > self.max_length:
- message['line'] = message[
- 'line'][:self.max_length] + ' (cut off, too long...)'
- if self.verbose in ['true', 'debug', 'd']:
- self.internalLogger.debug(
- 'Line was longer than %s chars and was truncated.',
- self.max_length)
+ def start_flusher(self):
+ interval = (self.retry_interval
+ if self.exception_flag else self.flush_interval)
+ self.flusher = threading.Timer(float(interval / 1000), self.flush)
+ self.flusher.start()
+ def buffer_log(self, message):
# Attempt to acquire lock to write to buf
# otherwise write to secondary as flush occurs
if self.lock.acquire(blocking=False):
- buf_size = reduce(lambda x, y: x + len(y['line']), self.buf, 0)
-
- if buf_size + len(message['line']) < self.buf_retention_byte_limit:
+ if self.buf_size + len(message['line']) < self.buf_retention_limit:
self.buf.append(message)
+ self.buf_size += len(message['line'])
else:
self.internalLogger.debug(
'The buffer size exceeded the limit: %s',
- self.buf_retention_byte_limit)
+ self.buf_retention_limit)
self.lock.release()
- if buf_size >= self.flush_limit and not self.exception_flag:
+ if self.buf_size >= self.flush_limit and not self.exception_flag:
self.flush()
else:
self.secondary.append(message)
if not self.flusher:
- interval = (self.retry_interval_secs
- if self.exception_flag else self.flush_interval_secs)
- self.flusher = threading.Timer(interval, self.flush)
- self.flusher.start()
+ self.start_flusher()
def clean_after_success(self):
del self.buf[:]
+ self.buf_size = 0
self.exception_flag = False
if self.flusher:
self.flusher.cancel()
@@ -140,8 +131,7 @@ def flush(self):
self.lock.release()
else:
if not self.flusher:
- self.flusher = threading.Timer(1, self.flush)
- self.flusher.start()
+ self.start_flusher()
# TODO(esatterwhite): complexity too high (14)
def emit(self, record): # noqa: C901
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 3c5fa8d..1ed992e 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -84,8 +84,8 @@ def stops_retention_when_buf_is_full(self):
'mac': 'C0:FF:EE:C0:FF:EE',
'buf_retention_limit': 50,
'equest_timeout': 10,
- 'flush_interval': 1,
- 'retry_interval_secs': 1
+ 'flush_interval': 1000,
+ 'retry_interval': 1000
}
handler = LogDNAHandler(LOGDNA_API_KEY, options)
From 9bfe479132acb0aa8e5784f2aa31298606e49789 Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Mon, 7 Jun 2021 21:54:12 +0400
Subject: [PATCH 21/55] feat(threadWorkerPools): introduce extra threads
In order to provide non-blocking I/O, introducing ThreadWorkerPools for multiple parts and imitating the non-blocking I/O implementation from [our Ruby library](github.com/logdna/ruby).
Semver: minor
---
logdna/configs.py | 5 +-
logdna/logdna.py | 270 ++++++++++++++++++++++++++++---------------
tests/test_logger.py | 10 +-
3 files changed, 185 insertions(+), 100 deletions(-)
diff --git a/logdna/configs.py b/logdna/configs.py
index 1c99347..9dac4a8 100644
--- a/logdna/configs.py
+++ b/logdna/configs.py
@@ -7,7 +7,10 @@
defaults = {
'DEFAULT_REQUEST_TIMEOUT': 30,
'FLUSH_INTERVAL': 250,
- 'FLUSH_LIMIT': 5 * 1024 * 1024,
+ 'FLUSH_LIMIT': 2 * 1024 * 1024,
+ 'MAX_CONCURRENT_REQUESTS': 10,
+ 'MAX_RETRY_ATTEMPTS': 3,
+ 'MAX_RETRY_JITTER': 0.5,
'LOGDNA_URL': 'https://logs.logdna.com/logs/ingest',
'BUF_RETENTION_LIMIT': 4 * 1024 * 1024,
'RETRY_INTERVAL': 5000,
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 4f50c91..5f2e299 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -6,69 +6,104 @@
import threading
import time
+from concurrent.futures import ThreadPoolExecutor
+
from .configs import defaults
from .utils import sanitize_meta, get_ip
class LogDNAHandler(logging.Handler):
def __init__(self, key, options={}):
+ # Setup Handler
logging.Handler.__init__(self)
+
+ # Set Internal Logger
self.internal_handler = logging.StreamHandler(sys.stdout)
self.internal_handler.setLevel(logging.DEBUG)
self.internalLogger = logging.getLogger('internal')
- self.internalLogger.setLevel(logging.DEBUG)
self.internalLogger.addHandler(self.internal_handler)
+ self.internalLogger.setLevel(logging.DEBUG)
+ # Set the Custom Variables
self.key = key
- self.buf = []
- self.buf_size = 0
- self.secondary = []
- self.exception_flag = False
- self.flusher = None
-
self.hostname = options.get('hostname', socket.gethostname())
self.ip = options.get('ip', get_ip())
self.mac = options.get('mac', None)
self.level = options.get('level', 'info')
- self.verbose = str(options.get('verbose', 'true')).lower()
self.app = options.get('app', '')
self.env = options.get('env', '')
+ self.tags = options.get('tags', [])
+ if isinstance(self.tags, str):
+ self.tags = [tag.strip() for tag in self.tags.split(',')]
+ elif not isinstance(self.tags, list):
+ self.tags = []
+
+ # Set the Connection Variables
self.url = options.get('url', defaults['LOGDNA_URL'])
self.request_timeout = options.get('request_timeout',
defaults['DEFAULT_REQUEST_TIMEOUT'])
+ self.user_agent = options.get('user_agent', defaults['USER_AGENT'])
+ self.max_retry_attempts = options.get('max_retry_attempts',
+ defaults['MAX_RETRY_ATTEMPTS'])
+ self.max_retry_jitter = options.get('max_retry_jitter',
+ defaults['MAX_RETRY_JITTER'])
+ self.max_concurrent_requests = options.get(
+ 'max_concurrent_requests', defaults['MAX_CONCURRENT_REQUESTS'])
+ self.retry_interval = options.get('retry_interval',
+ defaults['RETRY_INTERVAL'])
+
+ # Set the Flush-related Variables
+ self.buf = []
+ self.buf_size = 0
+ self.secondary = []
+ self.exception_flag = False
+ self.flusher = None
self.include_standard_meta = options.get('include_standard_meta',
False)
self.index_meta = options.get('index_meta', False)
self.flush_limit = options.get('flush_limit', defaults['FLUSH_LIMIT'])
self.flush_interval = options.get('flush_interval',
defaults['FLUSH_INTERVAL'])
- self.retry_interval = options.get('retry_interval',
- defaults['RETRY_INTERVAL'])
- self.tags = options.get('tags', [])
self.buf_retention_limit = options.get('buf_retention_limit',
defaults['BUF_RETENTION_LIMIT'])
- self.user_agent = options.get('user_agent', defaults['USER_AGENT'])
- if isinstance(self.tags, str):
- self.tags = [tag.strip() for tag in self.tags.split(',')]
- elif not isinstance(self.tags, list):
- self.tags = []
+ # Set up the Thread Pools
+ self.worker_thread_pool = ThreadPoolExecutor()
+ self.request_thread_pool = ThreadPoolExecutor(
+ max_workers=self.max_concurrent_requests)
+
self.setLevel(logging.DEBUG)
self.lock = threading.RLock()
def start_flusher(self):
- interval = (self.retry_interval
- if self.exception_flag else self.flush_interval)
- self.flusher = threading.Timer(float(interval / 1000), self.flush)
- self.flusher.start()
+ if not self.flusher:
+ interval = (self.retry_interval
+ if self.exception_flag else self.flush_interval)
+ self.flusher = threading.Timer(float(interval / 1000), self.flush)
+ self.flusher.start()
+
+ def close_flusher(self):
+ if self.flusher:
+ self.flusher.cancel()
+ self.flusher = None
def buffer_log(self, message):
+ if self.worker_thread_pool:
+ try:
+ self.worker_thread_pool.submit(self.buffer_log_sync, message)
+ except RuntimeError:
+ self.buffer_log_sync(message)
+ except Exception as e:
+ self.internalLogger.debug('Error in calling buffer_log: %s', e)
+
+ def buffer_log_sync(self, message):
# Attempt to acquire lock to write to buf
# otherwise write to secondary as flush occurs
if self.lock.acquire(blocking=False):
- if self.buf_size + len(message['line']) < self.buf_retention_limit:
+ msglen = len(message['line'])
+ if self.buf_size + msglen < self.buf_retention_limit:
self.buf.append(message)
- self.buf_size += len(message['line'])
+ self.buf_size += msglen
else:
self.internalLogger.debug(
'The buffer size exceeded the limit: %s',
@@ -76,78 +111,115 @@ def buffer_log(self, message):
self.lock.release()
if self.buf_size >= self.flush_limit and not self.exception_flag:
+ self.close_flusher()
self.flush()
+ else:
+ self.start_flusher()
else:
self.secondary.append(message)
- if not self.flusher:
- self.start_flusher()
-
def clean_after_success(self):
del self.buf[:]
self.buf_size = 0
self.exception_flag = False
- if self.flusher:
- self.flusher.cancel()
- self.flusher = None
+ self.close_flusher()
- def handle_exception(self, exception):
- if self.flusher:
- self.flusher.cancel()
- self.flusher = None
- self.exception_flag = True
- if self.verbose in ['true', 'error', 'err', 'e']:
- self.internalLogger.debug('Error sending logs %s', exception)
+ def flush(self):
+ if self.worker_thread_pool:
+ try:
+ self.worker_thread_pool.submit(self.flush_sync)
+ except RuntimeError:
+ self.flush_sync()
+ except Exception as e:
+ self.internalLogger.debug('Error in calling flush: %s', e)
+
+ def flush_sync(self):
+ if self.buf_size == 0:
+ return
+ if self.lock.acquire(blocking=False):
+ if self.request_thread_pool:
+ try:
+ self.request_thread_pool.submit(self.try_request)
+ except RuntimeError:
+ self.try_request()
+ except Exception as e:
+ self.internalLogger.debug(
+ 'Error in calling try_request: %s', e)
+ self.lock.release()
+ else:
+ self.start_flusher()
- # do not call without acquiring the lock
- def send_request(self):
+ def try_request(self):
self.buf.extend(self.secondary)
self.secondary = []
data = {'e': 'ls', 'ls': self.buf}
+ retries = 0
+ while True:
+ retries += 1
+ if retries > self.max_retry_attempts:
+ self.internalLogger.debug(
+ 'Flush exceeded %s tries. Discarding flush buffer',
+ self.max_retry_attempts)
+ self.close_flusher()
+ self.exception_flag = True
+ break
+
+ if self.send_request(data):
+ self.clean_after_success()
+ break
+
+ sleep_time = self.retry_interval * (1 << (retries - 1))
+ sleep_time += self.max_retry_jitter
+ time.sleep(sleep_time)
+
+ def send_request(self, data):
try:
- res = requests.post(url=self.url,
- json=data,
- auth=('user', self.key),
- params={
- 'hostname': self.hostname,
- 'ip': self.ip,
- 'mac': self.mac if self.mac else None,
- 'tags': self.tags if self.tags else None
- },
- stream=True,
- timeout=self.request_timeout,
- headers={'user-agent': self.user_agent})
- res.raise_for_status()
- # when no RequestException happened
- self.clean_after_success()
- except requests.exceptions.RequestException as e:
- self.handle_exception(e)
+ response = requests.post(url=self.url,
+ json=data,
+ auth=('user', self.key),
+ params={
+ 'hostname': self.hostname,
+ 'ip': self.ip,
+ 'mac': self.mac,
+ 'tags': self.tags
+ },
+ stream=True,
+ timeout=self.request_timeout,
+ headers={'user-agent': self.user_agent})
- def flush(self):
- if len(self.buf) == 0:
- return
- if self.lock.acquire(blocking=False):
- self.send_request()
- self.lock.release()
- else:
- if not self.flusher:
- self.start_flusher()
+ response.raise_for_status()
+ status_code = response.status_code
+ if status_code in [401, 403]:
+ self.internalLogger.debug(
+ 'Please provide a valid ingestion key.' +
+ ' Discarding flush buffer')
+ return True
+
+ if status_code == 200:
+ return True
- # TODO(esatterwhite): complexity too high (14)
- def emit(self, record): # noqa: C901
+ if status_code in [400, 500, 504]:
+ self.internalLogger.debug('The request failed %s. Retrying...',
+ response.reason)
+ return True
+ else:
+ self.internalLogger.debug(
+ 'The request failed: %s. Retrying...', response.reason)
+
+ except requests.exceptions.Timeout as timeout:
+ self.internalLogger.debug('Timeout error occurred %s. Retrying...',
+ timeout)
+
+ except requests.exceptions.RequestException as exception:
+ self.internalLogger.debug(
+ 'Error sending logs %s. Discarding flush buffer', exception)
+ return True
+
+ return False
+
+ def emit(self, record):
msg = self.format(record)
record = record.__dict__
- opts = {}
- if 'args' in record:
- opts = record['args']
- if self.include_standard_meta:
- if isinstance(opts, tuple):
- opts = {}
- if 'meta' not in opts:
- opts['meta'] = {}
- for key in ['name', 'pathname', 'lineno']:
- opts['meta'][key] = record[key]
-
message = {
'hostname': self.hostname,
'timestamp': int(time.time() * 1000),
@@ -156,25 +228,35 @@ def emit(self, record): # noqa: C901
'app': self.app or record['module'],
'env': self.env
}
- if not isinstance(opts, tuple):
- if 'level' in opts:
- message['level'] = opts['level']
- if 'app' in opts:
- message['app'] = opts['app']
- if 'hostname' in opts:
- message['hostname'] = opts['hostname']
- if 'env' in opts:
- message['env'] = opts['env']
- if 'timestamp' in opts:
- message['timestamp'] = opts['timestamp']
- if 'meta' in opts:
- if self.index_meta:
- message['meta'] = sanitize_meta(opts['meta'])
- else:
- message['meta'] = json.dumps(opts['meta'])
+
+ opts = {}
+ if 'args' in record and not isinstance(record['args'], tuple):
+ opts = record['args']
+
+ for key in message.keys():
+ if key in opts:
+ message[key] = opts[key]
+
+ if self.include_standard_meta:
+ if 'meta' not in opts:
+ opts['meta'] = {}
+ for key in ['name', 'pathname', 'lineno']:
+ if key in record:
+ opts['meta'][key] = record[key]
+ if self.index_meta:
+ message['meta'] = sanitize_meta(opts['meta'])
+ else:
+ message['meta'] = json.dumps(opts['meta'])
+
self.buffer_log(message)
def close(self):
- if len(self.buf) > 0:
- self.flush()
+ self.close_flusher()
+ self.flush_sync()
+ if self.worker_thread_pool:
+ self.worker_thread_pool.shutdown(wait=True)
+ self.worker_thread_pool = None
+ if self.request_thread_pool:
+ self.request_thread_pool.shutdown(wait=True)
+ self.request_thread_pool = None
logging.Handler.close(self)
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 1ed992e..8bc7d6b 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -49,8 +49,8 @@ def server_recieves_messages(self):
server_thread.join()
logdna_thread.join()
- self.assertEqual(len(expectedLines), 1)
- self.assertIn(line, expectedLines)
+ self.assertEqual(len(expectedLines), 1, 'line count')
+ self.assertIn(line, expectedLines, 'actual line')
logger.removeHandler(handler)
def messages_preserved_if_excp(self):
@@ -72,7 +72,7 @@ def messages_preserved_if_excp(self):
server_thread.join()
logdna_thread.join()
- self.assertEqual(len(handler.buf), 1)
+ self.assertEqual(len(handler.buf), 1, 'line count')
logger.removeHandler(handler)
def stops_retention_when_buf_is_full(self):
@@ -99,8 +99,8 @@ def stops_retention_when_buf_is_full(self):
server_thread.join()
logdna_thread.join()
- self.assertEqual(len(handler.buf), 1)
- self.assertNotEqual(handler.buf[0]['line'], lineTwo)
+ self.assertEqual(len(handler.buf), 1, 'line count')
+ self.assertNotEqual(handler.buf[0]['line'], lineTwo, 'test inequality')
logger.removeHandler(handler)
def test_run_tests(self):
From 72da47a740455a61e4592db8e132ed2aa741cba2 Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Tue, 15 Jun 2021 16:32:50 +0400
Subject: [PATCH 22/55] test(logger): modify testing for logger
Semver: patch
---
logdna/configs.py | 4 +-
logdna/logdna.py | 19 +++--
pyproject.toml | 2 +-
tests/mock/__init.__.py | 0
tests/mock/log.py | 18 -----
tests/mock/server.py | 19 -----
tests/test_logger.py | 149 +++++++++++++++++-----------------------
7 files changed, 75 insertions(+), 136 deletions(-)
delete mode 100644 tests/mock/__init.__.py
delete mode 100644 tests/mock/log.py
delete mode 100644 tests/mock/server.py
diff --git a/logdna/configs.py b/logdna/configs.py
index 9dac4a8..5e03d92 100644
--- a/logdna/configs.py
+++ b/logdna/configs.py
@@ -6,13 +6,13 @@
defaults = {
'DEFAULT_REQUEST_TIMEOUT': 30,
- 'FLUSH_INTERVAL': 250,
+ 'FLUSH_INTERVAL_SECS': 0.25,
'FLUSH_LIMIT': 2 * 1024 * 1024,
'MAX_CONCURRENT_REQUESTS': 10,
'MAX_RETRY_ATTEMPTS': 3,
'MAX_RETRY_JITTER': 0.5,
'LOGDNA_URL': 'https://logs.logdna.com/logs/ingest',
'BUF_RETENTION_LIMIT': 4 * 1024 * 1024,
- 'RETRY_INTERVAL': 5000,
+ 'RETRY_INTERVAL_SECS': 5,
'USER_AGENT': 'python/%s' % version
}
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 5f2e299..8ec4bd7 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -29,7 +29,7 @@ def __init__(self, key, options={}):
self.hostname = options.get('hostname', socket.gethostname())
self.ip = options.get('ip', get_ip())
self.mac = options.get('mac', None)
- self.level = options.get('level', 'info')
+ self.loglevel = options.get('level', 'info')
self.app = options.get('app', '')
self.env = options.get('env', '')
self.tags = options.get('tags', [])
@@ -49,8 +49,8 @@ def __init__(self, key, options={}):
defaults['MAX_RETRY_JITTER'])
self.max_concurrent_requests = options.get(
'max_concurrent_requests', defaults['MAX_CONCURRENT_REQUESTS'])
- self.retry_interval = options.get('retry_interval',
- defaults['RETRY_INTERVAL'])
+ self.retry_interval_secs = options.get('retry_interval_secs',
+ defaults['RETRY_INTERVAL_SECS'])
# Set the Flush-related Variables
self.buf = []
@@ -62,8 +62,8 @@ def __init__(self, key, options={}):
False)
self.index_meta = options.get('index_meta', False)
self.flush_limit = options.get('flush_limit', defaults['FLUSH_LIMIT'])
- self.flush_interval = options.get('flush_interval',
- defaults['FLUSH_INTERVAL'])
+ self.flush_interval_secs = options.get('flush_interval',
+ defaults['FLUSH_INTERVAL_SECS'])
self.buf_retention_limit = options.get('buf_retention_limit',
defaults['BUF_RETENTION_LIMIT'])
@@ -77,9 +77,8 @@ def __init__(self, key, options={}):
def start_flusher(self):
if not self.flusher:
- interval = (self.retry_interval
- if self.exception_flag else self.flush_interval)
- self.flusher = threading.Timer(float(interval / 1000), self.flush)
+ self.flusher = threading.Timer(self.flush_interval_secs,
+ self.flush)
self.flusher.start()
def close_flusher(self):
@@ -168,7 +167,7 @@ def try_request(self):
self.clean_after_success()
break
- sleep_time = self.retry_interval * (1 << (retries - 1))
+ sleep_time = self.retry_interval_secs * (1 << (retries - 1))
sleep_time += self.max_retry_jitter
time.sleep(sleep_time)
@@ -224,7 +223,7 @@ def emit(self, record):
'hostname': self.hostname,
'timestamp': int(time.time() * 1000),
'line': msg,
- 'level': record['levelname'] or self.level,
+ 'level': record['levelname'] or self.loglevel,
'app': self.app or record['module'],
'env': self.env
}
diff --git a/pyproject.toml b/pyproject.toml
index 5a1ce48..1300100 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -49,7 +49,7 @@ branch = true
source = ["logdna"]
[tool.coverage.report]
-fail_under = 67
+fail_under = 40
show_missing = true
[tool.coverage.json]
diff --git a/tests/mock/__init.__.py b/tests/mock/__init.__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/mock/log.py b/tests/mock/log.py
deleted file mode 100644
index d9999e0..0000000
--- a/tests/mock/log.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import logging
-from threading import Thread
-
-__all__ = ['logger', 'info', 'LOGDNA_API_KEY']
-LOGDNA_API_KEY = '< YOUR INGESTION KEY HERE >'
-logger = logging.getLogger('logdna')
-logger.setLevel(logging.INFO)
-
-
-def info(*args):
- def fn():
- for line in args:
- logger.info(line)
-
- thread = Thread(target=fn)
- thread.setDaemon(True)
- thread.start()
- return thread
diff --git a/tests/mock/server.py b/tests/mock/server.py
deleted file mode 100644
index c0a1791..0000000
--- a/tests/mock/server.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import socket
-from http.server import HTTPServer
-from threading import Thread
-
-
-def get_port():
- s = socket.socket(socket.AF_INET, type=socket.SOCK_STREAM)
- s.bind(('localhost', 0))
- _, port = s.getsockname()
- s.close()
- return port
-
-
-def start_server(port, cls):
- server = HTTPServer(('localhost', port), cls)
- thread = Thread(target=server.handle_request)
- thread.setDaemon(True)
- thread.start()
- return thread
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 8bc7d6b..6820ee1 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -1,112 +1,89 @@
-import json
+import logging
import unittest
-from http.server import BaseHTTPRequestHandler
from logdna import LogDNAHandler
-from .mock.server import get_port, start_server
-from .mock.log import logger, info, LOGDNA_API_KEY
+from concurrent.futures import ThreadPoolExecutor
+from logdna.configs import defaults
expectedLines = []
-
-
-class SuccessfulRequestHandler(BaseHTTPRequestHandler):
- def do_POST(self):
- content_length = int(self.headers['Content-Length'])
- body = self.rfile.read(content_length)
- self.send_response(200)
-
- self.end_headers()
- body = json.loads(body)['ls']
- for keys in body:
- expectedLines.append(keys['line'])
-
-
-class FailedRequestHandler(BaseHTTPRequestHandler):
- def do_POST(self):
- content_length = int(self.headers['Content-Length'])
- self.rfile.read(content_length)
- self.send_response(400)
- self.end_headers()
+LOGDNA_API_KEY = '< YOUR INGESTION KEY HERE >'
+logger = logging.getLogger('logdna')
+logger.setLevel(logging.INFO)
class LogDNAHandlerTest(unittest.TestCase):
- def server_recieves_messages(self):
- port = get_port()
+ def handler_test(self):
options = {
'hostname': 'localhost',
- 'url': 'http://localhost:{0}'.format(port),
'ip': '10.0.1.1',
- 'mac': 'C0:FF:EE:C0:FF:EE'
- }
-
- handler = LogDNAHandler(LOGDNA_API_KEY, options)
- logger.addHandler(handler)
- line = "python python python"
-
- server_thread = start_server(port, SuccessfulRequestHandler)
- logdna_thread = info(line)
-
- server_thread.join()
- logdna_thread.join()
-
- self.assertEqual(len(expectedLines), 1, 'line count')
- self.assertIn(line, expectedLines, 'actual line')
- logger.removeHandler(handler)
-
- def messages_preserved_if_excp(self):
- port = get_port()
- options = {
- 'hostname': 'localhost',
- 'url': 'http://localhost:{0}'.format(port),
- 'ip': '10.0.1.1',
- 'mac': 'C0:FF:EE:C0:FF:EE'
+ 'mac': 'C0:FF:EE:C0:FF:EE',
+ 'tags': 'sample,test'
}
handler = LogDNAHandler(LOGDNA_API_KEY, options)
- logger.addHandler(handler)
- line = "second test. server fails"
-
- server_thread = start_server(port, FailedRequestHandler)
- logdna_thread = info(line)
-
- server_thread.join()
- logdna_thread.join()
-
- self.assertEqual(len(handler.buf), 1, 'line count')
- logger.removeHandler(handler)
-
- def stops_retention_when_buf_is_full(self):
- port = get_port()
+ self.assertIsInstance(handler, logging.Handler)
+ self.assertIsInstance(handler.internal_handler, logging.StreamHandler)
+ self.assertIsNotNone(handler.internalLogger)
+ self.assertEqual(handler.key, LOGDNA_API_KEY)
+ self.assertEqual(handler.hostname, options['hostname'])
+ self.assertEqual(handler.ip, options['ip'])
+ self.assertEqual(handler.mac, options['mac'])
+ self.assertEqual(handler.loglevel, 'info')
+ self.assertEqual(handler.app, '')
+ self.assertEqual(handler.env, '')
+ self.assertEqual(handler.tags, options['tags'].split(','))
+
+ # Set the Connection Variables
+ self.assertEqual(handler.url, defaults['LOGDNA_URL'])
+ self.assertEqual(handler.request_timeout,
+ defaults['DEFAULT_REQUEST_TIMEOUT'])
+ self.assertEqual(handler.user_agent, defaults['USER_AGENT'])
+ self.assertEqual(handler.max_retry_attempts,
+ defaults['MAX_RETRY_ATTEMPTS'])
+ self.assertEqual(handler.max_retry_jitter,
+ defaults['MAX_RETRY_JITTER'])
+ self.assertEqual(handler.max_concurrent_requests,
+ defaults['MAX_CONCURRENT_REQUESTS'])
+ self.assertEqual(handler.retry_interval_secs,
+ defaults['RETRY_INTERVAL_SECS'])
+
+ # Set the Flush-related Variables
+ self.assertEqual(handler.buf, [])
+ self.assertEqual(handler.buf_size, 0)
+ self.assertEqual(handler.secondary, [])
+ self.assertFalse(handler.exception_flag)
+ self.assertIsNone(handler.flusher)
+ self.assertFalse(handler.include_standard_meta)
+ self.assertFalse(handler.index_meta)
+ self.assertEqual(handler.flush_limit, defaults['FLUSH_LIMIT'])
+ self.assertEqual(handler.flush_interval_secs,
+ defaults['FLUSH_INTERVAL_SECS'])
+ self.assertEqual(handler.buf_retention_limit,
+ defaults['BUF_RETENTION_LIMIT'])
+
+ # Set up the Thread Pools
+ self.assertIsInstance(handler.worker_thread_pool, ThreadPoolExecutor)
+ self.assertIsInstance(handler.request_thread_pool, ThreadPoolExecutor)
+ self.assertEqual(handler.level, logging.DEBUG)
+
+ def flusher_test(self):
options = {
'hostname': 'localhost',
- 'url': 'http://localhost:{0}'.format(port),
'ip': '10.0.1.1',
'mac': 'C0:FF:EE:C0:FF:EE',
- 'buf_retention_limit': 50,
- 'equest_timeout': 10,
- 'flush_interval': 1000,
- 'retry_interval': 1000
+ 'tags': 'sample,test'
}
handler = LogDNAHandler(LOGDNA_API_KEY, options)
- logger.addHandler(handler)
- line = "when buffer grows bigger than we want"
- lineTwo = "when buffer grows bigger than we want. And more and more"
-
- server_thread = start_server(port, FailedRequestHandler)
- logdna_thread = info(line, lineTwo)
-
- server_thread.join()
- logdna_thread.join()
-
- self.assertEqual(len(handler.buf), 1, 'line count')
- self.assertNotEqual(handler.buf[0]['line'], lineTwo, 'test inequality')
- logger.removeHandler(handler)
+ self.assertIsNone(handler.flusher)
+ handler.start_flusher()
+ self.assertIsNotNone(handler.flusher)
+ handler.close_flusher()
+ self.assertIsNone(handler.flusher)
def test_run_tests(self):
- self.server_recieves_messages()
- self.messages_preserved_if_excp()
- self.stops_retention_when_buf_is_full()
+ self.handler_test()
+ self.flusher_test()
if __name__ == '__main__':
From f250237dbde932e99ab199023516f66a248c5e80 Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Mon, 28 Jun 2021 21:24:21 +0400
Subject: [PATCH 23/55] feat(meta): enable adding custom meta fields
This commit introduces a new option called `custom_fields` similar to `tags` and also new default `meta` fields: `lineno`, `name`, `pathname`, `args`, etc.
Semver: minor
---
README.md | 21 +++----
logdna/configs.py | 1 +
logdna/logdna.py | 63 +++++++++----------
logdna/utils.py | 19 +++++-
pyproject.toml | 4 +-
tests/test_logger.py | 141 ++++++++++++++++++++++++++++++++++++-------
tests/test_utils.py | 19 +++++-
7 files changed, 193 insertions(+), 75 deletions(-)
diff --git a/README.md b/README.md
index 8aec2fc..ba9ab0f 100644
--- a/README.md
+++ b/README.md
@@ -67,10 +67,9 @@ _**Required**_
* [LogDNA Ingestion Key](https://app.logdna.com/manage/profile)
_**Optional**_
-* Hostname - ([string][]) - max length 32 chars
+* Hostname - ([string][])
* MAC Address - ([string][])
* IP Address - ([string][])
-* Max Length - ([bool][]) - formatted as options['max_length']
* Index Meta - ([bool][]) - formatted as options['index_meta']
## Usage
@@ -156,7 +155,6 @@ The [LogDNA API Key](https://app.logdna.com/manage/profile) associated with your
* Type: [string][]
* Default: `''`
* Values: `YourCustomApp`
-* Max Length: `32`
The default app passed along with every log sent through this instance.
@@ -166,7 +164,6 @@ The default app passed along with every log sent through this instance.
* Type: [string][]
* Default: `''`
* Values: `YourCustomEnv`
-* Max Length: `32`
The default env passed along with every log sent through this instance.
@@ -176,7 +173,6 @@ The default env passed along with every log sent through this instance.
* Type: [string][]
* Default: `''`
* Values: `YourCustomHostname`
-* Max Length: `32`
The default hostname passed along with every log sent through this instance.
@@ -190,6 +186,7 @@ Python [LogRecord][] objects includes language-specific information that may be
Setting `include_standard_meta` to `True` automatically populates meta objects with `name`, `pathname`, and `lineno`
from the [LogRecord][].
+*WARNING* This option is deprecated and will be removed in the upcoming major release.
##### index_meta
@@ -203,14 +200,12 @@ If this option is set to True then meta objects are parsed and searchable up to
*WARNING* If this option is True, your metadata objects MUST have consistent types across all log messages or the metadata object may not be parsed properly.
-
##### level
* _Optional_
* Type: [string][]
* Default: `Info`
* Values: `Debug`, `Trace`, `Info`, `Warn`, `Error`, `Fatal`, `YourCustomLevel`
-* Max Length: `32`
The default level passed along with every log sent through this instance.
@@ -247,6 +242,14 @@ List of tags used to dynamically group hosts. More information on tags is avail
A custom ingestion endpoint to stream log lines into.
+##### custom_fields
+
+* _Optional_
+* Type: [list][]<[string][]>
+* Default: `['args', 'name', 'pathname', 'lineno']`
+
+List of fields out of `record` object to include in the `meta` object. By default, `args`, `name`, `pathname`, and `lineno` will be included.
+
### log(line, [options])
#### line
@@ -254,7 +257,6 @@ A custom ingestion endpoint to stream log lines into.
* _Required_
* Type: [string][]
* Default: `''`
-* Max Length: `32000`
The log line to be sent to LogDNA.
@@ -266,7 +268,6 @@ The log line to be sent to LogDNA.
* Type: [string][]
* Default: `Info`
* Values: `Debug`, `Trace`, `Info`, `Warn`, `Error`, `Fatal`, `YourCustomLevel`
-* Max Length: `32`
The level passed along with this log line.
@@ -276,7 +277,6 @@ The level passed along with this log line.
* Type: [string][]
* Default: `''`
* Values: `YourCustomApp`
-* Max Length: `32`
The app passed along with this log line.
@@ -286,7 +286,6 @@ The app passed along with this log line.
* Type: [string][]
* Default: `''`
* Values: `YourCustomEnv`
-* Max Length: `32`
The environment passed with this log line.
diff --git a/logdna/configs.py b/logdna/configs.py
index 5e03d92..d790776 100644
--- a/logdna/configs.py
+++ b/logdna/configs.py
@@ -11,6 +11,7 @@
'MAX_CONCURRENT_REQUESTS': 10,
'MAX_RETRY_ATTEMPTS': 3,
'MAX_RETRY_JITTER': 0.5,
+ 'META_FIELDS': ['args', 'name', 'pathname', 'lineno'],
'LOGDNA_URL': 'https://logs.logdna.com/logs/ingest',
'BUF_RETENTION_LIMIT': 4 * 1024 * 1024,
'RETRY_INTERVAL_SECS': 5,
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 8ec4bd7..4a835e3 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -1,4 +1,3 @@
-import json
import logging
import requests
import socket
@@ -9,7 +8,7 @@
from concurrent.futures import ThreadPoolExecutor
from .configs import defaults
-from .utils import sanitize_meta, get_ip
+from .utils import sanitize_meta, get_ip, normalize_list_option
class LogDNAHandler(logging.Handler):
@@ -32,11 +31,9 @@ def __init__(self, key, options={}):
self.loglevel = options.get('level', 'info')
self.app = options.get('app', '')
self.env = options.get('env', '')
- self.tags = options.get('tags', [])
- if isinstance(self.tags, str):
- self.tags = [tag.strip() for tag in self.tags.split(',')]
- elif not isinstance(self.tags, list):
- self.tags = []
+ self.tags = normalize_list_option(options, 'tags')
+ self.custom_fields = normalize_list_option(options, 'custom_fields')
+ self.custom_fields += defaults['META_FIELDS']
# Set the Connection Variables
self.url = options.get('url', defaults['LOGDNA_URL'])
@@ -58,8 +55,13 @@ def __init__(self, key, options={}):
self.secondary = []
self.exception_flag = False
self.flusher = None
- self.include_standard_meta = options.get('include_standard_meta',
- False)
+ self.include_standard_meta = options.get('include_standard_meta', None)
+
+ if self.include_standard_meta is not None:
+ self.internalLogger.debug(
+ '"include_standard_meta" option will be deprecated ' +
+ 'removed in the upcoming major release')
+
self.index_meta = options.get('index_meta', False)
self.flush_limit = options.get('flush_limit', defaults['FLUSH_LIMIT'])
self.flush_interval_secs = options.get('flush_interval',
@@ -153,16 +155,8 @@ def try_request(self):
self.secondary = []
data = {'e': 'ls', 'ls': self.buf}
retries = 0
- while True:
+ while retries < self.max_retry_attempts:
retries += 1
- if retries > self.max_retry_attempts:
- self.internalLogger.debug(
- 'Flush exceeded %s tries. Discarding flush buffer',
- self.max_retry_attempts)
- self.close_flusher()
- self.exception_flag = True
- break
-
if self.send_request(data):
self.clean_after_success()
break
@@ -171,6 +165,13 @@ def try_request(self):
sleep_time += self.max_retry_jitter
time.sleep(sleep_time)
+ if retries >= self.max_retry_attempts:
+ self.internalLogger.debug(
+ 'Flush exceeded %s tries. Discarding flush buffer',
+ self.max_retry_attempts)
+ self.close_flusher()
+ self.exception_flag = True
+
def send_request(self, data):
try:
response = requests.post(url=self.url,
@@ -228,25 +229,15 @@ def emit(self, record):
'env': self.env
}
- opts = {}
- if 'args' in record and not isinstance(record['args'], tuple):
- opts = record['args']
-
- for key in message.keys():
- if key in opts:
- message[key] = opts[key]
-
- if self.include_standard_meta:
- if 'meta' not in opts:
- opts['meta'] = {}
- for key in ['name', 'pathname', 'lineno']:
- if key in record:
- opts['meta'][key] = record[key]
- if self.index_meta:
- message['meta'] = sanitize_meta(opts['meta'])
- else:
- message['meta'] = json.dumps(opts['meta'])
+ message['meta'] = {}
+ for key in self.custom_fields:
+ if key in record:
+ if isinstance(record[key], tuple):
+ message['meta'][key] = list(record[key])
+ elif record[key] is not None:
+ message['meta'][key] = record[key]
+ message['meta'] = sanitize_meta(message['meta'], self.index_meta)
self.buffer_log(message)
def close(self):
diff --git a/logdna/utils.py b/logdna/utils.py
index 790ae0a..7278f12 100644
--- a/logdna/utils.py
+++ b/logdna/utils.py
@@ -10,7 +10,24 @@ def is_jsonable(obj):
return False
-def sanitize_meta(meta):
+def normalize_list_option(options, key):
+ value = options.get(key, [])
+ if isinstance(value, str):
+ value = [val.strip() for val in value.split(',')]
+ elif not isinstance(value, list):
+ value = []
+ return value
+
+
+def sanitize_meta(meta, index_meta=False):
+ if not index_meta:
+ if is_jsonable(meta):
+ return json.dumps(meta)
+
+ return {
+ '__errors': 'Meta cannot be serialized into JSON-formatted string'
+ }
+
keys_to_sanitize = []
for key, value in meta.items():
if not is_jsonable(value):
diff --git a/pyproject.toml b/pyproject.toml
index 1300100..3f4c0a5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -29,7 +29,7 @@ python-semantic-release = "^7.15.3"
[tool.taskipy.tasks]
pre_test = "mkdir -p coverage"
-test = "pytest --junitxml=coverage/test.xml --cov=logdna --cov-report=html"
+test = "pytest --junitxml=coverage/test.xml --cov=logdna --cov-report=html --verbose"
post_test = "python scripts/json_coverage.py"
lint = "flake8 --doctests"
"lint:fix" = "yapf -r -i logdna scripts tests"
@@ -49,7 +49,7 @@ branch = true
source = ["logdna"]
[tool.coverage.report]
-fail_under = 40
+fail_under = 75
show_missing = true
[tool.coverage.json]
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 6820ee1..2c18b97 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -1,5 +1,6 @@
import logging
import unittest
+import requests
from logdna import LogDNAHandler
from concurrent.futures import ThreadPoolExecutor
@@ -9,29 +10,65 @@
LOGDNA_API_KEY = '< YOUR INGESTION KEY HERE >'
logger = logging.getLogger('logdna')
logger.setLevel(logging.INFO)
+sample_record = logging.LogRecord('test', logging.INFO, 'test', 5,
+ 'Something to test', '', '', '', '')
+sample_message = {
+ 'line': 'Something to test',
+ 'hostname': 'localhost',
+ 'level': 'INFO',
+ 'app': 'test',
+ 'env': '',
+ 'meta': {
+ 'args': '',
+ 'name': 'test',
+ 'pathname': 'test',
+ 'lineno': 5
+ }
+}
+sample_options = {
+ 'hostname': 'localhost',
+ 'ip': '10.0.1.1',
+ 'mac': 'C0:FF:EE:C0:FF:EE',
+ 'tags': 'sample,test',
+ 'index_meta': True
+}
+
+
+class MockThreadPoolExecutor():
+ def __init__(self, **kwargs):
+ pass
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_traceback):
+ pass
+
+ def submit(self, fn, *args, **kwargs):
+ # execute functions in series without creating threads
+ # for easier unit testing
+ result = fn(*args, **kwargs)
+ return result
+
+ def shutdown(self, wait=True):
+ pass
class LogDNAHandlerTest(unittest.TestCase):
def handler_test(self):
- options = {
- 'hostname': 'localhost',
- 'ip': '10.0.1.1',
- 'mac': 'C0:FF:EE:C0:FF:EE',
- 'tags': 'sample,test'
- }
-
- handler = LogDNAHandler(LOGDNA_API_KEY, options)
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
self.assertIsInstance(handler, logging.Handler)
self.assertIsInstance(handler.internal_handler, logging.StreamHandler)
self.assertIsNotNone(handler.internalLogger)
self.assertEqual(handler.key, LOGDNA_API_KEY)
- self.assertEqual(handler.hostname, options['hostname'])
- self.assertEqual(handler.ip, options['ip'])
- self.assertEqual(handler.mac, options['mac'])
+ self.assertEqual(handler.hostname, sample_options['hostname'])
+ self.assertEqual(handler.ip, sample_options['ip'])
+ self.assertEqual(handler.mac, sample_options['mac'])
self.assertEqual(handler.loglevel, 'info')
self.assertEqual(handler.app, '')
self.assertEqual(handler.env, '')
- self.assertEqual(handler.tags, options['tags'].split(','))
+ self.assertEqual(handler.tags, sample_options['tags'].split(','))
+ self.assertEqual(handler.custom_fields, defaults['META_FIELDS'])
# Set the Connection Variables
self.assertEqual(handler.url, defaults['LOGDNA_URL'])
@@ -53,8 +90,7 @@ def handler_test(self):
self.assertEqual(handler.secondary, [])
self.assertFalse(handler.exception_flag)
self.assertIsNone(handler.flusher)
- self.assertFalse(handler.include_standard_meta)
- self.assertFalse(handler.index_meta)
+ self.assertTrue(handler.index_meta)
self.assertEqual(handler.flush_limit, defaults['FLUSH_LIMIT'])
self.assertEqual(handler.flush_interval_secs,
defaults['FLUSH_INTERVAL_SECS'])
@@ -67,23 +103,84 @@ def handler_test(self):
self.assertEqual(handler.level, logging.DEBUG)
def flusher_test(self):
- options = {
- 'hostname': 'localhost',
- 'ip': '10.0.1.1',
- 'mac': 'C0:FF:EE:C0:FF:EE',
- 'tags': 'sample,test'
- }
-
- handler = LogDNAHandler(LOGDNA_API_KEY, options)
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
self.assertIsNone(handler.flusher)
handler.start_flusher()
self.assertIsNotNone(handler.flusher)
handler.close_flusher()
self.assertIsNone(handler.flusher)
+ def emit_test(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ handler.buffer_log = unittest.mock.Mock()
+ handler.emit(sample_record)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buffer_log.assert_called_once_with(sample_message)
+
+ def try_request_test(self):
+ requests.post = unittest.mock.Mock()
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ requests.post.assert_called_with(
+ url=handler.url,
+ json={
+ 'e': 'ls',
+ 'ls': handler.buf
+ },
+ auth=('user', handler.key),
+ params={
+ 'hostname': handler.hostname,
+ 'ip': handler.ip,
+ 'mac': handler.mac,
+ 'tags': handler.tags
+ },
+ stream=True,
+ timeout=handler.request_timeout,
+ headers={'user-agent': handler.user_agent})
+
+ def close_test(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ handler.close_flusher = unittest.mock.Mock()
+ handler.flush_sync = unittest.mock.Mock()
+ handler.close()
+ handler.close_flusher.assert_called_once_with()
+ handler.flush_sync.assert_called_once_with()
+ self.assertIsNone(handler.worker_thread_pool)
+ self.assertIsNone(handler.request_thread_pool)
+
+ def flush_test(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ handler.worker_thread_pool = MockThreadPoolExecutor()
+ handler.request_thread_pool = MockThreadPoolExecutor()
+ handler.buf_size += 1
+ handler.try_request = unittest.mock.Mock()
+ handler.flush()
+ handler.try_request.assert_called_once_with()
+
+ def buffer_log_test(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ handler.worker_thread_pool = MockThreadPoolExecutor()
+ handler.request_thread_pool = MockThreadPoolExecutor()
+ handler.close_flusher = unittest.mock.Mock()
+ handler.flush = unittest.mock.Mock()
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.flush_limit = 0
+ handler.buffer_log(sample_message)
+ handler.close_flusher.assert_called_once_with()
+ handler.flush.assert_called_once_with()
+ self.assertEqual(handler.buf, [sample_message])
+ self.assertEqual(handler.buf_size, len(sample_message['line']))
+
def test_run_tests(self):
self.handler_test()
self.flusher_test()
+ self.emit_test()
+ self.try_request_test()
+ self.close_test()
+ self.flush_test()
+ self.buffer_log_test()
if __name__ == '__main__':
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 21c990b..446840f 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,6 +1,9 @@
import unittest
from unittest.mock import patch
-from logdna.utils import is_jsonable, sanitize_meta, get_ip
+from logdna.utils import is_jsonable
+from logdna.utils import sanitize_meta
+from logdna.utils import get_ip
+from logdna.utils import normalize_list_option
IP = '10.0.50.10'
VIP = '10.1.60.20'
@@ -25,11 +28,11 @@ def setUp(self):
self.invalid = {'bar': 'foo', 'baz': set()}
def test_sanitize_simple(self):
- clean = sanitize_meta(self.valid)
+ clean = sanitize_meta(self.valid, True)
self.assertDictEqual(clean, self.valid)
def test_sanitize_complex(self):
- clean = sanitize_meta(self.invalid)
+ clean = sanitize_meta(self.invalid, True)
self.assertDictEqual(clean, {
'bar': 'foo',
'__errors': 'These keys have been sanitized: baz'
@@ -46,3 +49,13 @@ def test_get_ip_socket_error(self, _):
**{'return_value.getsockname.return_value': [IP, VIP]})
def test_get_ip_default(self, _):
self.assertEqual(get_ip(), IP, 'default to localhost on error')
+
+
+class NormalizeListOptionTest(unittest.TestCase):
+ def test_normalize_simple(self):
+ value1 = normalize_list_option({'tags': ' a, b'}, 'tags')
+ value2 = normalize_list_option({'tags': ['a', 'b']}, 'tags')
+ value3 = normalize_list_option({'tags': ('a', 'b')}, 'tags')
+ self.assertEqual(value1, ['a', 'b'])
+ self.assertEqual(value1, value2)
+ self.assertEqual(value3, [])
From 5dd754f177675eb25cd5d5449bd3bcc8286f8739 Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Thu, 15 Jul 2021 17:20:27 +0400
Subject: [PATCH 24/55] docs: update the README as requested
Semver: patch
---
README.md | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index ba9ab0f..5140281 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
* [Installation](#installation)
* [Setup](#setup)
* [Usage](#usage)
- * [Usage with File Config](#usage-with-file-config)
+ * [Usage with fileConfig](#usage-with-fileconfig)
* [API](#api)
* [LogDNAHandler(key: string, [options: dict])](#logdnahandlerkey-string-options-dict)
* [key](#key)
@@ -70,7 +70,7 @@ _**Optional**_
* Hostname - ([string][])
* MAC Address - ([string][])
* IP Address - ([string][])
-* Index Meta - ([bool][]) - formatted as options['index_meta']
+* Index Meta - ([bool][]) - formatted as `options['index_meta']`
## Usage
@@ -101,7 +101,7 @@ opts = {
log.info('My Sample Log Line', opts)
```
-### Usage with File Config
+### Usage with fileConfig
To use [LogDNAHandler](#logdnahandlerkey-string-options-dict) with [fileConfig][] (e.g., in a Django `settings.py` file):
@@ -116,7 +116,7 @@ LOGGING = {
'logdna': {
'level': logging.DEBUG,
'class': 'logging.handlers.LogDNAHandler',
- 'key': os.environ.get('LOGDNA_INGEST_KEY'),
+ 'key': os.environ.get('LOGDNA_INGESTION_KEY'),
'options': {
'app': '',
'env': os.environ.get('ENVIRONMENT'),
@@ -133,7 +133,7 @@ LOGGING = {
}
```
-(This example assumes you have set environment variables for `ENVIRONMENT` and `LOGDNA_INGEST_KEY`)
+(This example assumes you have set environment variables for `ENVIRONMENT` and `LOGDNA_INGESTION_KEY`.)
## API
@@ -143,7 +143,7 @@ LOGGING = {
* _**Required**_
* Type: [string][]
-* Values: `YourAPIKey`
+* Values: ``
The [LogDNA API Key](https://app.logdna.com/manage/profile) associated with your account.
@@ -154,16 +154,16 @@ The [LogDNA API Key](https://app.logdna.com/manage/profile) associated with your
* _Optional_
* Type: [string][]
* Default: `''`
-* Values: `YourCustomApp`
+* Values: ``
-The default app passed along with every log sent through this instance.
+The default app named that is included in every every log line sent through this instance.
##### env
* _Optional_
* Type: [string][]
* Default: `''`
-* Values: `YourCustomEnv`
+* Values: ``
The default env passed along with every log sent through this instance.
@@ -172,7 +172,7 @@ The default env passed along with every log sent through this instance.
* _Optional_
* Type: [string][]
* Default: `''`
-* Values: `YourCustomHostname`
+* Values: ``
The default hostname passed along with every log sent through this instance.
@@ -198,14 +198,14 @@ We allow meta objects to be passed with each line. By default these meta objects
If this option is set to True then meta objects are parsed and searchable up to three levels deep. Any fields deeper than three levels are stringified and cannot be searched.
-*WARNING* If this option is True, your metadata objects MUST have consistent types across all log messages or the metadata object may not be parsed properly.
+*WARNING* If this option is True, your metadata objects MUST have consistent types across all log messages or the metadata object might not be parsed properly.
##### level
* _Optional_
* Type: [string][]
* Default: `Info`
-* Values: `Debug`, `Trace`, `Info`, `Warn`, `Error`, `Fatal`, `YourCustomLevel`
+* Values: `Debug`, `Trace`, `Info`, `Warn`, `Error`, `Fatal`, ``
The default level passed along with every log sent through this instance.
@@ -267,7 +267,7 @@ The log line to be sent to LogDNA.
* _Optional_
* Type: [string][]
* Default: `Info`
-* Values: `Debug`, `Trace`, `Info`, `Warn`, `Error`, `Fatal`, `YourCustomLevel`
+* Values: `Debug`, `Trace`, `Info`, `Warn`, `Error`, `Fatal`, ``
The level passed along with this log line.
@@ -276,7 +276,7 @@ The level passed along with this log line.
* _Optional_
* Type: [string][]
* Default: `''`
-* Values: `YourCustomApp`
+* Values: ``
The app passed along with this log line.
@@ -285,7 +285,7 @@ The app passed along with this log line.
* _Optional_
* Type: [string][]
* Default: `''`
-* Values: `YourCustomEnv`
+* Values: ``
The environment passed with this log line.
From 946aa732e4d60de71e0357dda815a4e8bc852b70 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 15 Jul 2021 14:30:17 +0000
Subject: [PATCH 25/55] release: Version 1.17.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 8 ++++++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3dd813e..45b74ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
+## v1.17.0 (2021-07-15)
+### Feature
+* **meta:** Enable adding custom meta fields ([`f250237`](https://github.com/logdna/python/commit/f250237dbde932e99ab199023516f66a248c5e80))
+* **threadWorkerPools:** Introduce extra threads ([`9bfe479`](https://github.com/logdna/python/commit/9bfe479132acb0aa8e5784f2aa31298606e49789))
+
+### Documentation
+* Update the README as requested ([`5dd754f`](https://github.com/logdna/python/commit/5dd754f177675eb25cd5d5449bd3bcc8286f8739))
+
## v1.16.0 (2021-04-15)
diff --git a/logdna/VERSION b/logdna/VERSION
index 15b989e..092afa1 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.16.0
+1.17.0
diff --git a/pyproject.toml b/pyproject.toml
index 3f4c0a5..f88222e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.16.0"
+version = "1.17.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From e326e4c2461b808b5d3a885b37555f8e610615e4 Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Mon, 26 Jul 2021 17:36:43 +0400
Subject: [PATCH 26/55] fix(opts): repair logging options for each call
Semver: patch
---
logdna/logdna.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 4a835e3..2ad02fc 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -238,6 +238,15 @@ def emit(self, record):
message['meta'][key] = record[key]
message['meta'] = sanitize_meta(message['meta'], self.index_meta)
+
+ opts = {}
+ if 'args' in record and not isinstance(record['args'], tuple):
+ opts = record['args']
+
+ for key in ['app', 'env', 'hostname', 'level', 'timestamp']:
+ if key in opts:
+ message[key] = opts[key]
+
self.buffer_log(message)
def close(self):
From 6918c2dd9b0045b88539876016a5ac69aa97e8a8 Mon Sep 17 00:00:00 2001
From: Samir Musali
Date: Mon, 26 Jul 2021 17:58:03 +0400
Subject: [PATCH 27/55] test(opts): test call-specific opts
Semver: patch
---
pyproject.toml | 2 +-
tests/test_logger.py | 19 +++++++++++++------
2 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index f88222e..ffc6f3c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -49,7 +49,7 @@ branch = true
source = ["logdna"]
[tool.coverage.report]
-fail_under = 75
+fail_under = 76
show_missing = true
[tool.coverage.json]
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 2c18b97..0c0b15e 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -10,16 +10,23 @@
LOGDNA_API_KEY = '< YOUR INGESTION KEY HERE >'
logger = logging.getLogger('logdna')
logger.setLevel(logging.INFO)
+sample_args = {
+ 'app': 'differentTest',
+ 'level': 'debug',
+ 'hostname': 'differentHost',
+ 'env': 'differentEnv'
+}
sample_record = logging.LogRecord('test', logging.INFO, 'test', 5,
- 'Something to test', '', '', '', '')
+ 'Something to test', [sample_args], '', '',
+ '')
sample_message = {
'line': 'Something to test',
- 'hostname': 'localhost',
- 'level': 'INFO',
- 'app': 'test',
- 'env': '',
+ 'hostname': 'differentHost',
+ 'level': 'debug',
+ 'app': 'differentTest',
+ 'env': 'differentEnv',
'meta': {
- 'args': '',
+ 'args': sample_args,
'name': 'test',
'pathname': 'test',
'lineno': 5
From 398e82f6d46db00f895cc94612751e0c559c2aaa Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Mon, 26 Jul 2021 15:20:36 +0000
Subject: [PATCH 28/55] release: Version 1.18.0 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45b74ae..d502474 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.0 (2021-07-26)
+### Fix
+* **opts:** Repair logging options for each call ([`e326e4c`](https://github.com/logdna/python/commit/e326e4c2461b808b5d3a885b37555f8e610615e4))
+
## v1.17.0 (2021-07-15)
### Feature
* **meta:** Enable adding custom meta fields ([`f250237`](https://github.com/logdna/python/commit/f250237dbde932e99ab199023516f66a248c5e80))
diff --git a/logdna/VERSION b/logdna/VERSION
index 092afa1..84cc529 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.17.0
+1.18.0
diff --git a/pyproject.toml b/pyproject.toml
index ffc6f3c..036a22e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.17.0"
+version = "1.18.0"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From d00b9529d116ddf9ba454462d4d804dc54423c83 Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Thu, 28 Oct 2021 15:30:49 -0500
Subject: [PATCH 29/55] fix(threading): account for secondary buffer flush and
deadlock
In the case that work cannot be scheduled on the worker pool, logs are
pushed onto the `secondary` list and the check before a send doesn't
account for any logs that may be pending in the `secondary`.
This can happen in the case of a run time error when attempting to
submit work to one of the thread pools effectively leaving logs unsent.
Additionally eliminates a situation where lock wasn't being released
during and error situation and would evenutally result in every tread
being locked. This would prevent logs from being sent
fixes: #74
---
Makefile | 12 ++++++++++--
logdna/logdna.py | 18 ++++++++++++------
tests/test_logger.py | 31 +++++++++++++++++++++++++++++--
3 files changed, 51 insertions(+), 10 deletions(-)
diff --git a/Makefile b/Makefile
index 3e4f1b4..f2dd7ec 100644
--- a/Makefile
+++ b/Makefile
@@ -38,6 +38,9 @@ debug-%: ## Debug a variable by calling `make debug-VARIABLE`
help: ## Show this help, includes list of all actions.
@awk 'BEGIN {FS = ":.*?## "}; /^.+: .*?## / && !/awk/ {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' ${MAKEFILE_LIST}
+.PHONY:run
+run: ## purge build time artifacts
+ $(DOCKER_COMMAND) bash
.PHONY:clean
clean: ## purge build time artifacts
rm -rf dist/ build/ coverage/ pypoetry/ pip/ **/__pycache__/ .pytest_cache/ .cache .coverage
@@ -63,11 +66,16 @@ package: ## Generate a python sdist and wheel
$(POETRY_COMMAND) build
.PHONY:release
-release: clean install ## run semantic release build and publish results to github + pypi based on unreleased commits
+release: clean install fetch-tags ## run semantic release build and publish results to github + pypi based on unreleased commits
$(POETRY_COMMAND) run task release
+.PHONY: fetch-tags
+fetch-tags: ## workaround for jenkins repo cloning behavior
+ git config remote.origin.url "https://logdnabot:${GH_TOKEN}@github.com/logdna/python"
+ git fetch origin --tags
+
.PHONY:release-dry
-release-dry: clean install changelog ## run semantic release in noop mode
+release-dry: clean install fetch-tags changelog ## run semantic release in noop mode
$(POETRY_COMMAND) run semantic-release publish --noop --verbosity=DEBUG
.PHONY:release-patch
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 2ad02fc..412ae7a 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -109,21 +109,21 @@ def buffer_log_sync(self, message):
self.internalLogger.debug(
'The buffer size exceeded the limit: %s',
self.buf_retention_limit)
- self.lock.release()
if self.buf_size >= self.flush_limit and not self.exception_flag:
self.close_flusher()
self.flush()
else:
self.start_flusher()
+ self.lock.release()
else:
self.secondary.append(message)
def clean_after_success(self):
- del self.buf[:]
+ self.close_flusher()
+ self.buf.clear()
self.buf_size = 0
self.exception_flag = False
- self.close_flusher()
def flush(self):
if self.worker_thread_pool:
@@ -135,8 +135,9 @@ def flush(self):
self.internalLogger.debug('Error in calling flush: %s', e)
def flush_sync(self):
- if self.buf_size == 0:
+ if self.buf_size == 0 and len(self.secondary) == 0:
return
+
if self.lock.acquire(blocking=False):
if self.request_thread_pool:
try:
@@ -146,8 +147,12 @@ def flush_sync(self):
except Exception as e:
self.internalLogger.debug(
'Error in calling try_request: %s', e)
- self.lock.release()
+ finally:
+ self.lock.release()
+ else:
+ self.lock.release()
else:
+ self.close_flusher()
self.start_flusher()
def try_request(self):
@@ -181,7 +186,8 @@ def send_request(self, data):
'hostname': self.hostname,
'ip': self.ip,
'mac': self.mac,
- 'tags': self.tags
+ 'tags': self.tags,
+ 'now': int(time.time() * 1000)
},
stream=True,
timeout=self.request_timeout,
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 0c0b15e..91e45e0 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -1,11 +1,14 @@
import logging
import unittest
import requests
+import time
from logdna import LogDNAHandler
from concurrent.futures import ThreadPoolExecutor
from logdna.configs import defaults
+from unittest import mock
+now = int(time.time())
expectedLines = []
LOGDNA_API_KEY = '< YOUR INGESTION KEY HERE >'
logger = logging.getLogger('logdna')
@@ -37,7 +40,8 @@
'ip': '10.0.1.1',
'mac': 'C0:FF:EE:C0:FF:EE',
'tags': 'sample,test',
- 'index_meta': True
+ 'index_meta': True,
+ 'now': int(time.time() * 1000)
}
@@ -124,6 +128,7 @@ def emit_test(self):
sample_message['timestamp'] = unittest.mock.ANY
handler.buffer_log.assert_called_once_with(sample_message)
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def try_request_test(self):
requests.post = unittest.mock.Mock()
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
@@ -141,7 +146,8 @@ def try_request_test(self):
'hostname': handler.hostname,
'ip': handler.ip,
'mac': handler.mac,
- 'tags': handler.tags
+ 'tags': handler.tags,
+ 'now': int(now * 1000)
},
stream=True,
timeout=handler.request_timeout,
@@ -166,6 +172,26 @@ def flush_test(self):
handler.flush()
handler.try_request.assert_called_once_with()
+ def flush_secondary_test(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ clean = handler.clean_after_success
+ handler.worker_thread_pool = MockThreadPoolExecutor()
+ handler.request_thread_pool = MockThreadPoolExecutor()
+ handler.buf_size = 0
+ handler.secondary.append(sample_message)
+ handler.send_request = unittest.mock.MagicMock(return_value=True)
+ handler.clean_after_success = unittest.mock.Mock()
+ self.assertEqual(handler.secondary, [sample_message])
+ handler.flush()
+ handler.send_request.assert_called_with({
+ 'e': 'ls',
+ 'ls': [sample_message]
+ })
+
+ clean()
+ self.assertEqual(handler.secondary, [])
+ self.assertEqual(handler.buf, [])
+
def buffer_log_test(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
handler.worker_thread_pool = MockThreadPoolExecutor()
@@ -187,6 +213,7 @@ def test_run_tests(self):
self.try_request_test()
self.close_test()
self.flush_test()
+ self.flush_secondary_test()
self.buffer_log_test()
From 6d5d432f5b9cf1083c54e7c74125d884c7674f9d Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 18 Nov 2021 22:40:26 +0000
Subject: [PATCH 30/55] release: Version 1.18.1 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d502474..0d33be9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.1 (2021-11-18)
+### Fix
+* **threading:** Account for secondary buffer flush and deadlock ([`d00b952`](https://github.com/logdna/python/commit/d00b9529d116ddf9ba454462d4d804dc54423c83))
+
## v1.18.0 (2021-07-26)
### Fix
* **opts:** Repair logging options for each call ([`e326e4c`](https://github.com/logdna/python/commit/e326e4c2461b808b5d3a885b37555f8e610615e4))
diff --git a/logdna/VERSION b/logdna/VERSION
index 84cc529..ec6d649 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.0
+1.18.1
diff --git a/pyproject.toml b/pyproject.toml
index 036a22e..02ba191 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.0"
+version = "1.18.1"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From e4128592aa9c2301c6467115148f2f23f88da9d3 Mon Sep 17 00:00:00 2001
From: Dmitri Khokhlov
Date: Tue, 10 May 2022 08:55:13 -0700
Subject: [PATCH 31/55] fix: requests error handling (#82)
- added locally built docker build-image
- fixed retries for 5xx errors
- fixed various leftover build issues
LOG-11814
---
Dockerfile | 7 +
Jenkinsfile | 19 +-
Makefile | 29 +-
logdna/logdna.py | 86 ++++-
poetry.lock | 863 ++++++++++++++++++++++++-------------------
pyproject.toml | 6 +-
tests/test_logger.py | 120 +++---
7 files changed, 653 insertions(+), 477 deletions(-)
create mode 100644 Dockerfile
mode change 100644 => 100755 Jenkinsfile
mode change 100644 => 100755 Makefile
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..db887bb
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,7 @@
+FROM condaforge/miniforge3:4.12.0-0
+
+RUN conda install -y gcc pip poetry=1.1.7 git
+RUN mkdir /workdir && chmod 777 /workdir
+RUN git config --global --add safe.directory /workdir
+WORKDIR /workdir
+
diff --git a/Jenkinsfile b/Jenkinsfile
old mode 100644
new mode 100755
index 009701d..f4f00ee
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -5,7 +5,12 @@ def DEFAULT_BRANCH = 'master'
def CURRENT_BRANCH = [env.CHANGE_BRANCH, env.BRANCH_NAME]?.find{branch -> branch != null}
pipeline {
- agent none
+ agent {
+ node {
+ label 'ec2-fleet'
+ customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
+ }
+ }
options {
timestamps()
@@ -18,12 +23,6 @@ pipeline {
stages {
stage('Test') {
- agent {
- node {
- label 'ec2-fleet'
- customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
- }
- }
steps {
sh 'make install lint test'
@@ -45,12 +44,6 @@ pipeline {
}
stage('Release') {
- agent {
- node {
- label 'ec2-fleet'
- customWorkspace "${PROJECT_NAME}-${BUILD_NUMBER}"
- }
- }
stages {
stage('dry run') {
diff --git a/Makefile b/Makefile
old mode 100644
new mode 100755
index f2dd7ec..a7f8886
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ include .config.mk
DOCKER = docker
DOCKER_RUN := $(DOCKER) run --rm -i
WORKDIR :=/workdir
-DOCKER_COMMAND := $(DOCKER_RUN) -v $(PWD):$(WORKDIR):Z -w $(WORKDIR) \
+DOCKER_COMMAND := $(DOCKER_RUN) -u "$(shell id -u)":"$(shell id -g)" -v $(PWD):$(WORKDIR):Z -w $(WORKDIR) \
-e XDG_CONFIG_HOME=$(WORKDIR) \
-e XDG_CACHE_HOME=$(WORKDIR) \
-e POETRY_CACHE_DIR=$(WORKDIR)/.cache \
@@ -21,13 +21,19 @@ DOCKER_COMMAND := $(DOCKER_RUN) -v $(PWD):$(WORKDIR):Z -w $(WORKDIR) \
-e GIT_AUTHOR_EMAIL \
-e GIT_COMMITTER_NAME \
-e GIT_COMMITTER_EMAIL \
- us.gcr.io/logdna-k8s/python:3.7-ci
+ logdna-poetry:local
+
POETRY_COMMAND := $(DOCKER_COMMAND) poetry
# Exports the variables for shell use
export
+# build image
+.PHONY:build-image
+build-image:
+ DOCKER_BUILDKIT=1 $(DOCKER) build -t logdna-poetry:local .
+
# This helper function makes debugging much easier.
.PHONY:debug-%
debug-%: ## Debug a variable by calling `make debug-VARIABLE`
@@ -39,30 +45,31 @@ help: ## Show this help, includes list of all actions.
@awk 'BEGIN {FS = ":.*?## "}; /^.+: .*?## / && !/awk/ {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' ${MAKEFILE_LIST}
.PHONY:run
-run: ## purge build time artifacts
+run: install ## purge build time artifacts
$(DOCKER_COMMAND) bash
+
.PHONY:clean
clean: ## purge build time artifacts
rm -rf dist/ build/ coverage/ pypoetry/ pip/ **/__pycache__/ .pytest_cache/ .cache .coverage
.PHONY:changelog
-changelog: ## print the next version of the change log to stdout
+changelog: install ## print the next version of the change log to stdout
$(POETRY_COMMAND) run semantic-release changelog --unreleased
.PHONY:install
-install: ## install development and build time dependencies
- $(POETRY_COMMAND) install --no-interaction -vvv
+install: build-image ## install development and build time dependencies
+ $(POETRY_COMMAND) install --no-interaction
.PHONY:lint
-lint: ## run lint rules and print error report
+lint: install ## run lint rules and print error report
$(POETRY_COMMAND) run task lint
.PHONY:lint-fix
-lint-fix:## attempt to auto fix linting error and report remaining errors
+lint-fix: install ## attempt to auto fix linting error and report remaining errors
$(POETRY_COMMAND) run task lint:fix
.PHONY:package
-package: ## Generate a python sdist and wheel
+package: install ## Generate a python sdist and wheel
$(POETRY_COMMAND) build
.PHONY:release
@@ -71,7 +78,6 @@ release: clean install fetch-tags ## run semantic release build and publish resu
.PHONY: fetch-tags
fetch-tags: ## workaround for jenkins repo cloning behavior
- git config remote.origin.url "https://logdnabot:${GH_TOKEN}@github.com/logdna/python"
git fetch origin --tags
.PHONY:release-dry
@@ -91,6 +97,5 @@ release-major: clean install ## run semantic release build an
$(POETRY_COMMAND) run semantic-release publish --major
.PHONY:test
-test: ## run project test suite
+test: install ## run project test suite
$(POETRY_COMMAND) run task test
-
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 412ae7a..ec20841 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -177,7 +177,13 @@ def try_request(self):
self.close_flusher()
self.exception_flag = True
- def send_request(self, data):
+ def send_request(self, data): # noqa: max-complexity: 13
+ """
+ Send log data to LogDNA server
+ Returns:
+ True - discard flush buffer
+ False - retry, keep flush buffer
+ """
try:
response = requests.post(url=self.url,
json=data,
@@ -190,38 +196,84 @@ def send_request(self, data):
'now': int(time.time() * 1000)
},
stream=True,
+ allow_redirects=True,
timeout=self.request_timeout,
headers={'user-agent': self.user_agent})
- response.raise_for_status()
status_code = response.status_code
- if status_code in [401, 403]:
- self.internalLogger.debug(
- 'Please provide a valid ingestion key.' +
- ' Discarding flush buffer')
- return True
-
+ '''
+ response code:
+ 1XX unexpected status
+ 200 expected status, OK
+ 2XX unexpected status
+ 301 302 303 unexpected status,
+ per "allow_redirects=True"
+ 3XX unexpected status
+ 401, 403 expected client error,
+ invalid ingestion key
+ 4XX unexpected client error
+ 500 502 503 507 expected server error, transient
+ 5XX unexpected server error
+ handling:
+ expected status discard flush buffer
+ unexpected status log + discard flush buffer
+ expected client error log + discard flush buffer
+ unexpected client error log + discard flush buffer
+ expected server error log + retry
+ unexpected server error log + discard flush buffer
+ '''
if status_code == 200:
- return True
+ return True # discard
- if status_code in [400, 500, 504]:
- self.internalLogger.debug('The request failed %s. Retrying...',
- response.reason)
- return True
+ if isinstance(response.reason, bytes):
+ # We attempt to decode utf-8 first because some servers
+ # choose to localize their reason strings. If the string
+ # isn't utf-8, we fall back to iso-8859-1 for all other
+ # encodings. (See PR #3538)
+ try:
+ reason = response.reason.decode('utf-8')
+ except UnicodeDecodeError:
+ reason = response.reason.decode('iso-8859-1')
else:
+ reason = response.reason
+
+ if 200 < status_code <= 399:
+ self.internalLogger.debug('Unexpected response: %s. ' +
+ 'Discarding flush buffer',
+ reason)
+ return True # discard
+
+ if status_code in [401, 403]:
self.internalLogger.debug(
- 'The request failed: %s. Retrying...', response.reason)
+ 'Please provide a valid ingestion key. ' +
+ 'Discarding flush buffer')
+ return True # discard
+
+ if 400 <= status_code <= 499:
+ self.internalLogger.debug('Client Error: %s. ' +
+ 'Discarding flush buffer',
+ reason)
+ return True # discard
+
+ if status_code in [500, 502, 503, 507]:
+ self.internalLogger.debug('Server Error: %s. Retrying...',
+ reason)
+ return False # retry
+
+ self.internalLogger.debug('The request failed: %s.' +
+ 'Discarding flush buffer',
+ reason)
except requests.exceptions.Timeout as timeout:
- self.internalLogger.debug('Timeout error occurred %s. Retrying...',
+ self.internalLogger.debug('Timeout Error: %s. Retrying...',
timeout)
+ return False # retry
except requests.exceptions.RequestException as exception:
self.internalLogger.debug(
'Error sending logs %s. Discarding flush buffer', exception)
- return True
- return False
+ return True # discard
def emit(self, record):
msg = self.format(record)
diff --git a/poetry.lock b/poetry.lock
index 8490cff..ae55ed5 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,6 +1,6 @@
[[package]]
name = "appnope"
-version = "0.1.2"
+version = "0.1.3"
description = "Disable App Nap on macOS >= 10.9"
category = "dev"
optional = false
@@ -16,17 +16,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
-version = "20.3.0"
+version = "21.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+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", "zope.interface", "furo", "sphinx", "pre-commit"]
-docs = ["furo", "sphinx", "zope.interface"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
+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", "cloudpickle"]
+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", "cloudpickle"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "backcall"
@@ -38,20 +38,23 @@ python-versions = "*"
[[package]]
name = "bleach"
-version = "3.3.0"
+version = "5.0.0"
description = "An easy safelist-based HTML-sanitizing tool."
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.7"
[package.dependencies]
-packaging = "*"
six = ">=1.9.0"
webencodings = "*"
+[package.extras]
+css = ["tinycss2 (>=1.1.0)"]
+dev = ["pip-tools (==6.5.1)", "pytest (==7.1.1)", "flake8 (==4.0.1)", "tox (==3.24.5)", "sphinx (==4.3.2)", "twine (==4.0.0)", "wheel (==0.37.1)", "hashin (==0.17.0)", "black (==22.3.0)", "mypy (==0.942)"]
+
[[package]]
name = "certifi"
-version = "2020.12.5"
+version = "2021.10.8"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
@@ -59,7 +62,7 @@ python-versions = "*"
[[package]]
name = "cffi"
-version = "1.14.5"
+version = "1.15.0"
description = "Foreign Function Interface for Python calling C code."
category = "dev"
optional = false
@@ -69,24 +72,31 @@ python-versions = "*"
pycparser = "*"
[[package]]
-name = "chardet"
-version = "4.0.0"
-description = "Universal encoding detector for Python 2 and 3"
+name = "charset-normalizer"
+version = "2.0.12"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.5.0"
+
+[package.extras]
+unicode_backport = ["unicodedata2"]
[[package]]
name = "click"
-version = "7.1.2"
+version = "8.1.3"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.7"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "click-log"
-version = "0.3.2"
+version = "0.4.0"
description = "Logging integration for Click"
category = "dev"
optional = false
@@ -105,7 +115,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "coverage"
-version = "5.4"
+version = "5.5"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
@@ -116,7 +126,7 @@ toml = ["toml"]
[[package]]
name = "cryptography"
-version = "3.4.7"
+version = "37.0.2"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
category = "dev"
optional = false
@@ -127,23 +137,23 @@ 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)"]
+docstest = ["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)"]
+sdist = ["setuptools_rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"]
-test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
+test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
[[package]]
name = "decorator"
-version = "4.4.2"
+version = "5.1.1"
description = "Decorators for Humans"
category = "dev"
optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*"
+python-versions = ">=3.5"
[[package]]
name = "docutils"
-version = "0.17"
+version = "0.18.1"
description = "Docutils -- Python Documentation Utilities"
category = "dev"
optional = false
@@ -162,63 +172,65 @@ setuptools_scm = "*"
[[package]]
name = "flake8"
-version = "3.8.4"
+version = "3.9.2"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[package.dependencies]
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
mccabe = ">=0.6.0,<0.7.0"
-pycodestyle = ">=2.6.0a1,<2.7.0"
-pyflakes = ">=2.2.0,<2.3.0"
+pycodestyle = ">=2.7.0,<2.8.0"
+pyflakes = ">=2.3.0,<2.4.0"
[[package]]
name = "gitdb"
-version = "4.0.7"
+version = "4.0.9"
description = "Git Object Database"
category = "dev"
optional = false
-python-versions = ">=3.4"
+python-versions = ">=3.6"
[package.dependencies]
-smmap = ">=3.0.1,<5"
+smmap = ">=3.0.1,<6"
[[package]]
name = "gitpython"
-version = "3.1.14"
-description = "Python Git Library"
+version = "3.1.27"
+description = "GitPython is a python library used to interact with Git repositories"
category = "dev"
optional = false
-python-versions = ">=3.4"
+python-versions = ">=3.7"
[package.dependencies]
gitdb = ">=4.0.1,<5"
+typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""}
[[package]]
name = "idna"
-version = "2.10"
+version = "3.3"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
-version = "3.4.0"
+version = "4.11.3"
description = "Read metadata from Python packages"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[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)"]
-testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
+docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
+perf = ["ipython"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
[[package]]
name = "iniconfig"
@@ -230,7 +242,7 @@ python-versions = "*"
[[package]]
name = "invoke"
-version = "1.5.0"
+version = "1.7.0"
description = "Pythonic task execution"
category = "dev"
optional = false
@@ -238,37 +250,40 @@ python-versions = "*"
[[package]]
name = "ipdb"
-version = "0.13.4"
+version = "0.13.9"
description = "IPython-enabled pdb"
category = "dev"
optional = false
python-versions = ">=2.7"
[package.dependencies]
-ipython = {version = ">=5.1.0", markers = "python_version >= \"3.4\""}
+decorator = {version = "*", markers = "python_version > \"3.6\""}
+ipython = {version = ">=7.17.0", markers = "python_version > \"3.6\""}
+toml = {version = ">=0.10.2", markers = "python_version > \"3.6\""}
[[package]]
name = "ipython"
-version = "7.16.1"
+version = "7.33.0"
description = "IPython: Productive Interactive Computing"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
appnope = {version = "*", markers = "sys_platform == \"darwin\""}
backcall = "*"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
decorator = "*"
-jedi = ">=0.10"
-pexpect = {version = "*", markers = "sys_platform != \"win32\""}
+jedi = ">=0.16"
+matplotlib-inline = "*"
+pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
pickleshare = "*"
prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
pygments = "*"
traitlets = ">=4.2"
[package.extras]
-all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"]
+all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
doc = ["Sphinx (>=1.3)"]
kernel = ["ipykernel"]
nbconvert = ["nbconvert"]
@@ -276,19 +291,11 @@ nbformat = ["nbformat"]
notebook = ["notebook", "ipywidgets"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
-test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"]
-
-[[package]]
-name = "ipython-genutils"
-version = "0.2.0"
-description = "Vestigial utilities from IPython"
-category = "dev"
-optional = false
-python-versions = "*"
+test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
[[package]]
name = "jedi"
-version = "0.18.0"
+version = "0.18.1"
description = "An autocompletion tool for Python that can be used for text editors."
category = "dev"
optional = false
@@ -299,36 +306,48 @@ parso = ">=0.8.0,<0.9.0"
[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"]
+testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]]
name = "jeepney"
-version = "0.6.0"
+version = "0.8.0"
description = "Low-level, pure Python DBus protocol wrapper."
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.extras]
-test = ["pytest", "pytest-trio", "pytest-asyncio", "testpath", "trio"]
+test = ["pytest", "pytest-trio", "pytest-asyncio (>=0.17)", "testpath", "trio", "async-timeout"]
+trio = ["trio", "async-generator"]
[[package]]
name = "keyring"
-version = "22.3.0"
+version = "23.5.0"
description = "Store and access your passwords safely."
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
-importlib-metadata = {version = ">=1", markers = "python_version < \"3.8\""}
+importlib-metadata = ">=3.6"
jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""}
pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""}
SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""}
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
-testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "pytest-black (>=0.3.7)", "pytest-mypy"]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"]
+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 = "matplotlib-inline"
+version = "0.1.3"
+description = "Inline Matplotlib backend for Jupyter"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+traitlets = "*"
[[package]]
name = "mccabe"
@@ -348,18 +367,18 @@ python-versions = ">=3.5"
[[package]]
name = "packaging"
-version = "20.9"
+version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=3.6"
[package.dependencies]
-pyparsing = ">=2.0.2"
+pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "parso"
-version = "0.8.1"
+version = "0.8.3"
description = "A Python Parser"
category = "dev"
optional = false
@@ -390,43 +409,44 @@ python-versions = "*"
[[package]]
name = "pkginfo"
-version = "1.7.0"
+version = "1.8.2"
description = "Query metadatdata from sdists / bdists / installed packages."
category = "dev"
optional = false
python-versions = "*"
[package.extras]
-testing = ["nose", "coverage"]
+testing = ["coverage", "nose"]
[[package]]
name = "pluggy"
-version = "0.13.1"
+version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+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 = "prompt-toolkit"
-version = "3.0.3"
+version = "3.0.29"
description = "Library for building powerful interactive command lines in Python"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.6.2"
[package.dependencies]
wcwidth = "*"
[[package]]
name = "psutil"
-version = "5.8.0"
+version = "5.9.0"
description = "Cross-platform lib for process and system monitoring in Python."
category = "dev"
optional = false
@@ -445,15 +465,15 @@ python-versions = "*"
[[package]]
name = "py"
-version = "1.10.0"
+version = "1.11.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.*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pycodestyle"
-version = "2.6.0"
+version = "2.7.0"
description = "Python style guide checker"
category = "dev"
optional = false
@@ -461,7 +481,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pycparser"
-version = "2.20"
+version = "2.21"
description = "C parser in Python"
category = "dev"
optional = false
@@ -469,7 +489,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyflakes"
-version = "2.2.0"
+version = "2.3.1"
description = "passive checker of Python programs"
category = "dev"
optional = false
@@ -477,23 +497,26 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pygments"
-version = "2.7.4"
+version = "2.12.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
[[package]]
name = "pyparsing"
-version = "2.4.7"
-description = "Python parsing module"
+version = "3.0.8"
+description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "dev"
optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+python-versions = ">=3.6.8"
+
+[package.extras]
+diagrams = ["railroad-diagrams", "jinja2"]
[[package]]
name = "pytest"
-version = "6.2.2"
+version = "6.2.5"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
@@ -506,7 +529,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
iniconfig = "*"
packaging = "*"
-pluggy = ">=0.12,<1.0.0a1"
+pluggy = ">=0.12,<2.0"
py = ">=1.8.2"
toml = "*"
@@ -515,7 +538,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm
[[package]]
name = "pytest-cov"
-version = "2.11.1"
+version = "2.12.1"
description = "Pytest plugin for measuring coverage."
category = "dev"
optional = false
@@ -524,50 +547,52 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies]
coverage = ">=5.2.1"
pytest = ">=4.6"
+toml = "*"
[package.extras]
-testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"]
+testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
[[package]]
name = "python-gitlab"
-version = "2.6.0"
+version = "3.4.0"
description = "Interact with GitLab API"
category = "dev"
optional = false
-python-versions = ">=3.6.0"
+python-versions = ">=3.7.0"
[package.dependencies]
-requests = ">=2.22.0"
+requests = ">=2.25.0"
requests-toolbelt = ">=0.9.1"
[package.extras]
-autocompletion = ["argcomplete (>=1.10.0,<2)"]
+autocompletion = ["argcomplete (>=1.10.0,<3)"]
yaml = ["PyYaml (>=5.2)"]
[[package]]
name = "python-semantic-release"
-version = "7.15.3"
+version = "7.28.1"
description = "Automatic Semantic Versioning for Python projects"
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
-click = ">=7,<8"
+click = ">=7,<9"
click-log = ">=0.3,<1"
dotty-dict = ">=1.3.0,<2"
gitpython = ">=3.0.8,<4"
invoke = ">=1.4.1,<2"
-python-gitlab = ">=1.10,<3"
+python-gitlab = ">=2,<4"
requests = ">=2.25,<3"
semver = ">=2.10,<3"
-tomlkit = ">=0.7.0,<1.0.0"
+tomlkit = ">=0.10.0,<0.11.0"
twine = ">=3,<4"
[package.extras]
-dev = ["mypy", "tox", "isort", "black"]
+dev = ["tox", "isort", "black"]
docs = ["Sphinx (==1.3.6)"]
-test = ["coverage (>=5,<6)", "pytest (>=5,<6)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.5.0)", "mock (==1.3.0)"]
+mypy = ["mypy", "types-requests"]
+test = ["coverage (>=5,<6)", "pytest (>=5,<6)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.13.3)", "mock (==1.3.0)"]
[[package]]
name = "pywin32-ctypes"
@@ -579,38 +604,37 @@ python-versions = "*"
[[package]]
name = "readme-renderer"
-version = "29.0"
+version = "35.0"
description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
[package.dependencies]
bleach = ">=2.1.0"
docutils = ">=0.13.1"
Pygments = ">=2.5.1"
-six = "*"
[package.extras]
-md = ["cmarkgfm (>=0.5.0,<0.6.0)"]
+md = ["cmarkgfm (>=0.8.0)"]
[[package]]
name = "requests"
-version = "2.25.1"
+version = "2.27.1"
description = "Python HTTP for Humans."
category = "main"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[package.dependencies]
certifi = ">=2017.4.17"
-chardet = ">=3.0.2,<5"
-idna = ">=2.5,<3"
+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]
-security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
[[package]]
name = "requests-toolbelt"
@@ -623,20 +647,35 @@ python-versions = "*"
[package.dependencies]
requests = ">=2.0.1,<3.0.0"
+[[package]]
+name = "responses"
+version = "0.20.0"
+description = "A utility library for mocking out the `requests` Python library."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+requests = ">=2.0,<3.0"
+urllib3 = ">=1.25.10"
+
+[package.extras]
+tests = ["pytest (>=7.0.0)", "coverage (>=6.0.0)", "pytest-cov", "pytest-asyncio", "pytest-localserver", "flake8", "types-mock", "types-requests", "mypy"]
+
[[package]]
name = "rfc3986"
-version = "1.4.0"
+version = "2.0.0"
description = "Validating URI References per RFC 3986"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
[package.extras]
idna2008 = ["idna"]
[[package]]
name = "secretstorage"
-version = "3.3.1"
+version = "3.3.2"
description = "Python bindings to FreeDesktop.org Secret Service API"
category = "dev"
optional = false
@@ -656,18 +695,23 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "setuptools-scm"
-version = "6.0.1"
+version = "6.4.2"
description = "the blessed package to manage your versions by scm tags"
category = "dev"
optional = false
python-versions = ">=3.6"
+[package.dependencies]
+packaging = ">=20.0"
+tomli = ">=1.0.0"
+
[package.extras]
-toml = ["toml"]
+test = ["pytest (>=6.2)", "virtualenv (>20)"]
+toml = ["setuptools (>=42)"]
[[package]]
name = "six"
-version = "1.15.0"
+version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "dev"
optional = false
@@ -675,15 +719,15 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "smmap"
-version = "4.0.0"
+version = "5.0.0"
description = "A pure Python implementation of a sliding window memory map manager"
category = "dev"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
[[package]]
name = "tap.py"
-version = "3.0"
+version = "3.1"
description = "Test Anything Protocol (TAP) tools"
category = "dev"
optional = false
@@ -694,16 +738,17 @@ yaml = ["more-itertools", "PyYAML (>=5.1)"]
[[package]]
name = "taskipy"
-version = "1.6.0"
+version = "1.10.1"
description = "tasks runner for python projects"
category = "dev"
optional = false
python-versions = ">=3.6,<4.0"
[package.dependencies]
-mslex = ">=0.3.0,<0.4.0"
+colorama = ">=0.4.4,<0.5.0"
+mslex = {version = ">=0.3.0,<0.4.0", markers = "sys_platform == \"win32\""}
psutil = ">=5.7.2,<6.0.0"
-toml = ">=0.10.0,<0.11.0"
+tomli = ">=1.2.3,<2.0.0"
[[package]]
name = "toml"
@@ -713,46 +758,53 @@ category = "dev"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+[[package]]
+name = "tomli"
+version = "1.2.3"
+description = "A lil' TOML parser"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
[[package]]
name = "tomlkit"
-version = "0.7.0"
+version = "0.10.2"
description = "Style preserving TOML library"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.6,<4.0"
[[package]]
name = "tqdm"
-version = "4.59.0"
+version = "4.64.0"
description = "Fast, Extensible Progress Meter"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
[package.extras]
dev = ["py-make (>=0.1.0)", "twine", "wheel"]
notebook = ["ipywidgets (>=6)"]
+slack = ["slack-sdk"]
telegram = ["requests"]
[[package]]
name = "traitlets"
-version = "4.3.3"
-description = "Traitlets Python config system"
+version = "5.1.1"
+description = "Traitlets Python configuration system"
category = "dev"
optional = false
-python-versions = "*"
-
-[package.dependencies]
-decorator = "*"
-ipython-genutils = "*"
-six = "*"
+python-versions = ">=3.7"
[package.extras]
-test = ["pytest", "mock"]
+test = ["pytest"]
[[package]]
name = "twine"
-version = "3.3.0"
+version = "3.8.0"
description = "Collection of utilities for publishing packages on PyPI"
category = "dev"
optional = false
@@ -760,33 +812,34 @@ python-versions = ">=3.6"
[package.dependencies]
colorama = ">=0.4.3"
-importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
+importlib-metadata = ">=3.6"
keyring = ">=15.1"
-pkginfo = ">=1.4.2"
+pkginfo = ">=1.8.1"
readme-renderer = ">=21.0"
requests = ">=2.20"
requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0"
rfc3986 = ">=1.4.0"
tqdm = ">=4.14"
+urllib3 = ">=1.26.0"
[[package]]
name = "typing-extensions"
-version = "3.7.4.3"
-description = "Backported and Experimental Type Hints for Python 3.5+"
+version = "4.2.0"
+description = "Backported and Experimental Type Hints for Python 3.7+"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
[[package]]
name = "urllib3"
-version = "1.26.3"
+version = "1.26.9"
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)"]
+brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "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)"]
@@ -816,228 +869,253 @@ python-versions = "*"
[[package]]
name = "zipp"
-version = "3.4.0"
+version = "3.8.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
-testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=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 (>=0.9.1)"]
[metadata]
lock-version = "1.1"
-python-versions = "^3.6"
-content-hash = "e32b90a7d407970b1a0211de99d4adda266a46f7e222c8d3bb4dd57dbbec17ca"
+python-versions = "^3.7"
+content-hash = "95a17f1fcf4d2a76cbd338051e679f32f1327891061b7e486b0f5f8ebb759532"
[metadata.files]
appnope = [
- {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
- {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
+ {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
+ {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
]
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-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
- {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
+ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
+ {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
backcall = [
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
bleach = [
- {file = "bleach-3.3.0-py2.py3-none-any.whl", hash = "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125"},
- {file = "bleach-3.3.0.tar.gz", hash = "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"},
+ {file = "bleach-5.0.0-py3-none-any.whl", hash = "sha256:08a1fe86d253b5c88c92cc3d810fd8048a16d15762e1e5b74d502256e5926aa1"},
+ {file = "bleach-5.0.0.tar.gz", hash = "sha256:c6d6cc054bdc9c83b48b8083e236e5f00f238428666d2ce2e083eaa5fd568565"},
]
certifi = [
- {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
- {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
+ {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.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"},
- {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"},
- {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"},
- {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"},
- {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"},
- {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"},
- {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"},
- {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"},
- {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"},
- {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"},
- {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"},
- {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"},
- {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"},
- {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"},
- {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"},
- {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"},
- {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"},
- {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"},
- {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"},
- {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"},
- {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"},
- {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"},
- {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"},
- {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"},
- {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"},
- {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"},
- {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"},
- {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"},
- {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"},
- {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"},
- {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"},
- {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"},
- {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"},
- {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"},
- {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"},
- {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"},
- {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"},
-]
-chardet = [
- {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
- {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
+ {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.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
+ {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
]
click = [
- {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
- {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
+ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
+ {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
click-log = [
- {file = "click-log-0.3.2.tar.gz", hash = "sha256:16fd1ca3fc6b16c98cea63acf1ab474ea8e676849dc669d86afafb0ed7003124"},
- {file = "click_log-0.3.2-py2.py3-none-any.whl", hash = "sha256:eee14dc37cdf3072158570f00406572f9e03e414accdccfccd4c538df9ae322c"},
+ {file = "click-log-0.4.0.tar.gz", hash = "sha256:3970f8570ac54491237bcdb3d8ab5e3eef6c057df29f8c3d1151a51a9c23b975"},
+ {file = "click_log-0.4.0-py2.py3-none-any.whl", hash = "sha256:a43e394b528d52112af599f2fc9e4b7cf3c15f94e53581f74fa6867e68c91756"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
coverage = [
- {file = "coverage-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135"},
- {file = "coverage-5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c"},
- {file = "coverage-5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44"},
- {file = "coverage-5.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3"},
- {file = "coverage-5.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9"},
- {file = "coverage-5.4-cp27-cp27m-win32.whl", hash = "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1"},
- {file = "coverage-5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370"},
- {file = "coverage-5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0"},
- {file = "coverage-5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8"},
- {file = "coverage-5.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19"},
- {file = "coverage-5.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247"},
- {file = "coverage-5.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339"},
- {file = "coverage-5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337"},
- {file = "coverage-5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3"},
- {file = "coverage-5.4-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4"},
- {file = "coverage-5.4-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c"},
- {file = "coverage-5.4-cp35-cp35m-win32.whl", hash = "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f"},
- {file = "coverage-5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66"},
- {file = "coverage-5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d"},
- {file = "coverage-5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b"},
- {file = "coverage-5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9"},
- {file = "coverage-5.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af"},
- {file = "coverage-5.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5"},
- {file = "coverage-5.4-cp36-cp36m-win32.whl", hash = "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec"},
- {file = "coverage-5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9"},
- {file = "coverage-5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90"},
- {file = "coverage-5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc"},
- {file = "coverage-5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37"},
- {file = "coverage-5.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409"},
- {file = "coverage-5.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb"},
- {file = "coverage-5.4-cp37-cp37m-win32.whl", hash = "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a"},
- {file = "coverage-5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22"},
- {file = "coverage-5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f"},
- {file = "coverage-5.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3"},
- {file = "coverage-5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"},
- {file = "coverage-5.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c"},
- {file = "coverage-5.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994"},
- {file = "coverage-5.4-cp38-cp38-win32.whl", hash = "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39"},
- {file = "coverage-5.4-cp38-cp38-win_amd64.whl", hash = "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7"},
- {file = "coverage-5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c"},
- {file = "coverage-5.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3"},
- {file = "coverage-5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde"},
- {file = "coverage-5.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f"},
- {file = "coverage-5.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f"},
- {file = "coverage-5.4-cp39-cp39-win32.whl", hash = "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880"},
- {file = "coverage-5.4-cp39-cp39-win_amd64.whl", hash = "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345"},
- {file = "coverage-5.4-pp36-none-any.whl", hash = "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f"},
- {file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"},
- {file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"},
+ {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
+ {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
+ {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
+ {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
+ {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
+ {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
+ {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
+ {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
+ {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
+ {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
+ {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
+ {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
+ {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
+ {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
+ {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
+ {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
+ {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
+ {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
+ {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
+ {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
+ {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
+ {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
+ {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
+ {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
+ {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
+ {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
+ {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
+ {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
+ {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
+ {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
+ {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
+ {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
+ {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
+ {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
+ {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
+ {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
+ {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
+ {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
+ {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
+ {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
+ {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
+ {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
+ {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
+ {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
+ {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
+ {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
+ {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
+ {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
+ {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
+ {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
+ {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
+ {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
]
cryptography = [
- {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"},
- {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"},
- {file = "cryptography-3.4.7-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2"},
- {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6"},
- {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959"},
- {file = "cryptography-3.4.7-cp36-abi3-win32.whl", hash = "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d"},
- {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"},
- {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"},
- {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"},
- {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"},
- {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"},
- {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"},
+ {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"},
+ {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3c81599befb4d4f3d7648ed3217e00d21a9341a9a688ecdd615ff72ffbed7336"},
+ {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bd1096476aaac820426239ab534b636c77d71af66c547b9ddcd76eb9c79e004"},
+ {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:31fe38d14d2e5f787e0aecef831457da6cec68e0bb09a35835b0b44ae8b988fe"},
+ {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093cb351031656d3ee2f4fa1be579a8c69c754cf874206be1d4cf3b542042804"},
+ {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b281eab51e1b6b6afa525af2bd93c16d49358404f814fe2c2410058623928c"},
+ {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:0cc20f655157d4cfc7bada909dc5cc228211b075ba8407c46467f63597c78178"},
+ {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f8ec91983e638a9bcd75b39f1396e5c0dc2330cbd9ce4accefe68717e6779e0a"},
+ {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:46f4c544f6557a2fefa7ac8ac7d1b17bf9b647bd20b16decc8fbcab7117fbc15"},
+ {file = "cryptography-37.0.2-cp36-abi3-win32.whl", hash = "sha256:731c8abd27693323b348518ed0e0705713a36d79fdbd969ad968fbef0979a7e0"},
+ {file = "cryptography-37.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:471e0d70201c069f74c837983189949aa0d24bb2d751b57e26e3761f2f782b8d"},
+ {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a68254dd88021f24a68b613d8c51d5c5e74d735878b9e32cc0adf19d1f10aaf9"},
+ {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:a7d5137e556cc0ea418dca6186deabe9129cee318618eb1ffecbd35bee55ddc1"},
+ {file = "cryptography-37.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aeaba7b5e756ea52c8861c133c596afe93dd716cbcacae23b80bc238202dc023"},
+ {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e590dd70642eb2079d280420a888190aa040ad20f19ec8c6e097e38aa29e06"},
+ {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1b9362d34363f2c71b7853f6251219298124aa4cc2075ae2932e64c91a3e2717"},
+ {file = "cryptography-37.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e53258e69874a306fcecb88b7534d61820db8a98655662a3dd2ec7f1afd9132f"},
+ {file = "cryptography-37.0.2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:1f3bfbd611db5cb58ca82f3deb35e83af34bb8cf06043fa61500157d50a70982"},
+ {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:419c57d7b63f5ec38b1199a9521d77d7d1754eb97827bbb773162073ccd8c8d4"},
+ {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:dc26bb134452081859aa21d4990474ddb7e863aa39e60d1592800a8865a702de"},
+ {file = "cryptography-37.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b8398b3d0efc420e777c40c16764d6870bcef2eb383df9c6dbb9ffe12c64452"},
+ {file = "cryptography-37.0.2.tar.gz", hash = "sha256:f224ad253cc9cea7568f49077007d2263efa57396a2f2f78114066fd54b5c68e"},
]
decorator = [
- {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
- {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
+ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
+ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]
docutils = [
- {file = "docutils-0.17-py2.py3-none-any.whl", hash = "sha256:a71042bb7207c03d5647f280427f14bfbd1a65c9eb84f4b341d85fafb6bb4bdf"},
- {file = "docutils-0.17.tar.gz", hash = "sha256:e2ffeea817964356ba4470efba7c2f42b6b0de0b04e66378507e3e2504bbff4c"},
+ {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"},
+ {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"},
]
dotty-dict = [
{file = "dotty_dict-1.3.0.tar.gz", hash = "sha256:eb0035a3629ecd84397a68f1f42f1e94abd1c34577a19cd3eacad331ee7cbaf0"},
]
flake8 = [
- {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"},
- {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"},
+ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
+ {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
]
gitdb = [
- {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"},
- {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"},
+ {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.14-py3-none-any.whl", hash = "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b"},
- {file = "GitPython-3.1.14.tar.gz", hash = "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"},
+ {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"},
+ {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"},
]
idna = [
- {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
- {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
+ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
+ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
]
importlib-metadata = [
- {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"},
- {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"},
+ {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"},
+ {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
invoke = [
- {file = "invoke-1.5.0-py2-none-any.whl", hash = "sha256:da7c2d0be71be83ffd6337e078ef9643f41240024d6b2659e7b46e0b251e339f"},
- {file = "invoke-1.5.0-py3-none-any.whl", hash = "sha256:7e44d98a7dc00c91c79bac9e3007276965d2c96884b3c22077a9f04042bd6d90"},
- {file = "invoke-1.5.0.tar.gz", hash = "sha256:f0c560075b5fb29ba14dad44a7185514e94970d1b9d57dcd3723bec5fed92650"},
+ {file = "invoke-1.7.0-py3-none-any.whl", hash = "sha256:a5159fc63dba6ca2a87a1e33d282b99cea69711b03c64a35bb4e1c53c6c4afa0"},
+ {file = "invoke-1.7.0.tar.gz", hash = "sha256:e332e49de40463f2016315f51df42313855772be86435686156bc18f45b5cc6c"},
]
ipdb = [
- {file = "ipdb-0.13.4.tar.gz", hash = "sha256:c85398b5fb82f82399fc38c44fe3532c0dde1754abee727d8f5cfcc74547b334"},
+ {file = "ipdb-0.13.9.tar.gz", hash = "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5"},
]
ipython = [
- {file = "ipython-7.16.1-py3-none-any.whl", hash = "sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64"},
- {file = "ipython-7.16.1.tar.gz", hash = "sha256:9f4fcb31d3b2c533333893b9172264e4821c1ac91839500f31bd43f2c59b3ccf"},
-]
-ipython-genutils = [
- {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
- {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
+ {file = "ipython-7.33.0-py3-none-any.whl", hash = "sha256:916a3126896e4fd78dd4d9cf3e21586e7fd93bae3f1cd751588b75524b64bf94"},
+ {file = "ipython-7.33.0.tar.gz", hash = "sha256:bcffb865a83b081620301ba0ec4d95084454f26b91d6d66b475bff3dfb0218d4"},
]
jedi = [
- {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"},
- {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"},
+ {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
+ {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
]
jeepney = [
- {file = "jeepney-0.6.0-py3-none-any.whl", hash = "sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae"},
- {file = "jeepney-0.6.0.tar.gz", hash = "sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657"},
+ {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"},
+ {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"},
]
keyring = [
- {file = "keyring-22.3.0-py3-none-any.whl", hash = "sha256:2bc8363ebdd63886126a012057a85c8cb6e143877afa02619ac7dbc9f38a207b"},
- {file = "keyring-22.3.0.tar.gz", hash = "sha256:16927a444b2c73f983520a48dec79ddab49fe76429ea05b8d528d778c8339522"},
+ {file = "keyring-23.5.0-py3-none-any.whl", hash = "sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261"},
+ {file = "keyring-23.5.0.tar.gz", hash = "sha256:9012508e141a80bd1c0b6778d5c610dd9f8c464d75ac6774248500503f972fb9"},
+]
+matplotlib-inline = [
+ {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
+ {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
@@ -1048,12 +1126,12 @@ mslex = [
{file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"},
]
packaging = [
- {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
- {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
+ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
+ {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
parso = [
- {file = "parso-0.8.1-py2.py3-none-any.whl", hash = "sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410"},
- {file = "parso-0.8.1.tar.gz", hash = "sha256:8519430ad07087d4c997fda3a7918f7cfa27cb58972a8c89c2a0295a1c940e9e"},
+ {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
+ {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
]
pexpect = [
{file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
@@ -1064,167 +1142,178 @@ pickleshare = [
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
]
pkginfo = [
- {file = "pkginfo-1.7.0-py2.py3-none-any.whl", hash = "sha256:9fdbea6495622e022cc72c2e5e1b735218e4ffb2a2a69cde2694a6c1f16afb75"},
- {file = "pkginfo-1.7.0.tar.gz", hash = "sha256:029a70cb45c6171c329dfc890cde0879f8c52d6f3922794796e06f577bb03db4"},
+ {file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"},
+ {file = "pkginfo-1.8.2.tar.gz", hash = "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff"},
]
pluggy = [
- {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
- {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
+ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
+ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
prompt-toolkit = [
- {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"},
- {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"},
+ {file = "prompt_toolkit-3.0.29-py3-none-any.whl", hash = "sha256:62291dad495e665fca0bda814e342c69952086afb0f4094d0893d357e5c78752"},
+ {file = "prompt_toolkit-3.0.29.tar.gz", hash = "sha256:bd640f60e8cecd74f0dc249713d433ace2ddc62b65ee07f96d358e0b152b6ea7"},
]
psutil = [
- {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"},
- {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"},
- {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"},
- {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"},
- {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"},
- {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"},
- {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"},
- {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"},
- {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"},
- {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"},
- {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"},
- {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"},
- {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"},
- {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"},
- {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"},
- {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"},
- {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"},
- {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"},
- {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"},
- {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"},
- {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"},
- {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"},
- {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"},
- {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"},
- {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"},
- {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"},
- {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"},
- {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
+ {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"},
+ {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"},
+ {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"},
+ {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"},
+ {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"},
+ {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"},
+ {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"},
+ {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"},
+ {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"},
+ {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"},
+ {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"},
+ {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"},
+ {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"},
+ {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"},
+ {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"},
+ {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"},
+ {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"},
+ {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"},
+ {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"},
+ {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"},
+ {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"},
+ {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"},
+ {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"},
+ {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"},
+ {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"},
+ {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"},
+ {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"},
+ {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"},
+ {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"},
+ {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"},
+ {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"},
+ {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"},
]
ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
{file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
]
py = [
- {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
- {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
+ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
+ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pycodestyle = [
- {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"},
- {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"},
+ {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
+ {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
]
pycparser = [
- {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
- {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
pyflakes = [
- {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
- {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
+ {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
+ {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
]
pygments = [
- {file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"},
- {file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"},
+ {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
+ {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
]
pyparsing = [
- {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
- {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
+ {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"},
+ {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"},
]
pytest = [
- {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
- {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
+ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
+ {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
]
pytest-cov = [
- {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
- {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
+ {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"},
+ {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
]
python-gitlab = [
- {file = "python-gitlab-2.6.0.tar.gz", hash = "sha256:a862c6874524ab585b725a17b2cd2950fc09d6d74205f40a11be2a4e8f2dcaa1"},
- {file = "python_gitlab-2.6.0-py3-none-any.whl", hash = "sha256:8f6a62b2804021416f13e91f359085f1071fb7adedfb292ef0d0877df777a075"},
+ {file = "python-gitlab-3.4.0.tar.gz", hash = "sha256:6180b81ee2f265ad8d8412956a1740b4d3ceca7b28ae2f707dfe62375fed0082"},
+ {file = "python_gitlab-3.4.0-py3-none-any.whl", hash = "sha256:251b63f0589d51f854516948c84e9eb8df26e1e9dea595cf86b43f17c43007dd"},
]
python-semantic-release = [
- {file = "python-semantic-release-7.15.3.tar.gz", hash = "sha256:0ffcb267923731b3578d4377de67c59c5779c80e514ef9883c47c8877c059d74"},
- {file = "python_semantic_release-7.15.3-py3-none-any.whl", hash = "sha256:9b5c6e7ff9887b0cc8178004aa101aae2bfec8a464316cf287be8acbdf1e4954"},
+ {file = "python-semantic-release-7.28.1.tar.gz", hash = "sha256:d7f82b3d4c06b304d07689b8a1c7d0d448ff07c2ab81cd810c4f2f900f24c599"},
+ {file = "python_semantic_release-7.28.1-py3-none-any.whl", hash = "sha256:319c3e811d6e10bc3f0e967419eae0e5e9ba1e6745aa2fd94dd24e3995541ca2"},
]
pywin32-ctypes = [
{file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
{file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
]
readme-renderer = [
- {file = "readme_renderer-29.0-py2.py3-none-any.whl", hash = "sha256:63b4075c6698fcfa78e584930f07f39e05d46f3ec97f65006e430b595ca6348c"},
- {file = "readme_renderer-29.0.tar.gz", hash = "sha256:92fd5ac2bf8677f310f3303aa4bce5b9d5f9f2094ab98c29f13791d7b805a3db"},
+ {file = "readme_renderer-35.0-py3-none-any.whl", hash = "sha256:73b84905d091c31f36e50b4ae05ae2acead661f6a09a9abb4df7d2ddcdb6a698"},
+ {file = "readme_renderer-35.0.tar.gz", hash = "sha256:a727999acfc222fc21d82a12ed48c957c4989785e5865807c65a487d21677497"},
]
requests = [
- {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
- {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
+ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
+ {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
]
requests-toolbelt = [
{file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"},
{file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"},
]
+responses = [
+ {file = "responses-0.20.0-py3-none-any.whl", hash = "sha256:18831bc2d72443b67664d98038374a6fa1f27eaaff4dd9a7d7613723416fea3c"},
+ {file = "responses-0.20.0.tar.gz", hash = "sha256:644905bc4fb8a18fa37e3882b2ac05e610fe8c2f967d327eed669e314d94a541"},
+]
rfc3986 = [
- {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
- {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"},
+ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"},
+ {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"},
]
secretstorage = [
- {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"},
- {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"},
+ {file = "SecretStorage-3.3.2-py3-none-any.whl", hash = "sha256:755dc845b6ad76dcbcbc07ea3da75ae54bb1ea529eb72d15f83d26499a5df319"},
+ {file = "SecretStorage-3.3.2.tar.gz", hash = "sha256:0a8eb9645b320881c222e827c26f4cfcf55363e8b374a021981ef886657a912f"},
]
semver = [
{file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"},
{file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"},
]
setuptools-scm = [
- {file = "setuptools_scm-6.0.1-py3-none-any.whl", hash = "sha256:c3bd5f701c8def44a5c0bfe8d407bef3f80342217ef3492b951f3777bd2d915c"},
- {file = "setuptools_scm-6.0.1.tar.gz", hash = "sha256:d1925a69cb07e9b29416a275b9fadb009a23c148ace905b2fb220649a6c18e92"},
+ {file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"},
+ {file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"},
]
six = [
- {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
- {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
+ {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-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"},
- {file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"},
+ {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
+ {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
]
"tap.py" = [
- {file = "tap.py-3.0-py2.py3-none-any.whl", hash = "sha256:a598bfaa2e224d71f2e86147c2ef822c18ff2e1b8ef006397e5056b08f92f699"},
- {file = "tap.py-3.0.tar.gz", hash = "sha256:f5eeeeebfd64e53d32661752bb4c288589a3babbb96db3f391a4ec29f1359c70"},
+ {file = "tap.py-3.1-py3-none-any.whl", hash = "sha256:928c852f3361707b796c93730cc5402c6378660b161114461066acf53d65bf5d"},
+ {file = "tap.py-3.1.tar.gz", hash = "sha256:3c0cd45212ad5a25b35445964e2517efa000a118a1bfc3437dae828892eaf1e1"},
]
taskipy = [
- {file = "taskipy-1.6.0-py3-none-any.whl", hash = "sha256:33ee1d52b378cb4af3678fc459b75c3028f594c5e8e42ac0696cbe3e95d47394"},
- {file = "taskipy-1.6.0.tar.gz", hash = "sha256:ec4d1f2208ae24218950e3a2812e4e8b4397b1f65a6ad7e2b1240b702042fa3e"},
+ {file = "taskipy-1.10.1-py3-none-any.whl", hash = "sha256:9b38333654da487b6d16de6fa330b7629d1935d1e74819ba4c5f17a1c372d37b"},
+ {file = "taskipy-1.10.1.tar.gz", hash = "sha256:6fa0b11c43d103e376063e90be31d87b435aad50fb7dc1c9a2de9b60a85015ed"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
+tomli = [
+ {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"},
+ {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"},
+]
tomlkit = [
- {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"},
- {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"},
+ {file = "tomlkit-0.10.2-py3-none-any.whl", hash = "sha256:905cf92c2111ef80d355708f47ac24ad1b6fc2adc5107455940088c9bbecaedb"},
+ {file = "tomlkit-0.10.2.tar.gz", hash = "sha256:30d54c0b914e595f3d10a87888599eab5321a2a69abc773bbefff51599b72db6"},
]
tqdm = [
- {file = "tqdm-4.59.0-py2.py3-none-any.whl", hash = "sha256:9fdf349068d047d4cfbe24862c425883af1db29bcddf4b0eeb2524f6fbdb23c7"},
- {file = "tqdm-4.59.0.tar.gz", hash = "sha256:d666ae29164da3e517fcf125e41d4fe96e5bb375cd87ff9763f6b38b5592fe33"},
+ {file = "tqdm-4.64.0-py2.py3-none-any.whl", hash = "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"},
+ {file = "tqdm-4.64.0.tar.gz", hash = "sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d"},
]
traitlets = [
- {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"},
- {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
+ {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"},
+ {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"},
]
twine = [
- {file = "twine-3.3.0-py3-none-any.whl", hash = "sha256:2f6942ec2a17417e19d2dd372fc4faa424c87ee9ce49b4e20c427eb00a0f3f41"},
- {file = "twine-3.3.0.tar.gz", hash = "sha256:fcffa8fc37e8083a5be0728371f299598870ee1eccc94e9a25cef7b1dcfa8297"},
+ {file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"},
+ {file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"},
]
typing-extensions = [
- {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
- {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
- {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
+ {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
+ {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
]
urllib3 = [
- {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"},
- {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"},
+ {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"},
+ {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
]
wcwidth = [
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
@@ -1239,6 +1328,6 @@ yapf = [
{file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"},
]
zipp = [
- {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"},
- {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"},
+ {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
+ {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"},
]
diff --git a/pyproject.toml b/pyproject.toml
index 02ba191..0930cda 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,7 @@ commit_subject = "release: Version {version} [skip ci]"
commit_author = "LogDNA Bot "
[tool.poetry.dependencies]
-python = "^3.6"
+python = "^3.7"
requests = "^2.25.1"
[tool.poetry.dev-dependencies]
@@ -25,11 +25,11 @@ yapf = "^0.30.0"
pytest = "^6.2.2"
pytest-cov = "^2.11.1"
taskipy = "^1.6.0"
-python-semantic-release = "^7.15.3"
+python-semantic-release = "^7.28.1"
[tool.taskipy.tasks]
pre_test = "mkdir -p coverage"
-test = "pytest --junitxml=coverage/test.xml --cov=logdna --cov-report=html --verbose"
+test = "pytest --junitxml=coverage/test.xml --cov=logdna --cov-report=html --verbose tests/"
post_test = "python scripts/json_coverage.py"
lint = "flake8 --doctests"
"lint:fix" = "yapf -r -i logdna scripts tests"
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 91e45e0..545ec0b 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -2,15 +2,17 @@
import unittest
import requests
import time
+import os
from logdna import LogDNAHandler
from concurrent.futures import ThreadPoolExecutor
from logdna.configs import defaults
from unittest import mock
+from unittest.mock import patch
now = int(time.time())
expectedLines = []
-LOGDNA_API_KEY = '< YOUR INGESTION KEY HERE >'
+LOGDNA_API_KEY = os.environ.get('LOGDNA_INGESTION_KEY')
logger = logging.getLogger('logdna')
logger.setLevel(logging.INFO)
sample_args = {
@@ -19,6 +21,7 @@
'hostname': 'differentHost',
'env': 'differentEnv'
}
+
sample_record = logging.LogRecord('test', logging.INFO, 'test', 5,
'Something to test', [sample_args], '', '',
'')
@@ -35,13 +38,15 @@
'lineno': 5
}
}
+
sample_options = {
'hostname': 'localhost',
'ip': '10.0.1.1',
'mac': 'C0:FF:EE:C0:FF:EE',
'tags': 'sample,test',
'index_meta': True,
- 'now': int(time.time() * 1000)
+ 'now': int(time.time() * 1000),
+ 'retry_interval_secs': 0.5
}
@@ -66,7 +71,8 @@ def shutdown(self, wait=True):
class LogDNAHandlerTest(unittest.TestCase):
- def handler_test(self):
+
+ def test_handler(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
self.assertIsInstance(handler, logging.Handler)
self.assertIsInstance(handler.internal_handler, logging.StreamHandler)
@@ -93,7 +99,7 @@ def handler_test(self):
self.assertEqual(handler.max_concurrent_requests,
defaults['MAX_CONCURRENT_REQUESTS'])
self.assertEqual(handler.retry_interval_secs,
- defaults['RETRY_INTERVAL_SECS'])
+ sample_options['retry_interval_secs'])
# Set the Flush-related Variables
self.assertEqual(handler.buf, [])
@@ -113,7 +119,7 @@ def handler_test(self):
self.assertIsInstance(handler.request_thread_pool, ThreadPoolExecutor)
self.assertEqual(handler.level, logging.DEBUG)
- def flusher_test(self):
+ def test_flusher(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
self.assertIsNone(handler.flusher)
handler.start_flusher()
@@ -121,7 +127,7 @@ def flusher_test(self):
handler.close_flusher()
self.assertIsNone(handler.flusher)
- def emit_test(self):
+ def test_emit(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
handler.buffer_log = unittest.mock.Mock()
handler.emit(sample_record)
@@ -129,31 +135,66 @@ def emit_test(self):
handler.buffer_log.assert_called_once_with(sample_message)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
- def try_request_test(self):
- requests.post = unittest.mock.Mock()
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
- requests.post.assert_called_with(
- url=handler.url,
- json={
- 'e': 'ls',
- 'ls': handler.buf
- },
- auth=('user', handler.key),
- params={
- 'hostname': handler.hostname,
- 'ip': handler.ip,
- 'mac': handler.mac,
- 'tags': handler.tags,
- 'now': int(now * 1000)
- },
- stream=True,
- timeout=handler.request_timeout,
- headers={'user-agent': handler.user_agent})
-
- def close_test(self):
+ def test_try_request(self):
+ with patch('requests.post') as post_mock:
+ r = requests.Response()
+ r.status_code = 200
+ r.reason = 'OK'
+ post_mock.return_value = r
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ post_mock.assert_called_with(
+ url=handler.url,
+ json={
+ 'e': 'ls',
+ 'ls': handler.buf
+ },
+ auth=('user', handler.key),
+ params={
+ 'hostname': handler.hostname,
+ 'ip': handler.ip,
+ 'mac': handler.mac,
+ 'tags': handler.tags,
+ 'now': int(now * 1000)
+ },
+ stream=True,
+ allow_redirects=True,
+ timeout=handler.request_timeout,
+ headers={'user-agent': handler.user_agent})
+ self.assertFalse(handler.exception_flag)
+ self.assertTrue(post_mock.call_count, 1)
+
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
+ def test_try_request_500(self):
+ with patch('requests.post') as post_mock:
+ r = requests.Response()
+ r.status_code = 500
+ r.reason = 'OK'
+ post_mock.return_value = r
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ self.assertTrue(handler.exception_flag)
+ self.assertTrue(post_mock.call_count, 3)
+
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
+ def test_try_request_403(self):
+ with patch('requests.post') as post_mock:
+ r = requests.Response()
+ r.status_code = 403
+ r.reason = 'OK'
+ post_mock.return_value = r
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ self.assertFalse(handler.exception_flag)
+ self.assertTrue(post_mock.call_count, 1)
+
+ def test_close(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
handler.close_flusher = unittest.mock.Mock()
handler.flush_sync = unittest.mock.Mock()
@@ -163,7 +204,7 @@ def close_test(self):
self.assertIsNone(handler.worker_thread_pool)
self.assertIsNone(handler.request_thread_pool)
- def flush_test(self):
+ def test_flush(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
handler.worker_thread_pool = MockThreadPoolExecutor()
handler.request_thread_pool = MockThreadPoolExecutor()
@@ -172,7 +213,7 @@ def flush_test(self):
handler.flush()
handler.try_request.assert_called_once_with()
- def flush_secondary_test(self):
+ def test_flush_secondary(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
clean = handler.clean_after_success
handler.worker_thread_pool = MockThreadPoolExecutor()
@@ -187,12 +228,11 @@ def flush_secondary_test(self):
'e': 'ls',
'ls': [sample_message]
})
-
clean()
self.assertEqual(handler.secondary, [])
self.assertEqual(handler.buf, [])
- def buffer_log_test(self):
+ def test_buffer_log(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
handler.worker_thread_pool = MockThreadPoolExecutor()
handler.request_thread_pool = MockThreadPoolExecutor()
@@ -206,16 +246,6 @@ def buffer_log_test(self):
self.assertEqual(handler.buf, [sample_message])
self.assertEqual(handler.buf_size, len(sample_message['line']))
- def test_run_tests(self):
- self.handler_test()
- self.flusher_test()
- self.emit_test()
- self.try_request_test()
- self.close_test()
- self.flush_test()
- self.flush_secondary_test()
- self.buffer_log_test()
-
if __name__ == '__main__':
unittest.main()
From da42cb21f7a3cf9caf64827ce0767d130a451889 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Tue, 10 May 2022 15:58:44 +0000
Subject: [PATCH 32/55] release: Version 1.18.2 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d33be9..2e34481 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.2 (2022-05-10)
+### Fix
+* Requests error handling ([#82](https://github.com/logdna/python/issues/82)) ([`e412859`](https://github.com/logdna/python/commit/e4128592aa9c2301c6467115148f2f23f88da9d3))
+
## v1.18.1 (2021-11-18)
### Fix
* **threading:** Account for secondary buffer flush and deadlock ([`d00b952`](https://github.com/logdna/python/commit/d00b9529d116ddf9ba454462d4d804dc54423c83))
diff --git a/logdna/VERSION b/logdna/VERSION
index ec6d649..b57fc72 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.1
+1.18.2
diff --git a/pyproject.toml b/pyproject.toml
index 0930cda..dc7df10 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.1"
+version = "1.18.2"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 052a4add6d7e241af007c278a7c0243373fa1d7f Mon Sep 17 00:00:00 2001
From: Dmitri Khokhlov
Date: Fri, 18 Nov 2022 11:14:00 -0800
Subject: [PATCH 33/55] doc: updated code examples to fix "ValueError:
incomplete format" error (#86)
---
README.md | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 5140281..2576fe6 100644
--- a/README.md
+++ b/README.md
@@ -54,12 +54,14 @@ options = {
# Defaults to False; when True meta objects are searchable
options['index_meta'] = True
+options['custom_fields'] = 'meta'
+
test = LogDNAHandler(key, options)
log.addHandler(test)
-log.warning("Warning message", {'app': 'bloop'})
+log.warning("Warning message", extra={'app': 'bloop'})
log.info("Info message")
```
@@ -80,10 +82,10 @@ After initial setup, logging is as easy as:
log.info('My Sample Log Line')
# Add a custom level
-log.info('My Sample Log Line', { 'level': 'MyCustomLevel' })
+log.info('My Sample Log Line', extra={ 'level': 'MyCustomLevel' })
# Include an App name with this specific log
-log.info('My Sample Log Line', { 'level': 'Warn', 'app': 'myAppName'})
+log.info('My Sample Log Line', extra={ 'level': 'Warn', 'app': 'myAppName'})
# Pass associated objects along as metadata
meta = {
@@ -98,7 +100,7 @@ opts = {
'meta': meta
}
-log.info('My Sample Log Line', opts)
+log.info('My Sample Log Line', extra=opts)
```
### Usage with fileConfig
From 75b892f90c7998637423ff6829403bd140d574e0 Mon Sep 17 00:00:00 2001
From: Tony Rogers
Date: Wed, 7 Dec 2022 12:11:57 -0500
Subject: [PATCH 34/55] add option to log the ingestion API response on error
(#87)
---
logdna/logdna.py | 15 ++++++++++++++-
tests/test_logger.py | 15 +++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index ec20841..a430a2d 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -34,6 +34,7 @@ def __init__(self, key, options={}):
self.tags = normalize_list_option(options, 'tags')
self.custom_fields = normalize_list_option(options, 'custom_fields')
self.custom_fields += defaults['META_FIELDS']
+ self.log_error_response = options.get('log_error_response', False)
# Set the Connection Variables
self.url = options.get('url', defaults['LOGDNA_URL'])
@@ -177,7 +178,7 @@ def try_request(self):
self.close_flusher()
self.exception_flag = True
- def send_request(self, data): # noqa: max-complexity: 13
+ def send_request(self, data): # noqa: max-complexity: 13
"""
Send log data to LogDNA server
Returns:
@@ -241,23 +242,35 @@ def send_request(self, data): # noqa: max-complexity: 13
self.internalLogger.debug('Unexpected response: %s. ' +
'Discarding flush buffer',
reason)
+ if self.log_error_response:
+ self.internalLogger.debug(
+ 'Error Response: %s', response.text)
return True # discard
if status_code in [401, 403]:
self.internalLogger.debug(
'Please provide a valid ingestion key. ' +
'Discarding flush buffer')
+ if self.log_error_response:
+ self.internalLogger.debug(
+ 'Error Response: %s', response.text)
return True # discard
if 400 <= status_code <= 499:
self.internalLogger.debug('Client Error: %s. ' +
'Discarding flush buffer',
reason)
+ if self.log_error_response:
+ self.internalLogger.debug(
+ 'Error Response: %s', response.text)
return True # discard
if status_code in [500, 502, 503, 507]:
self.internalLogger.debug('Server Error: %s. Retrying...',
reason)
+ if self.log_error_response:
+ self.internalLogger.debug(
+ 'Error Response: %s', response.text)
return False # retry
self.internalLogger.debug('The request failed: %s.' +
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 545ec0b..bb177e2 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -194,6 +194,21 @@ def test_try_request_403(self):
self.assertFalse(handler.exception_flag)
self.assertTrue(post_mock.call_count, 1)
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
+ def test_try_request_403_log_response(self):
+ with patch('requests.post') as post_mock:
+ r = requests.Response()
+ r.status_code = 403
+ r.reason = 'OK'
+ post_mock.return_value = r
+ sample_options['log_error_response'] = True
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ self.assertFalse(handler.exception_flag)
+ self.assertTrue(post_mock.call_count, 1)
+
def test_close(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
handler.close_flusher = unittest.mock.Mock()
From d5bf85ca26579e0186e1abdbcd1b6c44c60c9eca Mon Sep 17 00:00:00 2001
From: Tony Rogers
Date: Wed, 7 Dec 2022 12:38:21 -0500
Subject: [PATCH 35/55] fix: add documentation for log_error_response option
(#88)
---
README.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/README.md b/README.md
index 2576fe6..8db2bf3 100644
--- a/README.md
+++ b/README.md
@@ -252,6 +252,14 @@ A custom ingestion endpoint to stream log lines into.
List of fields out of `record` object to include in the `meta` object. By default, `args`, `name`, `pathname`, and `lineno` will be included.
+##### log_error_response
+
+* _Optional_
+* Type: [bool][]
+* Default: `False`
+
+Enables logging of the API response when an HTTP error is encountered
+
### log(line, [options])
#### line
From c6f5b7f83ab3408d3f64a5cf6f76eabec32b1d45 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Wed, 7 Dec 2022 17:39:46 +0000
Subject: [PATCH 36/55] release: Version 1.18.3 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2e34481..5e737e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.3 (2022-12-07)
+### Fix
+* Add documentation for log_error_response option ([#88](https://github.com/logdna/python/issues/88)) ([`d5bf85c`](https://github.com/logdna/python/commit/d5bf85ca26579e0186e1abdbcd1b6c44c60c9eca))
+
## v1.18.2 (2022-05-10)
### Fix
* Requests error handling ([#82](https://github.com/logdna/python/issues/82)) ([`e412859`](https://github.com/logdna/python/commit/e4128592aa9c2301c6467115148f2f23f88da9d3))
diff --git a/logdna/VERSION b/logdna/VERSION
index b57fc72..b9fb27a 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.2
+1.18.3
diff --git a/pyproject.toml b/pyproject.toml
index dc7df10..a41ccf3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.2"
+version = "1.18.3"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 7ccde50ba50c0abbec1a7efe4dd665e8b35511c0 Mon Sep 17 00:00:00 2001
From: Dmitri Khokhlov
Date: Fri, 6 Jan 2023 08:13:06 -0800
Subject: [PATCH 37/55] fix: Dependabot -> Vulnerabilities -> cryptography >=
37.0.0 < 38.0.3 (#91)
---
Jenkinsfile | 0
Makefile | 0
poetry.lock | 691 ++++++++++++++++++++++++++-----------------------
pyproject.toml | 2 +-
4 files changed, 363 insertions(+), 330 deletions(-)
mode change 100755 => 100644 Jenkinsfile
mode change 100755 => 100644 Makefile
diff --git a/Jenkinsfile b/Jenkinsfile
old mode 100755
new mode 100644
diff --git a/Makefile b/Makefile
old mode 100755
new mode 100644
diff --git a/poetry.lock b/poetry.lock
index ae55ed5..fc524bf 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -8,7 +8,7 @@ python-versions = "*"
[[package]]
name = "atomicwrites"
-version = "1.4.0"
+version = "1.4.1"
description = "Atomic file writes."
category = "dev"
optional = false
@@ -16,17 +16,19 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
-version = "21.4.0"
+version = "22.2.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.6"
[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", "cloudpickle"]
-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", "cloudpickle"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
+cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
+dev = ["attrs[docs,tests]"]
+docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"]
+tests = ["attrs[tests-no-zope]", "zope.interface"]
+tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=0.971,<0.990)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests_no_zope = ["cloudpickle", "hypothesis", "mypy (>=0.971,<0.990)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
[[package]]
name = "backcall"
@@ -38,7 +40,7 @@ python-versions = "*"
[[package]]
name = "bleach"
-version = "5.0.0"
+version = "5.0.1"
description = "An easy safelist-based HTML-sanitizing tool."
category = "dev"
optional = false
@@ -49,20 +51,20 @@ six = ">=1.9.0"
webencodings = "*"
[package.extras]
-css = ["tinycss2 (>=1.1.0)"]
-dev = ["pip-tools (==6.5.1)", "pytest (==7.1.1)", "flake8 (==4.0.1)", "tox (==3.24.5)", "sphinx (==4.3.2)", "twine (==4.0.0)", "wheel (==0.37.1)", "hashin (==0.17.0)", "black (==22.3.0)", "mypy (==0.942)"]
+css = ["tinycss2 (>=1.1.0,<1.2)"]
+dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"]
[[package]]
name = "certifi"
-version = "2021.10.8"
+version = "2022.12.7"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
-python-versions = "*"
+python-versions = ">=3.6"
[[package]]
name = "cffi"
-version = "1.15.0"
+version = "1.15.1"
description = "Foreign Function Interface for Python calling C code."
category = "dev"
optional = false
@@ -73,11 +75,11 @@ pycparser = "*"
[[package]]
name = "charset-normalizer"
-version = "2.0.12"
+version = "2.1.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
-python-versions = ">=3.5.0"
+python-versions = ">=3.6.0"
[package.extras]
unicode_backport = ["unicodedata2"]
@@ -107,11 +109,11 @@ click = "*"
[[package]]
name = "colorama"
-version = "0.4.4"
+version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
[[package]]
name = "coverage"
@@ -126,7 +128,7 @@ toml = ["toml"]
[[package]]
name = "cryptography"
-version = "37.0.2"
+version = "39.0.0"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
category = "dev"
optional = false
@@ -136,12 +138,12 @@ python-versions = ">=3.6"
cffi = ">=1.12"
[package.extras]
-docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
-docstest = ["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)"]
+docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"]
+docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
+pep8test = ["black", "ruff"]
+sdist = ["setuptools-rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"]
-test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
+test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
[[package]]
name = "decorator"
@@ -153,22 +155,19 @@ python-versions = ">=3.5"
[[package]]
name = "docutils"
-version = "0.18.1"
+version = "0.19"
description = "Docutils -- Python Documentation Utilities"
category = "dev"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.7"
[[package]]
name = "dotty-dict"
-version = "1.3.0"
+version = "1.3.1"
description = "Dictionary wrapper for quick access to deeply nested keys."
category = "dev"
optional = false
-python-versions = "*"
-
-[package.dependencies]
-setuptools_scm = "*"
+python-versions = ">=3.5,<4.0"
[[package]]
name = "flake8"
@@ -186,18 +185,18 @@ pyflakes = ">=2.3.0,<2.4.0"
[[package]]
name = "gitdb"
-version = "4.0.9"
+version = "4.0.10"
description = "Git Object Database"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
smmap = ">=3.0.1,<6"
[[package]]
-name = "gitpython"
-version = "3.1.27"
+name = "GitPython"
+version = "3.1.30"
description = "GitPython is a python library used to interact with Git repositories"
category = "dev"
optional = false
@@ -209,7 +208,7 @@ typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""
[[package]]
name = "idna"
-version = "3.3"
+version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
@@ -217,7 +216,7 @@ python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
-version = "4.11.3"
+version = "6.0.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
@@ -228,9 +227,24 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
-testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
+testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
+
+[[package]]
+name = "importlib-resources"
+version = "5.10.2"
+description = "Read resources from Python packages"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[[package]]
name = "iniconfig"
@@ -242,7 +256,7 @@ python-versions = "*"
[[package]]
name = "invoke"
-version = "1.7.0"
+version = "1.7.3"
description = "Pythonic task execution"
category = "dev"
optional = false
@@ -250,20 +264,20 @@ python-versions = "*"
[[package]]
name = "ipdb"
-version = "0.13.9"
+version = "0.13.11"
description = "IPython-enabled pdb"
category = "dev"
optional = false
-python-versions = ">=2.7"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
decorator = {version = "*", markers = "python_version > \"3.6\""}
-ipython = {version = ">=7.17.0", markers = "python_version > \"3.6\""}
-toml = {version = ">=0.10.2", markers = "python_version > \"3.6\""}
+ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""}
+tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""}
[[package]]
name = "ipython"
-version = "7.33.0"
+version = "7.34.0"
description = "IPython: Productive Interactive Computing"
category = "dev"
optional = false
@@ -280,6 +294,7 @@ pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
pickleshare = "*"
prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
pygments = "*"
+setuptools = ">=18.5"
traitlets = ">=4.2"
[package.extras]
@@ -288,14 +303,29 @@ doc = ["Sphinx (>=1.3)"]
kernel = ["ipykernel"]
nbconvert = ["nbconvert"]
nbformat = ["nbformat"]
-notebook = ["notebook", "ipywidgets"]
+notebook = ["ipywidgets", "notebook"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
-test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
+test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"]
+
+[[package]]
+name = "jaraco.classes"
+version = "3.2.3"
+description = "Utility functions for Python class constructs"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+more-itertools = "*"
+
+[package.extras]
+docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
+testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[[package]]
name = "jedi"
-version = "0.18.1"
+version = "0.18.2"
description = "An autocompletion tool for Python that can be used for text editors."
category = "dev"
optional = false
@@ -305,8 +335,9 @@ python-versions = ">=3.6"
parso = ">=0.8.0,<0.9.0"
[package.extras]
+docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
+testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]]
name = "jeepney"
@@ -317,30 +348,33 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-test = ["pytest", "pytest-trio", "pytest-asyncio (>=0.17)", "testpath", "trio", "async-timeout"]
-trio = ["trio", "async-generator"]
+test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"]
+trio = ["async_generator", "trio"]
[[package]]
name = "keyring"
-version = "23.5.0"
+version = "23.13.1"
description = "Store and access your passwords safely."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
-importlib-metadata = ">=3.6"
+importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""}
+importlib-resources = {version = "*", markers = "python_version < \"3.9\""}
+"jaraco.classes" = "*"
jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""}
-pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""}
+pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""}
SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""}
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"]
-testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"]
+completion = ["shtab"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
+testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[[package]]
name = "matplotlib-inline"
-version = "0.1.3"
+version = "0.1.6"
description = "Inline Matplotlib backend for Jupyter"
category = "dev"
optional = false
@@ -357,6 +391,14 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "more-itertools"
+version = "9.0.0"
+description = "More routines for operating on iterables, beyond itertools"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
[[package]]
name = "mslex"
version = "0.3.0"
@@ -367,14 +409,11 @@ python-versions = ">=3.5"
[[package]]
name = "packaging"
-version = "21.3"
+version = "22.0"
description = "Core utilities for Python packages"
category = "dev"
optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
+python-versions = ">=3.7"
[[package]]
name = "parso"
@@ -409,14 +448,14 @@ python-versions = "*"
[[package]]
name = "pkginfo"
-version = "1.8.2"
+version = "1.9.4"
description = "Query metadatdata from sdists / bdists / installed packages."
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.6"
[package.extras]
-testing = ["coverage", "nose"]
+testing = ["pytest", "pytest-cov"]
[[package]]
name = "pluggy"
@@ -435,7 +474,7 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "prompt-toolkit"
-version = "3.0.29"
+version = "3.0.36"
description = "Library for building powerful interactive command lines in Python"
category = "dev"
optional = false
@@ -446,14 +485,14 @@ wcwidth = "*"
[[package]]
name = "psutil"
-version = "5.9.0"
+version = "5.9.4"
description = "Cross-platform lib for process and system monitoring in Python."
category = "dev"
optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.extras]
-test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
+test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
[[package]]
name = "ptyprocess"
@@ -496,23 +535,15 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
-name = "pygments"
-version = "2.12.0"
+name = "Pygments"
+version = "2.14.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
optional = false
python-versions = ">=3.6"
-[[package]]
-name = "pyparsing"
-version = "3.0.8"
-description = "pyparsing module - Classes and methods to define and execute parsing grammars"
-category = "dev"
-optional = false
-python-versions = ">=3.6.8"
-
[package.extras]
-diagrams = ["railroad-diagrams", "jinja2"]
+plugins = ["importlib-metadata"]
[[package]]
name = "pytest"
@@ -550,11 +581,11 @@ pytest = ">=4.6"
toml = "*"
[package.extras]
-testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "python-gitlab"
-version = "3.4.0"
+version = "3.12.0"
description = "Interact with GitLab API"
category = "dev"
optional = false
@@ -570,7 +601,7 @@ yaml = ["PyYaml (>=5.2)"]
[[package]]
name = "python-semantic-release"
-version = "7.28.1"
+version = "7.32.2"
description = "Automatic Semantic Versioning for Python projects"
category = "dev"
optional = false
@@ -582,17 +613,19 @@ click-log = ">=0.3,<1"
dotty-dict = ">=1.3.0,<2"
gitpython = ">=3.0.8,<4"
invoke = ">=1.4.1,<2"
+packaging = "*"
python-gitlab = ">=2,<4"
requests = ">=2.25,<3"
semver = ">=2.10,<3"
-tomlkit = ">=0.10.0,<0.11.0"
+tomlkit = ">=0.10,<1.0"
twine = ">=3,<4"
+wheel = "*"
[package.extras]
-dev = ["tox", "isort", "black"]
-docs = ["Sphinx (==1.3.6)"]
+dev = ["black", "isort", "tox"]
+docs = ["Jinja2 (==3.0.3)", "Sphinx (==1.3.6)"]
mypy = ["mypy", "types-requests"]
-test = ["coverage (>=5,<6)", "pytest (>=5,<6)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.13.3)", "mock (==1.3.0)"]
+test = ["coverage (>=5,<6)", "mock (==1.3.0)", "pytest (>=5,<6)", "pytest-mock (>=2,<3)", "pytest-xdist (>=1,<2)", "responses (==0.13.3)"]
[[package]]
name = "pywin32-ctypes"
@@ -604,7 +637,7 @@ python-versions = "*"
[[package]]
name = "readme-renderer"
-version = "35.0"
+version = "37.3"
description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse"
category = "dev"
optional = false
@@ -620,48 +653,33 @@ md = ["cmarkgfm (>=0.8.0)"]
[[package]]
name = "requests"
-version = "2.27.1"
+version = "2.28.1"
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.*"
+python-versions = ">=3.7, <4"
[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\""}
+charset-normalizer = ">=2,<3"
+idna = ">=2.5,<4"
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)"]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "requests-toolbelt"
-version = "0.9.1"
+version = "0.10.1"
description = "A utility belt for advanced users of python-requests"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
requests = ">=2.0.1,<3.0.0"
-[[package]]
-name = "responses"
-version = "0.20.0"
-description = "A utility library for mocking out the `requests` Python library."
-category = "dev"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-requests = ">=2.0,<3.0"
-urllib3 = ">=1.25.10"
-
-[package.extras]
-tests = ["pytest (>=7.0.0)", "coverage (>=6.0.0)", "pytest-cov", "pytest-asyncio", "pytest-localserver", "flake8", "types-mock", "types-requests", "mypy"]
-
[[package]]
name = "rfc3986"
version = "2.0.0"
@@ -674,8 +692,8 @@ python-versions = ">=3.7"
idna2008 = ["idna"]
[[package]]
-name = "secretstorage"
-version = "3.3.2"
+name = "SecretStorage"
+version = "3.3.3"
description = "Python bindings to FreeDesktop.org Secret Service API"
category = "dev"
optional = false
@@ -694,20 +712,17 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
-name = "setuptools-scm"
-version = "6.4.2"
-description = "the blessed package to manage your versions by scm tags"
+name = "setuptools"
+version = "65.6.3"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-packaging = ">=20.0"
-tomli = ">=1.0.0"
+python-versions = ">=3.7"
[package.extras]
-test = ["pytest (>=6.2)", "virtualenv (>20)"]
-toml = ["setuptools (>=42)"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "six"
@@ -734,11 +749,11 @@ optional = false
python-versions = "*"
[package.extras]
-yaml = ["more-itertools", "PyYAML (>=5.1)"]
+yaml = ["PyYAML (>=5.1)", "more-itertools"]
[[package]]
name = "taskipy"
-version = "1.10.1"
+version = "1.10.3"
description = "tasks runner for python projects"
category = "dev"
optional = false
@@ -748,7 +763,7 @@ python-versions = ">=3.6,<4.0"
colorama = ">=0.4.4,<0.5.0"
mslex = {version = ">=0.3.0,<0.4.0", markers = "sys_platform == \"win32\""}
psutil = ">=5.7.2,<6.0.0"
-tomli = ">=1.2.3,<2.0.0"
+tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
[[package]]
name = "toml"
@@ -760,23 +775,23 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "tomli"
-version = "1.2.3"
+version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[[package]]
name = "tomlkit"
-version = "0.10.2"
+version = "0.11.6"
description = "Style preserving TOML library"
category = "dev"
optional = false
-python-versions = ">=3.6,<4.0"
+python-versions = ">=3.6"
[[package]]
name = "tqdm"
-version = "4.64.0"
+version = "4.64.1"
description = "Fast, Extensible Progress Meter"
category = "dev"
optional = false
@@ -793,14 +808,15 @@ telegram = ["requests"]
[[package]]
name = "traitlets"
-version = "5.1.1"
+version = "5.8.0"
description = "Traitlets Python configuration system"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
-test = ["pytest"]
+docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
+test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"]
[[package]]
name = "twine"
@@ -824,7 +840,7 @@ urllib3 = ">=1.26.0"
[[package]]
name = "typing-extensions"
-version = "4.2.0"
+version = "4.4.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "dev"
optional = false
@@ -832,15 +848,15 @@ python-versions = ">=3.7"
[[package]]
name = "urllib3"
-version = "1.26.9"
+version = "1.26.13"
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"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[package.extras]
-brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
-secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
@@ -859,6 +875,17 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "wheel"
+version = "0.38.4"
+description = "A built-package format for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+test = ["pytest (>=3.0.0)"]
+
[[package]]
name = "yapf"
version = "0.30.0"
@@ -869,20 +896,20 @@ python-versions = "*"
[[package]]
name = "zipp"
-version = "3.8.0"
+version = "3.11.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
-docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
-testing = ["pytest (>=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 (>=0.9.1)"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
+testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "95a17f1fcf4d2a76cbd338051e679f32f1327891061b7e486b0f5f8ebb759532"
+content-hash = "0418105f2bf54e32025584bb913cf422a22714b854ba60b8d8f8c6a6c8345d66"
[metadata.files]
appnope = [
@@ -890,80 +917,93 @@ appnope = [
{file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
]
atomicwrites = [
- {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
- {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
+ {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
]
attrs = [
- {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
- {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
+ {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
+ {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
]
backcall = [
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
bleach = [
- {file = "bleach-5.0.0-py3-none-any.whl", hash = "sha256:08a1fe86d253b5c88c92cc3d810fd8048a16d15762e1e5b74d502256e5926aa1"},
- {file = "bleach-5.0.0.tar.gz", hash = "sha256:c6d6cc054bdc9c83b48b8083e236e5f00f238428666d2ce2e083eaa5fd568565"},
+ {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"},
+ {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"},
]
certifi = [
- {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
- {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
+ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
+ {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
]
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"},
+ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
+ {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
+ {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
+ {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
+ {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
+ {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
+ {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
+ {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
+ {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
+ {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
+ {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
+ {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
+ {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
+ {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
+ {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
+ {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
+ {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
+ {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
+ {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
+ {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
+ {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
+ {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
+ {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
+ {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
+ {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
+ {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
+ {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
+ {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
+ {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
+ {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
+ {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
+ {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
+ {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
]
charset-normalizer = [
- {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
- {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
+ {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
+ {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
@@ -974,8 +1014,8 @@ click-log = [
{file = "click_log-0.4.0-py2.py3-none-any.whl", hash = "sha256:a43e394b528d52112af599f2fc9e4b7cf3c15f94e53581f74fa6867e68c91756"},
]
colorama = [
- {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
- {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
coverage = [
{file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
@@ -1032,102 +1072,117 @@ coverage = [
{file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
]
cryptography = [
- {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"},
- {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3c81599befb4d4f3d7648ed3217e00d21a9341a9a688ecdd615ff72ffbed7336"},
- {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bd1096476aaac820426239ab534b636c77d71af66c547b9ddcd76eb9c79e004"},
- {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:31fe38d14d2e5f787e0aecef831457da6cec68e0bb09a35835b0b44ae8b988fe"},
- {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093cb351031656d3ee2f4fa1be579a8c69c754cf874206be1d4cf3b542042804"},
- {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b281eab51e1b6b6afa525af2bd93c16d49358404f814fe2c2410058623928c"},
- {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:0cc20f655157d4cfc7bada909dc5cc228211b075ba8407c46467f63597c78178"},
- {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f8ec91983e638a9bcd75b39f1396e5c0dc2330cbd9ce4accefe68717e6779e0a"},
- {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:46f4c544f6557a2fefa7ac8ac7d1b17bf9b647bd20b16decc8fbcab7117fbc15"},
- {file = "cryptography-37.0.2-cp36-abi3-win32.whl", hash = "sha256:731c8abd27693323b348518ed0e0705713a36d79fdbd969ad968fbef0979a7e0"},
- {file = "cryptography-37.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:471e0d70201c069f74c837983189949aa0d24bb2d751b57e26e3761f2f782b8d"},
- {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a68254dd88021f24a68b613d8c51d5c5e74d735878b9e32cc0adf19d1f10aaf9"},
- {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:a7d5137e556cc0ea418dca6186deabe9129cee318618eb1ffecbd35bee55ddc1"},
- {file = "cryptography-37.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aeaba7b5e756ea52c8861c133c596afe93dd716cbcacae23b80bc238202dc023"},
- {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e590dd70642eb2079d280420a888190aa040ad20f19ec8c6e097e38aa29e06"},
- {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1b9362d34363f2c71b7853f6251219298124aa4cc2075ae2932e64c91a3e2717"},
- {file = "cryptography-37.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e53258e69874a306fcecb88b7534d61820db8a98655662a3dd2ec7f1afd9132f"},
- {file = "cryptography-37.0.2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:1f3bfbd611db5cb58ca82f3deb35e83af34bb8cf06043fa61500157d50a70982"},
- {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:419c57d7b63f5ec38b1199a9521d77d7d1754eb97827bbb773162073ccd8c8d4"},
- {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:dc26bb134452081859aa21d4990474ddb7e863aa39e60d1592800a8865a702de"},
- {file = "cryptography-37.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b8398b3d0efc420e777c40c16764d6870bcef2eb383df9c6dbb9ffe12c64452"},
- {file = "cryptography-37.0.2.tar.gz", hash = "sha256:f224ad253cc9cea7568f49077007d2263efa57396a2f2f78114066fd54b5c68e"},
+ {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288"},
+ {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72"},
+ {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9"},
+ {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f"},
+ {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b"},
+ {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c"},
+ {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96"},
+ {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717"},
+ {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df"},
+ {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1"},
+ {file = "cryptography-39.0.0-cp36-abi3-win32.whl", hash = "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de"},
+ {file = "cryptography-39.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e"},
+ {file = "cryptography-39.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb"},
+ {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f"},
+ {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"},
+ {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190"},
+ {file = "cryptography-39.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773"},
+ {file = "cryptography-39.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856"},
+ {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f"},
+ {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce"},
+ {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8"},
+ {file = "cryptography-39.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39"},
+ {file = "cryptography-39.0.0.tar.gz", hash = "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf"},
]
decorator = [
{file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
{file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
]
docutils = [
- {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"},
- {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"},
+ {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"},
+ {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"},
]
dotty-dict = [
- {file = "dotty_dict-1.3.0.tar.gz", hash = "sha256:eb0035a3629ecd84397a68f1f42f1e94abd1c34577a19cd3eacad331ee7cbaf0"},
+ {file = "dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f"},
+ {file = "dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15"},
]
flake8 = [
{file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
{file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
]
gitdb = [
- {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
- {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
+ {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"},
+ {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"},
]
-gitpython = [
- {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"},
- {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"},
+GitPython = [
+ {file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"},
+ {file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"},
]
idna = [
- {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
- {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
+ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
importlib-metadata = [
- {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"},
- {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"},
+ {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"},
+ {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"},
+]
+importlib-resources = [
+ {file = "importlib_resources-5.10.2-py3-none-any.whl", hash = "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6"},
+ {file = "importlib_resources-5.10.2.tar.gz", hash = "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
invoke = [
- {file = "invoke-1.7.0-py3-none-any.whl", hash = "sha256:a5159fc63dba6ca2a87a1e33d282b99cea69711b03c64a35bb4e1c53c6c4afa0"},
- {file = "invoke-1.7.0.tar.gz", hash = "sha256:e332e49de40463f2016315f51df42313855772be86435686156bc18f45b5cc6c"},
+ {file = "invoke-1.7.3-py3-none-any.whl", hash = "sha256:d9694a865764dd3fd91f25f7e9a97fb41666e822bbb00e670091e3f43933574d"},
+ {file = "invoke-1.7.3.tar.gz", hash = "sha256:41b428342d466a82135d5ab37119685a989713742be46e42a3a399d685579314"},
]
ipdb = [
- {file = "ipdb-0.13.9.tar.gz", hash = "sha256:951bd9a64731c444fd907a5ce268543020086a697f6be08f7cc2c9a752a278c5"},
+ {file = "ipdb-0.13.11-py3-none-any.whl", hash = "sha256:f74c2f741c18b909eaf89f19fde973f745ac721744aa1465888ce45813b63a9c"},
+ {file = "ipdb-0.13.11.tar.gz", hash = "sha256:c23b6736f01fd4586cc2ecbebdf79a5eb454796853e1cd8f2ed3b7b91d4a3e93"},
]
ipython = [
- {file = "ipython-7.33.0-py3-none-any.whl", hash = "sha256:916a3126896e4fd78dd4d9cf3e21586e7fd93bae3f1cd751588b75524b64bf94"},
- {file = "ipython-7.33.0.tar.gz", hash = "sha256:bcffb865a83b081620301ba0ec4d95084454f26b91d6d66b475bff3dfb0218d4"},
+ {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"},
+ {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"},
+]
+"jaraco.classes" = [
+ {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"},
+ {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"},
]
jedi = [
- {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
- {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
+ {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"},
+ {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"},
]
jeepney = [
{file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"},
{file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"},
]
keyring = [
- {file = "keyring-23.5.0-py3-none-any.whl", hash = "sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261"},
- {file = "keyring-23.5.0.tar.gz", hash = "sha256:9012508e141a80bd1c0b6778d5c610dd9f8c464d75ac6774248500503f972fb9"},
+ {file = "keyring-23.13.1-py3-none-any.whl", hash = "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd"},
+ {file = "keyring-23.13.1.tar.gz", hash = "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"},
]
matplotlib-inline = [
- {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
- {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
+ {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
+ {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
+more-itertools = [
+ {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"},
+ {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"},
+]
mslex = [
{file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"},
{file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"},
]
packaging = [
- {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
- {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
+ {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"},
+ {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"},
]
parso = [
{file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
@@ -1142,50 +1197,32 @@ pickleshare = [
{file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
]
pkginfo = [
- {file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"},
- {file = "pkginfo-1.8.2.tar.gz", hash = "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff"},
+ {file = "pkginfo-1.9.4-py3-none-any.whl", hash = "sha256:7fc056f8e6b93355925083373b0f6bfbabe84ee3f29854fd155c9d6a4211267d"},
+ {file = "pkginfo-1.9.4.tar.gz", hash = "sha256:e769fd353593d43e0c9f47e17e25f09a8efcddcdf9a71674ea3ba444ff31bb44"},
]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
prompt-toolkit = [
- {file = "prompt_toolkit-3.0.29-py3-none-any.whl", hash = "sha256:62291dad495e665fca0bda814e342c69952086afb0f4094d0893d357e5c78752"},
- {file = "prompt_toolkit-3.0.29.tar.gz", hash = "sha256:bd640f60e8cecd74f0dc249713d433ace2ddc62b65ee07f96d358e0b152b6ea7"},
+ {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"},
+ {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"},
]
psutil = [
- {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"},
- {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"},
- {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"},
- {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"},
- {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"},
- {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"},
- {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"},
- {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"},
- {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"},
- {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"},
- {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"},
- {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"},
- {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"},
- {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"},
- {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"},
- {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"},
- {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"},
- {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"},
- {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"},
- {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"},
- {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"},
- {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"},
- {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"},
- {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"},
- {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"},
- {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"},
- {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"},
- {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"},
- {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"},
- {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"},
- {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"},
- {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"},
+ {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"},
+ {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"},
+ {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"},
+ {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"},
+ {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"},
+ {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"},
+ {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"},
+ {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"},
+ {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"},
+ {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"},
+ {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"},
+ {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"},
+ {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"},
+ {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"},
]
ptyprocess = [
{file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
@@ -1207,13 +1244,9 @@ pyflakes = [
{file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
{file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
]
-pygments = [
- {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"},
- {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"},
-]
-pyparsing = [
- {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"},
- {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"},
+Pygments = [
+ {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"},
+ {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"},
]
pytest = [
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
@@ -1224,48 +1257,44 @@ pytest-cov = [
{file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
]
python-gitlab = [
- {file = "python-gitlab-3.4.0.tar.gz", hash = "sha256:6180b81ee2f265ad8d8412956a1740b4d3ceca7b28ae2f707dfe62375fed0082"},
- {file = "python_gitlab-3.4.0-py3-none-any.whl", hash = "sha256:251b63f0589d51f854516948c84e9eb8df26e1e9dea595cf86b43f17c43007dd"},
+ {file = "python-gitlab-3.12.0.tar.gz", hash = "sha256:567390c2b93690dae62ed9738bf9f221fa45c01378fdf896089dbf7c8a134fbd"},
+ {file = "python_gitlab-3.12.0-py3-none-any.whl", hash = "sha256:a5eb36b49783fda34563376674d5251dbbdbd1abd23b287dadf82f67d861b2c1"},
]
python-semantic-release = [
- {file = "python-semantic-release-7.28.1.tar.gz", hash = "sha256:d7f82b3d4c06b304d07689b8a1c7d0d448ff07c2ab81cd810c4f2f900f24c599"},
- {file = "python_semantic_release-7.28.1-py3-none-any.whl", hash = "sha256:319c3e811d6e10bc3f0e967419eae0e5e9ba1e6745aa2fd94dd24e3995541ca2"},
+ {file = "python-semantic-release-7.32.2.tar.gz", hash = "sha256:4d8f5d20680723e1329765b6f3e28b43f058fd1ef5f423f6f95397cd927c3ebc"},
+ {file = "python_semantic_release-7.32.2-py3-none-any.whl", hash = "sha256:9fcf82f403b91a61e58728ea05e2e2e25010ce9ed07830fe78251819b4b834d9"},
]
pywin32-ctypes = [
{file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
{file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
]
readme-renderer = [
- {file = "readme_renderer-35.0-py3-none-any.whl", hash = "sha256:73b84905d091c31f36e50b4ae05ae2acead661f6a09a9abb4df7d2ddcdb6a698"},
- {file = "readme_renderer-35.0.tar.gz", hash = "sha256:a727999acfc222fc21d82a12ed48c957c4989785e5865807c65a487d21677497"},
+ {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"},
+ {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"},
]
requests = [
- {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
- {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
+ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
+ {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
]
requests-toolbelt = [
- {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"},
- {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"},
-]
-responses = [
- {file = "responses-0.20.0-py3-none-any.whl", hash = "sha256:18831bc2d72443b67664d98038374a6fa1f27eaaff4dd9a7d7613723416fea3c"},
- {file = "responses-0.20.0.tar.gz", hash = "sha256:644905bc4fb8a18fa37e3882b2ac05e610fe8c2f967d327eed669e314d94a541"},
+ {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"},
+ {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"},
]
rfc3986 = [
{file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"},
{file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"},
]
-secretstorage = [
- {file = "SecretStorage-3.3.2-py3-none-any.whl", hash = "sha256:755dc845b6ad76dcbcbc07ea3da75ae54bb1ea529eb72d15f83d26499a5df319"},
- {file = "SecretStorage-3.3.2.tar.gz", hash = "sha256:0a8eb9645b320881c222e827c26f4cfcf55363e8b374a021981ef886657a912f"},
+SecretStorage = [
+ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"},
+ {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"},
]
semver = [
{file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"},
{file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"},
]
-setuptools-scm = [
- {file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"},
- {file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"},
+setuptools = [
+ {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"},
+ {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
@@ -1280,40 +1309,40 @@ smmap = [
{file = "tap.py-3.1.tar.gz", hash = "sha256:3c0cd45212ad5a25b35445964e2517efa000a118a1bfc3437dae828892eaf1e1"},
]
taskipy = [
- {file = "taskipy-1.10.1-py3-none-any.whl", hash = "sha256:9b38333654da487b6d16de6fa330b7629d1935d1e74819ba4c5f17a1c372d37b"},
- {file = "taskipy-1.10.1.tar.gz", hash = "sha256:6fa0b11c43d103e376063e90be31d87b435aad50fb7dc1c9a2de9b60a85015ed"},
+ {file = "taskipy-1.10.3-py3-none-any.whl", hash = "sha256:4c0070ca53868d97989f7ab5c6f237525d52ee184f9b967576e8fe427ed9d0b8"},
+ {file = "taskipy-1.10.3.tar.gz", hash = "sha256:112beaf21e3d5569950b99162a1de003fa885fabee9e450757a6b874be914877"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
tomli = [
- {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"},
- {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"},
+ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
tomlkit = [
- {file = "tomlkit-0.10.2-py3-none-any.whl", hash = "sha256:905cf92c2111ef80d355708f47ac24ad1b6fc2adc5107455940088c9bbecaedb"},
- {file = "tomlkit-0.10.2.tar.gz", hash = "sha256:30d54c0b914e595f3d10a87888599eab5321a2a69abc773bbefff51599b72db6"},
+ {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"},
+ {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"},
]
tqdm = [
- {file = "tqdm-4.64.0-py2.py3-none-any.whl", hash = "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"},
- {file = "tqdm-4.64.0.tar.gz", hash = "sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d"},
+ {file = "tqdm-4.64.1-py2.py3-none-any.whl", hash = "sha256:6fee160d6ffcd1b1c68c65f14c829c22832bc401726335ce92c52d395944a6a1"},
+ {file = "tqdm-4.64.1.tar.gz", hash = "sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4"},
]
traitlets = [
- {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"},
- {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"},
+ {file = "traitlets-5.8.0-py3-none-any.whl", hash = "sha256:c864831efa0ba6576d09b44884b34e41defc18c0d7e720b4a2d6698c842cab3e"},
+ {file = "traitlets-5.8.0.tar.gz", hash = "sha256:6cc57d6dc28c85d5365961726ffd19b538739347749e13ebe34e03323a0e8f84"},
]
twine = [
{file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"},
{file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"},
]
typing-extensions = [
- {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
- {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
+ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
+ {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
]
urllib3 = [
- {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"},
- {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
+ {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"},
+ {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"},
]
wcwidth = [
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
@@ -1323,11 +1352,15 @@ webencodings = [
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
]
+wheel = [
+ {file = "wheel-0.38.4-py3-none-any.whl", hash = "sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8"},
+ {file = "wheel-0.38.4.tar.gz", hash = "sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac"},
+]
yapf = [
{file = "yapf-0.30.0-py2.py3-none-any.whl", hash = "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"},
{file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"},
]
zipp = [
- {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"},
- {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"},
+ {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"},
+ {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"},
]
diff --git a/pyproject.toml b/pyproject.toml
index a41ccf3..31960c5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -14,7 +14,7 @@ commit_author = "LogDNA Bot "
[tool.poetry.dependencies]
python = "^3.7"
-requests = "^2.25.1"
+requests = "^2.28.1"
[tool.poetry.dev-dependencies]
coverage = "^5.4"
From 834dd9aac5f763c7eb462d47c175f76f1aa442b5 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Fri, 6 Jan 2023 16:17:15 +0000
Subject: [PATCH 38/55] release: Version 1.18.4 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e737e5..0a58667 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.4 (2023-01-06)
+### Fix
+* Dependabot -> Vulnerabilities -> cryptography >= 37.0.0 < 38.0.3 ([#91](https://github.com/logdna/python/issues/91)) ([`7ccde50`](https://github.com/logdna/python/commit/7ccde50ba50c0abbec1a7efe4dd665e8b35511c0))
+
## v1.18.3 (2022-12-07)
### Fix
* Add documentation for log_error_response option ([#88](https://github.com/logdna/python/issues/88)) ([`d5bf85c`](https://github.com/logdna/python/commit/d5bf85ca26579e0186e1abdbcd1b6c44c60c9eca))
diff --git a/logdna/VERSION b/logdna/VERSION
index b9fb27a..a67b05e 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.3
+1.18.4
diff --git a/pyproject.toml b/pyproject.toml
index 31960c5..3b661cd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.3"
+version = "1.18.4"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 905b06a648e19896087b0c51ba1e055212727560 Mon Sep 17 00:00:00 2001
From: Dmitri Khokhlov
Date: Fri, 27 Jan 2023 08:49:38 -0800
Subject: [PATCH 39/55] fix(chore): upgraded pytest to resolve security
vulnerability in py <= 1.11.0 (#92)
---
poetry.lock | 698 ++++++++++---------------------------------------
pyproject.toml | 2 +-
2 files changed, 143 insertions(+), 557 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index fc524bf..c7fb7c7 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -6,14 +6,6 @@ category = "dev"
optional = false
python-versions = "*"
-[[package]]
-name = "atomicwrites"
-version = "1.4.1"
-description = "Atomic file writes."
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
[[package]]
name = "attrs"
version = "22.2.0"
@@ -23,12 +15,12 @@ optional = false
python-versions = ">=3.6"
[package.extras]
-cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
-dev = ["attrs[docs,tests]"]
-docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"]
-tests = ["attrs[tests-no-zope]", "zope.interface"]
-tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=0.971,<0.990)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
-tests_no_zope = ["cloudpickle", "hypothesis", "mypy (>=0.971,<0.990)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+cov = ["attrs", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
+dev = ["attrs"]
+docs = ["furo", "sphinx", "myst-parser", "zope.interface", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
+tests = ["attrs", "zope.interface"]
+tests-no-zope = ["hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist", "cloudpickle", "mypy (>=0.971,<0.990)", "pytest-mypy-plugins"]
+tests_no_zope = ["hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist", "cloudpickle", "mypy (>=0.971,<0.990)", "pytest-mypy-plugins"]
[[package]]
name = "backcall"
@@ -40,7 +32,7 @@ python-versions = "*"
[[package]]
name = "bleach"
-version = "5.0.1"
+version = "6.0.0"
description = "An easy safelist-based HTML-sanitizing tool."
category = "dev"
optional = false
@@ -52,7 +44,6 @@ webencodings = "*"
[package.extras]
css = ["tinycss2 (>=1.1.0,<1.2)"]
-dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"]
[[package]]
name = "certifi"
@@ -75,14 +66,11 @@ pycparser = "*"
[[package]]
name = "charset-normalizer"
-version = "2.1.1"
+version = "3.0.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
-python-versions = ">=3.6.0"
-
-[package.extras]
-unicode_backport = ["unicodedata2"]
+python-versions = "*"
[[package]]
name = "click"
@@ -139,11 +127,11 @@ cffi = ">=1.12"
[package.extras]
docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"]
-docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
+docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
pep8test = ["black", "ruff"]
sdist = ["setuptools-rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"]
-test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
+test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
[[package]]
name = "decorator"
@@ -169,6 +157,17 @@ category = "dev"
optional = false
python-versions = ">=3.5,<4.0"
+[[package]]
+name = "exceptiongroup"
+version = "1.1.0"
+description = "Backport of PEP 654 (exception groups)"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+test = ["pytest (>=6)"]
+
[[package]]
name = "flake8"
version = "3.9.2"
@@ -195,7 +194,7 @@ python-versions = ">=3.7"
smmap = ">=3.0.1,<6"
[[package]]
-name = "GitPython"
+name = "gitpython"
version = "3.1.30"
description = "GitPython is a python library used to interact with Git repositories"
category = "dev"
@@ -227,9 +226,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"]
perf = ["ipython"]
-testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8", "importlib-resources (>=1.3)"]
[[package]]
name = "importlib-resources"
@@ -243,16 +242,16 @@ python-versions = ">=3.7"
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "sphinx-lint", "jaraco.tidelift (>=1.4)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"]
[[package]]
name = "iniconfig"
-version = "1.1.1"
-description = "iniconfig: brain-dead simple config-ini parsing"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
[[package]]
name = "invoke"
@@ -271,8 +270,8 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
-decorator = {version = "*", markers = "python_version > \"3.6\""}
-ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""}
+decorator = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\" or python_version >= \"3.11\""}
+ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\" and python_version < \"3.11\" or python_version >= \"3.11\""}
tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""}
[[package]]
@@ -294,7 +293,6 @@ pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
pickleshare = "*"
prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
pygments = "*"
-setuptools = ">=18.5"
traitlets = ">=4.2"
[package.extras]
@@ -303,10 +301,10 @@ doc = ["Sphinx (>=1.3)"]
kernel = ["ipykernel"]
nbconvert = ["nbconvert"]
nbformat = ["nbformat"]
-notebook = ["ipywidgets", "notebook"]
+notebook = ["notebook", "ipywidgets"]
parallel = ["ipyparallel"]
qtconsole = ["qtconsole"]
-test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"]
+test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
[[package]]
name = "jaraco.classes"
@@ -320,8 +318,8 @@ python-versions = ">=3.7"
more-itertools = "*"
[package.extras]
-docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
-testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"]
[[package]]
name = "jedi"
@@ -335,7 +333,7 @@ python-versions = ">=3.6"
parso = ">=0.8.0,<0.9.0"
[package.extras]
-docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
+docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx-rtd-theme (==0.4.3)", "sphinx (==1.8.5)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
@@ -348,8 +346,8 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"]
-trio = ["async_generator", "trio"]
+test = ["pytest", "pytest-trio", "pytest-asyncio (>=0.17)", "testpath", "trio", "async-timeout"]
+trio = ["trio", "async-generator"]
[[package]]
name = "keyring"
@@ -369,8 +367,8 @@ SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""}
[package.extras]
completion = ["shtab"]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
-testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"]
[[package]]
name = "matplotlib-inline"
@@ -409,7 +407,7 @@ python-versions = ">=3.5"
[[package]]
name = "packaging"
-version = "22.0"
+version = "23.0"
description = "Core utilities for Python packages"
category = "dev"
optional = false
@@ -448,8 +446,8 @@ python-versions = "*"
[[package]]
name = "pkginfo"
-version = "1.9.4"
-description = "Query metadatdata from sdists / bdists / installed packages."
+version = "1.9.6"
+description = "Query metadata from sdists / bdists / installed packages."
category = "dev"
optional = false
python-versions = ">=3.6"
@@ -492,7 +490,7 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.extras]
-test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
+test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"]
[[package]]
name = "ptyprocess"
@@ -502,14 +500,6 @@ category = "dev"
optional = false
python-versions = "*"
-[[package]]
-name = "py"
-version = "1.11.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.*, !=3.4.*"
-
[[package]]
name = "pycodestyle"
version = "2.7.0"
@@ -535,7 +525,7 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
-name = "Pygments"
+name = "pygments"
version = "2.14.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "dev"
@@ -547,25 +537,24 @@ plugins = ["importlib-metadata"]
[[package]]
name = "pytest"
-version = "6.2.5"
+version = "7.2.1"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
[package.dependencies]
-atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
-py = ">=1.8.2"
-toml = "*"
+tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras]
-testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
+testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]]
name = "pytest-cov"
@@ -581,7 +570,7 @@ pytest = ">=4.6"
toml = "*"
[package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
[[package]]
name = "python-gitlab"
@@ -601,7 +590,7 @@ yaml = ["PyYaml (>=5.2)"]
[[package]]
name = "python-semantic-release"
-version = "7.32.2"
+version = "7.33.0"
description = "Automatic Semantic Versioning for Python projects"
category = "dev"
optional = false
@@ -619,13 +608,12 @@ requests = ">=2.25,<3"
semver = ">=2.10,<3"
tomlkit = ">=0.10,<1.0"
twine = ">=3,<4"
-wheel = "*"
[package.extras]
-dev = ["black", "isort", "tox"]
-docs = ["Jinja2 (==3.0.3)", "Sphinx (==1.3.6)"]
+dev = ["tox", "isort", "black"]
+docs = ["Sphinx (==1.3.6)", "Jinja2 (==3.0.3)"]
mypy = ["mypy", "types-requests"]
-test = ["coverage (>=5,<6)", "mock (==1.3.0)", "pytest (>=5,<6)", "pytest-mock (>=2,<3)", "pytest-xdist (>=1,<2)", "responses (==0.13.3)"]
+test = ["coverage (>=5,<6)", "pytest (>=7,<8)", "pytest-xdist (>=1,<2)", "pytest-mock (>=2,<3)", "responses (==0.13.3)", "mock (==1.3.0)"]
[[package]]
name = "pywin32-ctypes"
@@ -653,7 +641,7 @@ md = ["cmarkgfm (>=0.8.0)"]
[[package]]
name = "requests"
-version = "2.28.1"
+version = "2.28.2"
description = "Python HTTP for Humans."
category = "main"
optional = false
@@ -661,7 +649,7 @@ python-versions = ">=3.7, <4"
[package.dependencies]
certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<3"
+charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
@@ -692,7 +680,7 @@ python-versions = ">=3.7"
idna2008 = ["idna"]
[[package]]
-name = "SecretStorage"
+name = "secretstorage"
version = "3.3.3"
description = "Python bindings to FreeDesktop.org Secret Service API"
category = "dev"
@@ -711,19 +699,6 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-[[package]]
-name = "setuptools"
-version = "65.6.3"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-category = "dev"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
-
[[package]]
name = "six"
version = "1.16.0"
@@ -749,7 +724,7 @@ optional = false
python-versions = "*"
[package.extras]
-yaml = ["PyYAML (>=5.1)", "more-itertools"]
+yaml = ["more-itertools", "PyYAML (>=5.1)"]
[[package]]
name = "taskipy"
@@ -808,7 +783,7 @@ telegram = ["requests"]
[[package]]
name = "traitlets"
-version = "5.8.0"
+version = "5.8.1"
description = "Traitlets Python configuration system"
category = "dev"
optional = false
@@ -848,20 +823,20 @@ python-versions = ">=3.7"
[[package]]
name = "urllib3"
-version = "1.26.13"
+version = "1.26.14"
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.*, !=3.5.*"
[package.extras]
-brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
-secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
+brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
+secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "wcwidth"
-version = "0.2.5"
+version = "0.2.6"
description = "Measures the displayed width of unicode strings in a terminal"
category = "dev"
optional = false
@@ -875,17 +850,6 @@ category = "dev"
optional = false
python-versions = "*"
-[[package]]
-name = "wheel"
-version = "0.38.4"
-description = "A built-package format for Python"
-category = "dev"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-test = ["pytest (>=3.0.0)"]
-
[[package]]
name = "yapf"
version = "0.30.0"
@@ -903,464 +867,86 @@ optional = false
python-versions = ">=3.7"
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
-testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "jaraco.functools", "more-itertools", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
-content-hash = "0418105f2bf54e32025584bb913cf422a22714b854ba60b8d8f8c6a6c8345d66"
+content-hash = "2ce3c41b1d2a1222b4e53674ab5901777d35e52bcee9eaeb93ff625c74e14d31"
[metadata.files]
-appnope = [
- {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"},
- {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"},
-]
-atomicwrites = [
- {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
-]
-attrs = [
- {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
- {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
-]
-backcall = [
- {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
- {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
-]
-bleach = [
- {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"},
- {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"},
-]
-certifi = [
- {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
- {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
-]
-cffi = [
- {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
- {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
- {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
- {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
- {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
- {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
- {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
- {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
- {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
- {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
- {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
- {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
- {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
- {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
- {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
- {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
- {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
- {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
- {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
-]
-charset-normalizer = [
- {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
- {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
-]
-click = [
- {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
- {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
-]
-click-log = [
- {file = "click-log-0.4.0.tar.gz", hash = "sha256:3970f8570ac54491237bcdb3d8ab5e3eef6c057df29f8c3d1151a51a9c23b975"},
- {file = "click_log-0.4.0-py2.py3-none-any.whl", hash = "sha256:a43e394b528d52112af599f2fc9e4b7cf3c15f94e53581f74fa6867e68c91756"},
-]
-colorama = [
- {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
- {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
-]
-coverage = [
- {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
- {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
- {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
- {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
- {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
- {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
- {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
- {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
- {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
- {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
- {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
- {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
- {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
- {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
- {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
- {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
- {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
- {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
- {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
- {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
- {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
- {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
- {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
- {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
- {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
- {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
- {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
- {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
- {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
- {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
- {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
- {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
- {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
- {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
- {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
- {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
- {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
- {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
- {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
- {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
- {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
- {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
- {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
- {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
- {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
- {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
- {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
- {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
- {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
-]
-cryptography = [
- {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288"},
- {file = "cryptography-39.0.0-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72"},
- {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9"},
- {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f"},
- {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b"},
- {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c"},
- {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96"},
- {file = "cryptography-39.0.0-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717"},
- {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df"},
- {file = "cryptography-39.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1"},
- {file = "cryptography-39.0.0-cp36-abi3-win32.whl", hash = "sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de"},
- {file = "cryptography-39.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e"},
- {file = "cryptography-39.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb"},
- {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f"},
- {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458"},
- {file = "cryptography-39.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190"},
- {file = "cryptography-39.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773"},
- {file = "cryptography-39.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856"},
- {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f"},
- {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce"},
- {file = "cryptography-39.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8"},
- {file = "cryptography-39.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39"},
- {file = "cryptography-39.0.0.tar.gz", hash = "sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf"},
-]
-decorator = [
- {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
- {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
-]
-docutils = [
- {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"},
- {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"},
-]
-dotty-dict = [
- {file = "dotty_dict-1.3.1-py3-none-any.whl", hash = "sha256:5022d234d9922f13aa711b4950372a06a6d64cb6d6db9ba43d0ba133ebfce31f"},
- {file = "dotty_dict-1.3.1.tar.gz", hash = "sha256:4b016e03b8ae265539757a53eba24b9bfda506fb94fbce0bee843c6f05541a15"},
-]
-flake8 = [
- {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
- {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
-]
-gitdb = [
- {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"},
- {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"},
-]
-GitPython = [
- {file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"},
- {file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"},
-]
-idna = [
- {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
- {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
-]
-importlib-metadata = [
- {file = "importlib_metadata-6.0.0-py3-none-any.whl", hash = "sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad"},
- {file = "importlib_metadata-6.0.0.tar.gz", hash = "sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d"},
-]
-importlib-resources = [
- {file = "importlib_resources-5.10.2-py3-none-any.whl", hash = "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6"},
- {file = "importlib_resources-5.10.2.tar.gz", hash = "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484"},
-]
-iniconfig = [
- {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
- {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
-]
-invoke = [
- {file = "invoke-1.7.3-py3-none-any.whl", hash = "sha256:d9694a865764dd3fd91f25f7e9a97fb41666e822bbb00e670091e3f43933574d"},
- {file = "invoke-1.7.3.tar.gz", hash = "sha256:41b428342d466a82135d5ab37119685a989713742be46e42a3a399d685579314"},
-]
-ipdb = [
- {file = "ipdb-0.13.11-py3-none-any.whl", hash = "sha256:f74c2f741c18b909eaf89f19fde973f745ac721744aa1465888ce45813b63a9c"},
- {file = "ipdb-0.13.11.tar.gz", hash = "sha256:c23b6736f01fd4586cc2ecbebdf79a5eb454796853e1cd8f2ed3b7b91d4a3e93"},
-]
-ipython = [
- {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"},
- {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"},
-]
-"jaraco.classes" = [
- {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"},
- {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"},
-]
-jedi = [
- {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"},
- {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"},
-]
-jeepney = [
- {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"},
- {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"},
-]
-keyring = [
- {file = "keyring-23.13.1-py3-none-any.whl", hash = "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd"},
- {file = "keyring-23.13.1.tar.gz", hash = "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"},
-]
-matplotlib-inline = [
- {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
- {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
-]
-mccabe = [
- {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
- {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
-]
-more-itertools = [
- {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"},
- {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"},
-]
-mslex = [
- {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"},
- {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"},
-]
-packaging = [
- {file = "packaging-22.0-py3-none-any.whl", hash = "sha256:957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3"},
- {file = "packaging-22.0.tar.gz", hash = "sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3"},
-]
-parso = [
- {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
- {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
-]
-pexpect = [
- {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
- {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
-]
-pickleshare = [
- {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
- {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
-]
-pkginfo = [
- {file = "pkginfo-1.9.4-py3-none-any.whl", hash = "sha256:7fc056f8e6b93355925083373b0f6bfbabe84ee3f29854fd155c9d6a4211267d"},
- {file = "pkginfo-1.9.4.tar.gz", hash = "sha256:e769fd353593d43e0c9f47e17e25f09a8efcddcdf9a71674ea3ba444ff31bb44"},
-]
-pluggy = [
- {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
- {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
-]
-prompt-toolkit = [
- {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"},
- {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"},
-]
-psutil = [
- {file = "psutil-5.9.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c1ca331af862803a42677c120aff8a814a804e09832f166f226bfd22b56feee8"},
- {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:68908971daf802203f3d37e78d3f8831b6d1014864d7a85937941bb35f09aefe"},
- {file = "psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"},
- {file = "psutil-5.9.4-cp27-cp27m-win32.whl", hash = "sha256:852dd5d9f8a47169fe62fd4a971aa07859476c2ba22c2254d4a1baa4e10b95ad"},
- {file = "psutil-5.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:9120cd39dca5c5e1c54b59a41d205023d436799b1c8c4d3ff71af18535728e94"},
- {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"},
- {file = "psutil-5.9.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:efeae04f9516907be44904cc7ce08defb6b665128992a56957abc9b61dca94b7"},
- {file = "psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"},
- {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16653106f3b59386ffe10e0bad3bb6299e169d5327d3f187614b1cb8f24cf2e1"},
- {file = "psutil-5.9.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54c0d3d8e0078b7666984e11b12b88af2db11d11249a8ac8920dd5ef68a66e08"},
- {file = "psutil-5.9.4-cp36-abi3-win32.whl", hash = "sha256:149555f59a69b33f056ba1c4eb22bb7bf24332ce631c44a319cec09f876aaeff"},
- {file = "psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"},
- {file = "psutil-5.9.4-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:6001c809253a29599bc0dfd5179d9f8a5779f9dffea1da0f13c53ee568115e1e"},
- {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"},
-]
-ptyprocess = [
- {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
- {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
-]
-py = [
- {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
- {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
-]
-pycodestyle = [
- {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
- {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
-]
-pycparser = [
- {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
- {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
-]
-pyflakes = [
- {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
- {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
-]
-Pygments = [
- {file = "Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"},
- {file = "Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"},
-]
-pytest = [
- {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
- {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
-]
-pytest-cov = [
- {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"},
- {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
-]
-python-gitlab = [
- {file = "python-gitlab-3.12.0.tar.gz", hash = "sha256:567390c2b93690dae62ed9738bf9f221fa45c01378fdf896089dbf7c8a134fbd"},
- {file = "python_gitlab-3.12.0-py3-none-any.whl", hash = "sha256:a5eb36b49783fda34563376674d5251dbbdbd1abd23b287dadf82f67d861b2c1"},
-]
-python-semantic-release = [
- {file = "python-semantic-release-7.32.2.tar.gz", hash = "sha256:4d8f5d20680723e1329765b6f3e28b43f058fd1ef5f423f6f95397cd927c3ebc"},
- {file = "python_semantic_release-7.32.2-py3-none-any.whl", hash = "sha256:9fcf82f403b91a61e58728ea05e2e2e25010ce9ed07830fe78251819b4b834d9"},
-]
-pywin32-ctypes = [
- {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
- {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
-]
-readme-renderer = [
- {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"},
- {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"},
-]
-requests = [
- {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
- {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
-]
-requests-toolbelt = [
- {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"},
- {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"},
-]
-rfc3986 = [
- {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"},
- {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"},
-]
-SecretStorage = [
- {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"},
- {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"},
-]
-semver = [
- {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"},
- {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"},
-]
-setuptools = [
- {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"},
- {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"},
-]
-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"},
-]
-"tap.py" = [
- {file = "tap.py-3.1-py3-none-any.whl", hash = "sha256:928c852f3361707b796c93730cc5402c6378660b161114461066acf53d65bf5d"},
- {file = "tap.py-3.1.tar.gz", hash = "sha256:3c0cd45212ad5a25b35445964e2517efa000a118a1bfc3437dae828892eaf1e1"},
-]
-taskipy = [
- {file = "taskipy-1.10.3-py3-none-any.whl", hash = "sha256:4c0070ca53868d97989f7ab5c6f237525d52ee184f9b967576e8fe427ed9d0b8"},
- {file = "taskipy-1.10.3.tar.gz", hash = "sha256:112beaf21e3d5569950b99162a1de003fa885fabee9e450757a6b874be914877"},
-]
-toml = [
- {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
- {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-tomli = [
- {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
- {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
-]
-tomlkit = [
- {file = "tomlkit-0.11.6-py3-none-any.whl", hash = "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b"},
- {file = "tomlkit-0.11.6.tar.gz", hash = "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73"},
-]
-tqdm = [
- {file = "tqdm-4.64.1-py2.py3-none-any.whl", hash = "sha256:6fee160d6ffcd1b1c68c65f14c829c22832bc401726335ce92c52d395944a6a1"},
- {file = "tqdm-4.64.1.tar.gz", hash = "sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4"},
-]
-traitlets = [
- {file = "traitlets-5.8.0-py3-none-any.whl", hash = "sha256:c864831efa0ba6576d09b44884b34e41defc18c0d7e720b4a2d6698c842cab3e"},
- {file = "traitlets-5.8.0.tar.gz", hash = "sha256:6cc57d6dc28c85d5365961726ffd19b538739347749e13ebe34e03323a0e8f84"},
-]
-twine = [
- {file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"},
- {file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"},
-]
-typing-extensions = [
- {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
- {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
-]
-urllib3 = [
- {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"},
- {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"},
-]
-wcwidth = [
- {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
- {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
-]
-webencodings = [
- {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
- {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
-]
-wheel = [
- {file = "wheel-0.38.4-py3-none-any.whl", hash = "sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8"},
- {file = "wheel-0.38.4.tar.gz", hash = "sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac"},
-]
-yapf = [
- {file = "yapf-0.30.0-py2.py3-none-any.whl", hash = "sha256:3abf61ba67cf603069710d30acbc88cfe565d907e16ad81429ae90ce9651e0c9"},
- {file = "yapf-0.30.0.tar.gz", hash = "sha256:3000abee4c28daebad55da6c85f3cd07b8062ce48e2e9943c8da1b9667d48427"},
-]
-zipp = [
- {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"},
- {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"},
-]
+appnope = []
+attrs = []
+backcall = []
+bleach = []
+certifi = []
+cffi = []
+charset-normalizer = []
+click = []
+click-log = []
+colorama = []
+coverage = []
+cryptography = []
+decorator = []
+docutils = []
+dotty-dict = []
+exceptiongroup = []
+flake8 = []
+gitdb = []
+gitpython = []
+idna = []
+importlib-metadata = []
+importlib-resources = []
+iniconfig = []
+invoke = []
+ipdb = []
+ipython = []
+"jaraco.classes" = []
+jedi = []
+jeepney = []
+keyring = []
+matplotlib-inline = []
+mccabe = []
+more-itertools = []
+mslex = []
+packaging = []
+parso = []
+pexpect = []
+pickleshare = []
+pkginfo = []
+pluggy = []
+prompt-toolkit = []
+psutil = []
+ptyprocess = []
+pycodestyle = []
+pycparser = []
+pyflakes = []
+pygments = []
+pytest = []
+pytest-cov = []
+python-gitlab = []
+python-semantic-release = []
+pywin32-ctypes = []
+readme-renderer = []
+requests = []
+requests-toolbelt = []
+rfc3986 = []
+secretstorage = []
+semver = []
+six = []
+smmap = []
+"tap.py" = []
+taskipy = []
+toml = []
+tomli = []
+tomlkit = []
+tqdm = []
+traitlets = []
+twine = []
+typing-extensions = []
+urllib3 = []
+wcwidth = []
+webencodings = []
+yapf = []
+zipp = []
diff --git a/pyproject.toml b/pyproject.toml
index 3b661cd..72f00e8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -22,7 +22,7 @@ coverage = "^5.4"
ipdb = "^0.13.4"
flake8 = "^3.8.4"
yapf = "^0.30.0"
-pytest = "^6.2.2"
+pytest = "^7.2.0"
pytest-cov = "^2.11.1"
taskipy = "^1.6.0"
python-semantic-release = "^7.28.1"
From 6ea7450cb0400d37a79c371822fa4c7ac0818f32 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Fri, 27 Jan 2023 16:53:21 +0000
Subject: [PATCH 40/55] release: Version 1.18.5 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a58667..e779eb2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.5 (2023-01-27)
+### Fix
+* **chore:** Upgraded pytest to resolve security vulnerability in py <= 1.11.0 ([#92](https://github.com/logdna/python/issues/92)) ([`905b06a`](https://github.com/logdna/python/commit/905b06a648e19896087b0c51ba1e055212727560))
+
## v1.18.4 (2023-01-06)
### Fix
* Dependabot -> Vulnerabilities -> cryptography >= 37.0.0 < 38.0.3 ([#91](https://github.com/logdna/python/issues/91)) ([`7ccde50`](https://github.com/logdna/python/commit/7ccde50ba50c0abbec1a7efe4dd665e8b35511c0))
diff --git a/logdna/VERSION b/logdna/VERSION
index a67b05e..8e8b0a9 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.4
+1.18.5
diff --git a/pyproject.toml b/pyproject.toml
index 72f00e8..353354e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.4"
+version = "1.18.5"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 712d81d4ab2bfbf95d65898fb365a5bcb1396199 Mon Sep 17 00:00:00 2001
From: Dmitri Khokhlov
Date: Fri, 27 Jan 2023 14:04:28 -0800
Subject: [PATCH 41/55] fix: added retries for http 429 504 (#93)
- added retries for http 429 504
LOG-11814
---
logdna/logdna.py | 14 +++++++++++--
tests/test_logger.py | 48 +++++++++++++++++++++++++++++++++++++++++---
2 files changed, 57 insertions(+), 5 deletions(-)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index a430a2d..7597f45 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -212,8 +212,10 @@ def send_request(self, data): # noqa: max-complexity: 13
3XX unexpected status
401, 403 expected client error,
invalid ingestion key
+ 429 expected server error,
+ "client error", transient
4XX unexpected client error
- 500 502 503 507 expected server error, transient
+ 500 502 503 504 507 expected server error, transient
5XX unexpected server error
handling:
expected status discard flush buffer
@@ -256,6 +258,14 @@ def send_request(self, data): # noqa: max-complexity: 13
'Error Response: %s', response.text)
return True # discard
+ if status_code == 429:
+ self.internalLogger.debug('Client Error: %s. Retrying...',
+ reason)
+ if self.log_error_response:
+ self.internalLogger.debug(
+ 'Error Response: %s', response.text)
+ return False # retry
+
if 400 <= status_code <= 499:
self.internalLogger.debug('Client Error: %s. ' +
'Discarding flush buffer',
@@ -265,7 +275,7 @@ def send_request(self, data): # noqa: max-complexity: 13
'Error Response: %s', response.text)
return True # discard
- if status_code in [500, 502, 503, 507]:
+ if status_code in [500, 502, 503, 504, 507]:
self.internalLogger.debug('Server Error: %s. Retrying...',
reason)
if self.log_error_response:
diff --git a/tests/test_logger.py b/tests/test_logger.py
index bb177e2..6ee1a08 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -171,7 +171,49 @@ def test_try_request_500(self):
with patch('requests.post') as post_mock:
r = requests.Response()
r.status_code = 500
- r.reason = 'OK'
+ r.reason = 'Internal Server Error'
+ post_mock.return_value = r
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ self.assertTrue(handler.exception_flag)
+ self.assertTrue(post_mock.call_count, 3)
+
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
+ def test_try_request_502(self):
+ with patch('requests.post') as post_mock:
+ r = requests.Response()
+ r.status_code = 502
+ r.reason = 'Bad Gateway'
+ post_mock.return_value = r
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ self.assertTrue(handler.exception_flag)
+ self.assertTrue(post_mock.call_count, 3)
+
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
+ def test_try_request_504(self):
+ with patch('requests.post') as post_mock:
+ r = requests.Response()
+ r.status_code = 504
+ r.reason = 'Gateway Timeout'
+ post_mock.return_value = r
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ sample_message['timestamp'] = unittest.mock.ANY
+ handler.buf = [sample_message]
+ handler.try_request()
+ self.assertTrue(handler.exception_flag)
+ self.assertTrue(post_mock.call_count, 3)
+
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
+ def test_try_request_429(self):
+ with patch('requests.post') as post_mock:
+ r = requests.Response()
+ r.status_code = 429
+ r.reason = 'Too Many Requests'
post_mock.return_value = r
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
@@ -185,7 +227,7 @@ def test_try_request_403(self):
with patch('requests.post') as post_mock:
r = requests.Response()
r.status_code = 403
- r.reason = 'OK'
+ r.reason = 'Forbidden'
post_mock.return_value = r
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
@@ -199,7 +241,7 @@ def test_try_request_403_log_response(self):
with patch('requests.post') as post_mock:
r = requests.Response()
r.status_code = 403
- r.reason = 'OK'
+ r.reason = 'Forbidden'
post_mock.return_value = r
sample_options['log_error_response'] = True
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
From 1d43ec6c0314bcc9b6dfe037376abc11494838ad Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Fri, 27 Jan 2023 22:08:39 +0000
Subject: [PATCH 42/55] release: Version 1.18.6 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e779eb2..d7fc86d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.6 (2023-01-27)
+### Fix
+* Added retries for http 429 504 ([#93](https://github.com/logdna/python/issues/93)) ([`712d81d`](https://github.com/logdna/python/commit/712d81d4ab2bfbf95d65898fb365a5bcb1396199))
+
## v1.18.5 (2023-01-27)
### Fix
* **chore:** Upgraded pytest to resolve security vulnerability in py <= 1.11.0 ([#92](https://github.com/logdna/python/issues/92)) ([`905b06a`](https://github.com/logdna/python/commit/905b06a648e19896087b0c51ba1e055212727560))
diff --git a/logdna/VERSION b/logdna/VERSION
index 8e8b0a9..04a8bc2 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.5
+1.18.6
diff --git a/pyproject.toml b/pyproject.toml
index 353354e..34dc1c3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.5"
+version = "1.18.6"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 97803b55a102539a75b0763891c1d27757460067 Mon Sep 17 00:00:00 2001
From: Eric Satterwhite
Date: Fri, 5 May 2023 16:25:53 -0500
Subject: [PATCH 43/55] fix: gate buils from non maintainers
adds a gate to the build that prevents builds from auto triggering from people outside the org
---
Jenkinsfile | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/Jenkinsfile b/Jenkinsfile
index f4f00ee..a516452 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -22,6 +22,17 @@ pipeline {
}
stages {
+ stage('Validate PR Source') {
+ when {
+ expression { env.CHANGE_FORK }
+ not {
+ triggeredBy 'issueCommentCause'
+ }
+ }
+ steps {
+ error("A maintainer needs to approve this PR for CI by commenting")
+ }
+ }
stage('Test') {
steps {
From 90d5cdfb9f189d0b4c38e633df70fd95046f66df Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Fri, 5 May 2023 21:34:03 +0000
Subject: [PATCH 44/55] release: Version 1.18.7 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d7fc86d..cbec101 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.7 (2023-05-05)
+### Fix
+* Gate buils from non maintainers ([`97803b5`](https://github.com/logdna/python/commit/97803b55a102539a75b0763891c1d27757460067))
+
## v1.18.6 (2023-01-27)
### Fix
* Added retries for http 429 504 ([#93](https://github.com/logdna/python/issues/93)) ([`712d81d`](https://github.com/logdna/python/commit/712d81d4ab2bfbf95d65898fb365a5bcb1396199))
diff --git a/logdna/VERSION b/logdna/VERSION
index 04a8bc2..d6f3a38 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.6
+1.18.7
diff --git a/pyproject.toml b/pyproject.toml
index 34dc1c3..188319d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.6"
+version = "1.18.7"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 033a63f7043e2e48889be23a7e90446ba47cb71b Mon Sep 17 00:00:00 2001
From: Stephen Hawkins <139154814+shawkinsmezmo@users.noreply.github.com>
Date: Mon, 17 Jul 2023 15:12:19 -0700
Subject: [PATCH 45/55] LOG-15414 - Fix race condition that results in dropped
logs (#100)
* Fix race condition that results in dropped logs
* Address PR feedback: simplify locking semantics around buffer access
* Address PR feedback: Remove exception flag, minor test improvements
---------
Co-authored-by: Stephen Hawkins
---
logdna/logdna.py | 144 ++++++++++++-----------
tests/test_logger.py | 268 +++++++++++++++++++++++--------------------
2 files changed, 221 insertions(+), 191 deletions(-)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 7597f45..3a5043a 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -53,9 +53,7 @@ def __init__(self, key, options={}):
# Set the Flush-related Variables
self.buf = []
self.buf_size = 0
- self.secondary = []
- self.exception_flag = False
- self.flusher = None
+
self.include_standard_meta = options.get('include_standard_meta', None)
if self.include_standard_meta is not None:
@@ -78,14 +76,23 @@ def __init__(self, key, options={}):
self.setLevel(logging.DEBUG)
self.lock = threading.RLock()
- def start_flusher(self):
- if not self.flusher:
- self.flusher = threading.Timer(self.flush_interval_secs,
- self.flush)
- self.flusher.start()
+ # Start the flusher
+ self.flusher_stopped = threading.Event()
+ self.flusher = threading.Timer(
+ self.flush_interval_secs, self.flush_timer_worker)
+ self.flusher.start()
+
+ def flush_timer_worker(self):
+ while not self.flusher_stopped.wait(self.flush_interval_secs):
+ try:
+ self.flush()
+ except Exception as e:
+ self.internalLogger.exception(
+ f'Error in flush_timer_worker: {e}')
def close_flusher(self):
if self.flusher:
+ self.flusher_stopped.set()
self.flusher.cancel()
self.flusher = None
@@ -99,72 +106,61 @@ def buffer_log(self, message):
self.internalLogger.debug('Error in calling buffer_log: %s', e)
def buffer_log_sync(self, message):
- # Attempt to acquire lock to write to buf
- # otherwise write to secondary as flush occurs
- if self.lock.acquire(blocking=False):
- msglen = len(message['line'])
- if self.buf_size + msglen < self.buf_retention_limit:
- self.buf.append(message)
- self.buf_size += msglen
- else:
- self.internalLogger.debug(
- 'The buffer size exceeded the limit: %s',
- self.buf_retention_limit)
-
- if self.buf_size >= self.flush_limit and not self.exception_flag:
- self.close_flusher()
- self.flush()
- else:
- self.start_flusher()
- self.lock.release()
- else:
- self.secondary.append(message)
+ # Attempt to acquire lock to write to buffer
+ if self.lock.acquire(blocking=True):
+ try:
+ msglen = len(message['line'])
+ if self.buf_size + msglen < self.buf_retention_limit:
+ self.buf.append(message)
+ self.buf_size += msglen
+ else:
+ self.internalLogger.debug(
+ 'The buffer size exceeded the limit: %s',
+ self.buf_retention_limit)
- def clean_after_success(self):
- self.close_flusher()
- self.buf.clear()
- self.buf_size = 0
- self.exception_flag = False
+ if self.buf_size >= self.flush_limit:
+ self.flush()
+ except Exception as e:
+ self.internalLogger.exception(f'Error in buffer_log_sync: {e}')
+ finally:
+ self.lock.release()
def flush(self):
- if self.worker_thread_pool:
+ self.schedule_flush_sync()
+
+ def schedule_flush_sync(self, should_block=False):
+ if self.request_thread_pool:
try:
- self.worker_thread_pool.submit(self.flush_sync)
+ self.request_thread_pool.submit(
+ self.try_lock_and_do_flush_request, should_block)
except RuntimeError:
- self.flush_sync()
+ self.try_lock_and_do_flush_request(should_block)
except Exception as e:
- self.internalLogger.debug('Error in calling flush: %s', e)
-
- def flush_sync(self):
- if self.buf_size == 0 and len(self.secondary) == 0:
- return
+ self.internalLogger.debug(
+ 'Error in calling try_lock_and_do_flush_request: %s', e)
- if self.lock.acquire(blocking=False):
- if self.request_thread_pool:
- try:
- self.request_thread_pool.submit(self.try_request)
- except RuntimeError:
- self.try_request()
- except Exception as e:
- self.internalLogger.debug(
- 'Error in calling try_request: %s', e)
- finally:
- self.lock.release()
- else:
+ def try_lock_and_do_flush_request(self, should_block=False):
+ local_buf = []
+ if self.lock.acquire(blocking=should_block):
+ if not self.buf:
self.lock.release()
- else:
- self.close_flusher()
- self.start_flusher()
-
- def try_request(self):
- self.buf.extend(self.secondary)
- self.secondary = []
- data = {'e': 'ls', 'ls': self.buf}
+ return
+
+ if self.buf:
+ local_buf = self.buf.copy()
+ self.buf.clear()
+ self.buf_size = 0
+ self.lock.release()
+
+ if local_buf:
+ self.try_request(local_buf)
+
+ def try_request(self, buf):
+ data = {'e': 'ls', 'ls': buf}
retries = 0
while retries < self.max_retry_attempts:
retries += 1
if self.send_request(data):
- self.clean_after_success()
break
sleep_time = self.retry_interval_secs * (1 << (retries - 1))
@@ -175,8 +171,6 @@ def try_request(self):
self.internalLogger.debug(
'Flush exceeded %s tries. Discarding flush buffer',
self.max_retry_attempts)
- self.close_flusher()
- self.exception_flag = True
def send_request(self, data): # noqa: max-complexity: 13
"""
@@ -331,11 +325,29 @@ def emit(self, record):
self.buffer_log(message)
def close(self):
- self.close_flusher()
- self.flush_sync()
+ # First gracefully shut down any threads that are still attempting
+ # to add log messages to the buffer. This ensures that we don't lose
+ # any log messages that are in the process of being added to the
+ # buffer.
if self.worker_thread_pool:
self.worker_thread_pool.shutdown(wait=True)
self.worker_thread_pool = None
+
+ # Now that we've shut down the worker threads, we can safely close
+ # the flusher thread.
+ self.close_flusher()
+
+ # Manually force a flush of any remaining log messages in the buffer.
+ # We block here to ensure that the flush completes prior to the
+ # application exiting and because the probability of this
+ # introducing a noticeable delay is very low because close() is only
+ # called when the logger and application are shutting down.
+ self.schedule_flush_sync(should_block=True)
+
+ # Finally, shut down the thread pool that was used to send the log
+ # messages to the server. We can assume at this point that all log
+ # messages that were in the buffer prior to the worker threads
+ # shutting down have been sent to the server.
if self.request_thread_pool:
self.request_thread_pool.shutdown(wait=True)
self.request_thread_pool = None
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 6ee1a08..e88b7ce 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -71,99 +71,100 @@ def shutdown(self, wait=True):
class LogDNAHandlerTest(unittest.TestCase):
+ def setUp(self):
+ self.handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+
+ def tearDown(self):
+ self.handler.close()
def test_handler(self):
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- self.assertIsInstance(handler, logging.Handler)
- self.assertIsInstance(handler.internal_handler, logging.StreamHandler)
- self.assertIsNotNone(handler.internalLogger)
- self.assertEqual(handler.key, LOGDNA_API_KEY)
- self.assertEqual(handler.hostname, sample_options['hostname'])
- self.assertEqual(handler.ip, sample_options['ip'])
- self.assertEqual(handler.mac, sample_options['mac'])
- self.assertEqual(handler.loglevel, 'info')
- self.assertEqual(handler.app, '')
- self.assertEqual(handler.env, '')
- self.assertEqual(handler.tags, sample_options['tags'].split(','))
- self.assertEqual(handler.custom_fields, defaults['META_FIELDS'])
+ self.assertIsInstance(self.handler, logging.Handler)
+ self.assertIsInstance(
+ self.handler.internal_handler, logging.StreamHandler)
+ self.assertIsNotNone(self.handler.internalLogger)
+ self.assertEqual(self.handler.key, LOGDNA_API_KEY)
+ self.assertEqual(self.handler.hostname, sample_options['hostname'])
+ self.assertEqual(self.handler.ip, sample_options['ip'])
+ self.assertEqual(self.handler.mac, sample_options['mac'])
+ self.assertEqual(self.handler.loglevel, 'info')
+ self.assertEqual(self.handler.app, '')
+ self.assertEqual(self.handler.env, '')
+ self.assertEqual(self.handler.tags, sample_options['tags'].split(','))
+ self.assertEqual(self.handler.custom_fields, defaults['META_FIELDS'])
# Set the Connection Variables
- self.assertEqual(handler.url, defaults['LOGDNA_URL'])
- self.assertEqual(handler.request_timeout,
+ self.assertEqual(self.handler.url, defaults['LOGDNA_URL'])
+ self.assertEqual(self.handler.request_timeout,
defaults['DEFAULT_REQUEST_TIMEOUT'])
- self.assertEqual(handler.user_agent, defaults['USER_AGENT'])
- self.assertEqual(handler.max_retry_attempts,
+ self.assertEqual(self.handler.user_agent, defaults['USER_AGENT'])
+ self.assertEqual(self.handler.max_retry_attempts,
defaults['MAX_RETRY_ATTEMPTS'])
- self.assertEqual(handler.max_retry_jitter,
+ self.assertEqual(self.handler.max_retry_jitter,
defaults['MAX_RETRY_JITTER'])
- self.assertEqual(handler.max_concurrent_requests,
+ self.assertEqual(self.handler.max_concurrent_requests,
defaults['MAX_CONCURRENT_REQUESTS'])
- self.assertEqual(handler.retry_interval_secs,
+ self.assertEqual(self.handler.retry_interval_secs,
sample_options['retry_interval_secs'])
# Set the Flush-related Variables
- self.assertEqual(handler.buf, [])
- self.assertEqual(handler.buf_size, 0)
- self.assertEqual(handler.secondary, [])
- self.assertFalse(handler.exception_flag)
- self.assertIsNone(handler.flusher)
- self.assertTrue(handler.index_meta)
- self.assertEqual(handler.flush_limit, defaults['FLUSH_LIMIT'])
- self.assertEqual(handler.flush_interval_secs,
+ self.assertEqual(self.handler.buf, [])
+ self.assertEqual(self.handler.buf_size, 0)
+ self.assertIsNotNone(self.handler.flusher)
+ self.assertTrue(self.handler.index_meta)
+ self.assertEqual(self.handler.flush_limit, defaults['FLUSH_LIMIT'])
+ self.assertEqual(self.handler.flush_interval_secs,
defaults['FLUSH_INTERVAL_SECS'])
- self.assertEqual(handler.buf_retention_limit,
+ self.assertEqual(self.handler.buf_retention_limit,
defaults['BUF_RETENTION_LIMIT'])
# Set up the Thread Pools
- self.assertIsInstance(handler.worker_thread_pool, ThreadPoolExecutor)
- self.assertIsInstance(handler.request_thread_pool, ThreadPoolExecutor)
- self.assertEqual(handler.level, logging.DEBUG)
+ self.assertIsInstance(
+ self.handler.worker_thread_pool, ThreadPoolExecutor)
+ self.assertIsInstance(
+ self.handler.request_thread_pool, ThreadPoolExecutor)
+ self.assertEqual(self.handler.level, logging.DEBUG)
def test_flusher(self):
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- self.assertIsNone(handler.flusher)
- handler.start_flusher()
- self.assertIsNotNone(handler.flusher)
- handler.close_flusher()
- self.assertIsNone(handler.flusher)
+ self.assertIsNotNone(self.handler.flusher)
+ self.handler.close_flusher()
+ self.assertIsNone(self.handler.flusher)
+ self.assertTrue(self.handler.flusher_stopped)
def test_emit(self):
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- handler.buffer_log = unittest.mock.Mock()
- handler.emit(sample_record)
+ self.handler.buffer_log = unittest.mock.Mock()
+ self.handler.emit(sample_record)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buffer_log.assert_called_once_with(sample_message)
+ self.handler.buffer_log.assert_called_once_with(sample_message)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
- def test_try_request(self):
+ def test_try_lock_and_do_flush_request(self):
with patch('requests.post') as post_mock:
r = requests.Response()
r.status_code = 200
r.reason = 'OK'
post_mock.return_value = r
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
+ self.handler.buf = [sample_message]
+ test_buf = self.handler.buf.copy()
+ self.handler.try_lock_and_do_flush_request()
post_mock.assert_called_with(
- url=handler.url,
+ url=self.handler.url,
json={
'e': 'ls',
- 'ls': handler.buf
+ 'ls': test_buf
},
- auth=('user', handler.key),
+ auth=('user', self.handler.key),
params={
- 'hostname': handler.hostname,
- 'ip': handler.ip,
- 'mac': handler.mac,
- 'tags': handler.tags,
+ 'hostname': self.handler.hostname,
+ 'ip': self.handler.ip,
+ 'mac': self.handler.mac,
+ 'tags': self.handler.tags,
'now': int(now * 1000)
},
stream=True,
allow_redirects=True,
- timeout=handler.request_timeout,
- headers={'user-agent': handler.user_agent})
- self.assertFalse(handler.exception_flag)
+ timeout=self.handler.request_timeout,
+ headers={'user-agent': self.handler.user_agent})
self.assertTrue(post_mock.call_count, 1)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
@@ -173,11 +174,9 @@ def test_try_request_500(self):
r.status_code = 500
r.reason = 'Internal Server Error'
post_mock.return_value = r
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
- self.assertTrue(handler.exception_flag)
+ self.handler.buf = [sample_message]
+ self.handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
@@ -187,11 +186,9 @@ def test_try_request_502(self):
r.status_code = 502
r.reason = 'Bad Gateway'
post_mock.return_value = r
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
- self.assertTrue(handler.exception_flag)
+ self.handler.buf = [sample_message]
+ self.handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
@@ -201,11 +198,9 @@ def test_try_request_504(self):
r.status_code = 504
r.reason = 'Gateway Timeout'
post_mock.return_value = r
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
- self.assertTrue(handler.exception_flag)
+ self.handler.buf = [sample_message]
+ self.handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
@@ -215,11 +210,9 @@ def test_try_request_429(self):
r.status_code = 429
r.reason = 'Too Many Requests'
post_mock.return_value = r
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
- self.assertTrue(handler.exception_flag)
+ self.handler.buf = [sample_message]
+ self.handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
@@ -229,11 +222,9 @@ def test_try_request_403(self):
r.status_code = 403
r.reason = 'Forbidden'
post_mock.return_value = r
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
- self.assertFalse(handler.exception_flag)
+ self.handler.buf = [sample_message]
+ self.handler.try_request([])
self.assertTrue(post_mock.call_count, 1)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
@@ -244,64 +235,91 @@ def test_try_request_403_log_response(self):
r.reason = 'Forbidden'
post_mock.return_value = r
sample_options['log_error_response'] = True
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
sample_message['timestamp'] = unittest.mock.ANY
- handler.buf = [sample_message]
- handler.try_request()
- self.assertFalse(handler.exception_flag)
+ self.handler.buf = [sample_message]
+ self.handler.try_request([])
self.assertTrue(post_mock.call_count, 1)
def test_close(self):
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- handler.close_flusher = unittest.mock.Mock()
- handler.flush_sync = unittest.mock.Mock()
- handler.close()
- handler.close_flusher.assert_called_once_with()
- handler.flush_sync.assert_called_once_with()
- self.assertIsNone(handler.worker_thread_pool)
- self.assertIsNone(handler.request_thread_pool)
+ close_flusher_mock = unittest.mock.Mock()
+ close_flusher_mock.side_effect = self.handler.close_flusher
+ self.handler.close_flusher = close_flusher_mock
+ self.handler.schedule_flush_sync = unittest.mock.Mock()
+ self.handler.close()
+ self.handler.close_flusher.assert_called_once_with()
+ self.handler.schedule_flush_sync.assert_called_once_with(
+ should_block=True)
+ self.assertIsNone(self.handler.worker_thread_pool)
+ self.assertIsNone(self.handler.request_thread_pool)
def test_flush(self):
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- handler.worker_thread_pool = MockThreadPoolExecutor()
- handler.request_thread_pool = MockThreadPoolExecutor()
- handler.buf_size += 1
- handler.try_request = unittest.mock.Mock()
- handler.flush()
- handler.try_request.assert_called_once_with()
-
- def test_flush_secondary(self):
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- clean = handler.clean_after_success
- handler.worker_thread_pool = MockThreadPoolExecutor()
- handler.request_thread_pool = MockThreadPoolExecutor()
- handler.buf_size = 0
- handler.secondary.append(sample_message)
- handler.send_request = unittest.mock.MagicMock(return_value=True)
- handler.clean_after_success = unittest.mock.Mock()
- self.assertEqual(handler.secondary, [sample_message])
- handler.flush()
- handler.send_request.assert_called_with({
- 'e': 'ls',
- 'ls': [sample_message]
- })
- clean()
- self.assertEqual(handler.secondary, [])
- self.assertEqual(handler.buf, [])
+ self.handler.worker_thread_pool = MockThreadPoolExecutor()
+ self.handler.request_thread_pool = MockThreadPoolExecutor()
+ self.handler.buf = [sample_message]
+ self.handler.buf_size += len(self.handler.buf)
+ self.handler.try_request = unittest.mock.Mock()
+ self.handler.flush()
+ self.handler.try_request.assert_called_once_with([sample_message])
def test_buffer_log(self):
- handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
- handler.worker_thread_pool = MockThreadPoolExecutor()
- handler.request_thread_pool = MockThreadPoolExecutor()
- handler.close_flusher = unittest.mock.Mock()
- handler.flush = unittest.mock.Mock()
+ self.handler.worker_thread_pool = MockThreadPoolExecutor()
+ self.handler.request_thread_pool = MockThreadPoolExecutor()
+ self.handler.flush = unittest.mock.Mock()
sample_message['timestamp'] = unittest.mock.ANY
- handler.flush_limit = 0
- handler.buffer_log(sample_message)
- handler.close_flusher.assert_called_once_with()
- handler.flush.assert_called_once_with()
- self.assertEqual(handler.buf, [sample_message])
- self.assertEqual(handler.buf_size, len(sample_message['line']))
+ self.handler.flush_limit = 0
+ self.handler.buffer_log(sample_message)
+ self.handler.flush.assert_called_once_with()
+ self.assertEqual(self.handler.buf, [sample_message])
+ self.assertEqual(self.handler.buf_size, len(sample_message['line']))
+
+ # Attempts to reproduce the specific scenario that resulted in
+ # https://mezmo.atlassian.net/browse/LOG-15414 where log messages
+ # would be dropped due to race conditions. The test essentially
+ # does the following:
+ # 1. Create a LogDNAHandler
+ # 2. Call handler.emit() with a large number of log records at a rate
+ # sufficiently high to trigger the race
+ # 3. Verify that no log records are dropped.
+ #
+ # This test is not deterministic, but it should be sufficient to
+ # catch regressions. It reliably reproduces the issue in question
+ # and fails with the previous version of this code.
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
+ def test_when_emitManyLogs_then_noLogsDropped(self):
+ num_logs = 10**5
+ received = list()
+
+ def append_received(json=None, **kwargs):
+ ids = [int(log['line']) for log in json['ls']]
+ for id in ids:
+ received.append(id)
+ r = requests.Response()
+ r.status_code = 200
+ r.reason = 'OK'
+ # Simulate some reasonable request latency
+ time.sleep(0.1)
+ return r
+
+ def get_sample_record(id):
+ return logging.LogRecord(
+ name='test',
+ level=logging.INFO,
+ pathname='test',
+ lineno=5,
+ msg=str(id),
+ args=[sample_args],
+ exc_info='',
+ func='',
+ sinfo='')
+
+ with patch('requests.post', side_effect=append_received):
+
+ for i in range(num_logs):
+ self.handler.emit(get_sample_record(i))
+ self.handler.close()
+
+ self.assertEqual(len(received), num_logs)
+ self.assertEquals(set(received), set(range(num_logs)))
if __name__ == '__main__':
From 913e5f4f35f2920a6b2162022b93495b4774654c Mon Sep 17 00:00:00 2001
From: Stephen Hawkins <139154814+shawkinsmezmo@users.noreply.github.com>
Date: Tue, 18 Jul 2023 10:24:42 -0700
Subject: [PATCH 46/55] fix: Bump semver (#101)
Co-authored-by: Stephen Hawkins
---
tests/test_logger.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/test_logger.py b/tests/test_logger.py
index e88b7ce..7fc0564 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -313,7 +313,6 @@ def get_sample_record(id):
sinfo='')
with patch('requests.post', side_effect=append_received):
-
for i in range(num_logs):
self.handler.emit(get_sample_record(i))
self.handler.close()
From 658f6c5c9933295aaec940a7c57a6b7a5209405b Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Tue, 18 Jul 2023 17:29:34 +0000
Subject: [PATCH 47/55] release: Version 1.18.8 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cbec101..4b335ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.8 (2023-07-18)
+### Fix
+* Bump semver ([#101](https://github.com/logdna/python/issues/101)) ([`913e5f4`](https://github.com/logdna/python/commit/913e5f4f35f2920a6b2162022b93495b4774654c))
+
## v1.18.7 (2023-05-05)
### Fix
* Gate buils from non maintainers ([`97803b5`](https://github.com/logdna/python/commit/97803b55a102539a75b0763891c1d27757460067))
diff --git a/logdna/VERSION b/logdna/VERSION
index d6f3a38..1a31d39 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.7
+1.18.8
diff --git a/pyproject.toml b/pyproject.toml
index 188319d..d62d663 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.7"
+version = "1.18.8"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 17a69b044de43a7a9d7d3e6eb65a0c60f1fa23f0 Mon Sep 17 00:00:00 2001
From: Stephen Hawkins <139154814+shawkinsmezmo@users.noreply.github.com>
Date: Thu, 20 Jul 2023 22:33:10 -0700
Subject: [PATCH 48/55] fix: Make flush thread a daemon thread to prevent
shutdown hang (#102)
Co-authored-by: Stephen Hawkins
---
logdna/logdna.py | 6 +++---
tests/test_logger.py | 5 +++++
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 3a5043a..36aa9e2 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -78,8 +78,8 @@ def __init__(self, key, options={}):
# Start the flusher
self.flusher_stopped = threading.Event()
- self.flusher = threading.Timer(
- self.flush_interval_secs, self.flush_timer_worker)
+ self.flusher = threading.Thread(
+ target=self.flush_timer_worker, daemon=True)
self.flusher.start()
def flush_timer_worker(self):
@@ -93,7 +93,7 @@ def flush_timer_worker(self):
def close_flusher(self):
if self.flusher:
self.flusher_stopped.set()
- self.flusher.cancel()
+ self.flusher.join()
self.flusher = None
def buffer_log(self, message):
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 7fc0564..f195555 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -320,6 +320,11 @@ def get_sample_record(id):
self.assertEqual(len(received), num_logs)
self.assertEquals(set(received), set(range(num_logs)))
+ def test_when_handlerShutDown_then_handlerDoesNotHang(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ self.assertIsNotNone(handler)
+ # Do nothing. This test should pass by virtue of not hanging.
+
if __name__ == '__main__':
unittest.main()
From ceb3545171aa6d44c7d5fa2e1fe90012cae5650a Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Fri, 21 Jul 2023 05:37:03 +0000
Subject: [PATCH 49/55] release: Version 1.18.9 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b335ed..087d85c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.9 (2023-07-21)
+### Fix
+* Make flush thread a daemon thread to prevent shutdown hang ([#102](https://github.com/logdna/python/issues/102)) ([`17a69b0`](https://github.com/logdna/python/commit/17a69b044de43a7a9d7d3e6eb65a0c60f1fa23f0))
+
## v1.18.8 (2023-07-18)
### Fix
* Bump semver ([#101](https://github.com/logdna/python/issues/101)) ([`913e5f4`](https://github.com/logdna/python/commit/913e5f4f35f2920a6b2162022b93495b4774654c))
diff --git a/logdna/VERSION b/logdna/VERSION
index 1a31d39..cafc0b7 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.8
+1.18.9
diff --git a/pyproject.toml b/pyproject.toml
index d62d663..9e67a25 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.8"
+version = "1.18.9"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 5394e9714779878cd415a4566cd44d9183e150b9 Mon Sep 17 00:00:00 2001
From: Stephen Hawkins <139154814+shawkinsmezmo@users.noreply.github.com>
Date: Wed, 26 Jul 2023 08:59:51 -0700
Subject: [PATCH 50/55] fix: Utilize apikey header for auth to make compatible
with pipelines (#104)
Co-authored-by: Stephen Hawkins
---
logdna/logdna.py | 7 +++++--
tests/test_logger.py | 5 +++--
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 36aa9e2..fd55074 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -180,9 +180,12 @@ def send_request(self, data): # noqa: max-complexity: 13
False - retry, keep flush buffer
"""
try:
+ headers = {
+ 'user-agent': self.user_agent,
+ 'apikey': self.key
+ }
response = requests.post(url=self.url,
json=data,
- auth=('user', self.key),
params={
'hostname': self.hostname,
'ip': self.ip,
@@ -193,7 +196,7 @@ def send_request(self, data): # noqa: max-complexity: 13
stream=True,
allow_redirects=True,
timeout=self.request_timeout,
- headers={'user-agent': self.user_agent})
+ headers=headers)
status_code = response.status_code
'''
diff --git a/tests/test_logger.py b/tests/test_logger.py
index f195555..8adacda 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -153,7 +153,6 @@ def test_try_lock_and_do_flush_request(self):
'e': 'ls',
'ls': test_buf
},
- auth=('user', self.handler.key),
params={
'hostname': self.handler.hostname,
'ip': self.handler.ip,
@@ -164,7 +163,9 @@ def test_try_lock_and_do_flush_request(self):
stream=True,
allow_redirects=True,
timeout=self.handler.request_timeout,
- headers={'user-agent': self.handler.user_agent})
+ headers={
+ 'user-agent': self.handler.user_agent,
+ 'apikey': LOGDNA_API_KEY})
self.assertTrue(post_mock.call_count, 1)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
From 8af0c6dcee19037a782310861e267e4d8926082b Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Wed, 26 Jul 2023 16:04:15 +0000
Subject: [PATCH 51/55] release: Version 1.18.10 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 087d85c..44a7ab8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.10 (2023-07-26)
+### Fix
+* Utilize apikey header for auth to make compatible with pipelines ([#104](https://github.com/logdna/python/issues/104)) ([`5394e97`](https://github.com/logdna/python/commit/5394e9714779878cd415a4566cd44d9183e150b9))
+
## v1.18.9 (2023-07-21)
### Fix
* Make flush thread a daemon thread to prevent shutdown hang ([#102](https://github.com/logdna/python/issues/102)) ([`17a69b0`](https://github.com/logdna/python/commit/17a69b044de43a7a9d7d3e6eb65a0c60f1fa23f0))
diff --git a/logdna/VERSION b/logdna/VERSION
index cafc0b7..0150af1 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.9
+1.18.10
diff --git a/pyproject.toml b/pyproject.toml
index 9e67a25..9c28b2e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.9"
+version = "1.18.10"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 0337a09433953227dabb0b65da8583e8f9273986 Mon Sep 17 00:00:00 2001
From: Stephen Hawkins <139154814+shawkinsmezmo@users.noreply.github.com>
Date: Wed, 26 Jul 2023 09:20:32 -0700
Subject: [PATCH 52/55] fix: Remove Thread/event to fix Django regression
(#107)
Co-authored-by: Stephen Hawkins
---
logdna/logdna.py | 47 +++++-----
tests/test_logger.py | 201 +++++++++++++++++++++++--------------------
2 files changed, 132 insertions(+), 116 deletions(-)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index fd55074..6022c07 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -76,24 +76,17 @@ def __init__(self, key, options={}):
self.setLevel(logging.DEBUG)
self.lock = threading.RLock()
- # Start the flusher
- self.flusher_stopped = threading.Event()
- self.flusher = threading.Thread(
- target=self.flush_timer_worker, daemon=True)
- self.flusher.start()
-
- def flush_timer_worker(self):
- while not self.flusher_stopped.wait(self.flush_interval_secs):
- try:
- self.flush()
- except Exception as e:
- self.internalLogger.exception(
- f'Error in flush_timer_worker: {e}')
+ self.flusher = None
+
+ def start_flusher(self):
+ if not self.flusher:
+ self.flusher = threading.Timer(
+ self.flush_interval_secs, self.flush)
+ self.flusher.start()
def close_flusher(self):
if self.flusher:
- self.flusher_stopped.set()
- self.flusher.join()
+ self.flusher.cancel()
self.flusher = None
def buffer_log(self, message):
@@ -119,7 +112,10 @@ def buffer_log_sync(self, message):
self.buf_retention_limit)
if self.buf_size >= self.flush_limit:
+ self.close_flusher()
self.flush()
+ else:
+ self.start_flusher()
except Exception as e:
self.internalLogger.exception(f'Error in buffer_log_sync: {e}')
finally:
@@ -143,13 +139,15 @@ def try_lock_and_do_flush_request(self, should_block=False):
local_buf = []
if self.lock.acquire(blocking=should_block):
if not self.buf:
+ self.close_flusher()
self.lock.release()
return
- if self.buf:
- local_buf = self.buf.copy()
- self.buf.clear()
- self.buf_size = 0
+ local_buf = self.buf.copy()
+ self.buf.clear()
+ self.buf_size = 0
+ if local_buf:
+ self.close_flusher()
self.lock.release()
if local_buf:
@@ -304,10 +302,10 @@ def emit(self, record):
'line': msg,
'level': record['levelname'] or self.loglevel,
'app': self.app or record['module'],
- 'env': self.env
+ 'env': self.env,
+ 'meta': {}
}
- message['meta'] = {}
for key in self.custom_fields:
if key in record:
if isinstance(record[key], tuple):
@@ -328,6 +326,9 @@ def emit(self, record):
self.buffer_log(message)
def close(self):
+ # Close the flusher
+ self.close_flusher()
+
# First gracefully shut down any threads that are still attempting
# to add log messages to the buffer. This ensures that we don't lose
# any log messages that are in the process of being added to the
@@ -336,10 +337,6 @@ def close(self):
self.worker_thread_pool.shutdown(wait=True)
self.worker_thread_pool = None
- # Now that we've shut down the worker threads, we can safely close
- # the flusher thread.
- self.close_flusher()
-
# Manually force a flush of any remaining log messages in the buffer.
# We block here to ensure that the flush completes prior to the
# application exiting and because the probability of this
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 8adacda..6fe68e3 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -71,207 +71,225 @@ def shutdown(self, wait=True):
class LogDNAHandlerTest(unittest.TestCase):
- def setUp(self):
- self.handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
-
- def tearDown(self):
- self.handler.close()
-
def test_handler(self):
- self.assertIsInstance(self.handler, logging.Handler)
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ self.assertIsInstance(handler, logging.Handler)
self.assertIsInstance(
- self.handler.internal_handler, logging.StreamHandler)
- self.assertIsNotNone(self.handler.internalLogger)
- self.assertEqual(self.handler.key, LOGDNA_API_KEY)
- self.assertEqual(self.handler.hostname, sample_options['hostname'])
- self.assertEqual(self.handler.ip, sample_options['ip'])
- self.assertEqual(self.handler.mac, sample_options['mac'])
- self.assertEqual(self.handler.loglevel, 'info')
- self.assertEqual(self.handler.app, '')
- self.assertEqual(self.handler.env, '')
- self.assertEqual(self.handler.tags, sample_options['tags'].split(','))
- self.assertEqual(self.handler.custom_fields, defaults['META_FIELDS'])
+ handler.internal_handler, logging.StreamHandler)
+ self.assertIsNotNone(handler.internalLogger)
+ self.assertEqual(handler.key, LOGDNA_API_KEY)
+ self.assertEqual(handler.hostname, sample_options['hostname'])
+ self.assertEqual(handler.ip, sample_options['ip'])
+ self.assertEqual(handler.mac, sample_options['mac'])
+ self.assertEqual(handler.loglevel, 'info')
+ self.assertEqual(handler.app, '')
+ self.assertEqual(handler.env, '')
+ self.assertEqual(handler.tags, sample_options['tags'].split(','))
+ self.assertEqual(handler.custom_fields, defaults['META_FIELDS'])
# Set the Connection Variables
- self.assertEqual(self.handler.url, defaults['LOGDNA_URL'])
- self.assertEqual(self.handler.request_timeout,
+ self.assertEqual(handler.url, defaults['LOGDNA_URL'])
+ self.assertEqual(handler.request_timeout,
defaults['DEFAULT_REQUEST_TIMEOUT'])
- self.assertEqual(self.handler.user_agent, defaults['USER_AGENT'])
- self.assertEqual(self.handler.max_retry_attempts,
+ self.assertEqual(handler.user_agent, defaults['USER_AGENT'])
+ self.assertEqual(handler.max_retry_attempts,
defaults['MAX_RETRY_ATTEMPTS'])
- self.assertEqual(self.handler.max_retry_jitter,
+ self.assertEqual(handler.max_retry_jitter,
defaults['MAX_RETRY_JITTER'])
- self.assertEqual(self.handler.max_concurrent_requests,
+ self.assertEqual(handler.max_concurrent_requests,
defaults['MAX_CONCURRENT_REQUESTS'])
- self.assertEqual(self.handler.retry_interval_secs,
+ self.assertEqual(handler.retry_interval_secs,
sample_options['retry_interval_secs'])
# Set the Flush-related Variables
- self.assertEqual(self.handler.buf, [])
- self.assertEqual(self.handler.buf_size, 0)
- self.assertIsNotNone(self.handler.flusher)
- self.assertTrue(self.handler.index_meta)
- self.assertEqual(self.handler.flush_limit, defaults['FLUSH_LIMIT'])
- self.assertEqual(self.handler.flush_interval_secs,
+ self.assertEqual(handler.buf, [])
+ self.assertEqual(handler.buf_size, 0)
+ self.assertIsNone(handler.flusher)
+ self.assertTrue(handler.index_meta)
+ self.assertEqual(handler.flush_limit, defaults['FLUSH_LIMIT'])
+ self.assertEqual(handler.flush_interval_secs,
defaults['FLUSH_INTERVAL_SECS'])
- self.assertEqual(self.handler.buf_retention_limit,
+ self.assertEqual(handler.buf_retention_limit,
defaults['BUF_RETENTION_LIMIT'])
# Set up the Thread Pools
self.assertIsInstance(
- self.handler.worker_thread_pool, ThreadPoolExecutor)
+ handler.worker_thread_pool, ThreadPoolExecutor)
self.assertIsInstance(
- self.handler.request_thread_pool, ThreadPoolExecutor)
- self.assertEqual(self.handler.level, logging.DEBUG)
+ handler.request_thread_pool, ThreadPoolExecutor)
+ self.assertEqual(handler.level, logging.DEBUG)
+ @mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_flusher(self):
- self.assertIsNotNone(self.handler.flusher)
- self.handler.close_flusher()
- self.assertIsNone(self.handler.flusher)
- self.assertTrue(self.handler.flusher_stopped)
+ with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ r = requests.Response()
+ r.status_code = 200
+ r.reason = 'OK'
+ post_mock.return_value = r
+ handler.emit(sample_record)
+ self.assertIsNotNone(handler.flusher)
+ handler.close_flusher()
+ self.assertIsNone(handler.flusher)
def test_emit(self):
- self.handler.buffer_log = unittest.mock.Mock()
- self.handler.emit(sample_record)
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ handler.buffer_log = unittest.mock.Mock()
+ handler.emit(sample_record)
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buffer_log.assert_called_once_with(sample_message)
+ handler.buffer_log.assert_called_once_with(sample_message)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_try_lock_and_do_flush_request(self):
with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
r = requests.Response()
r.status_code = 200
r.reason = 'OK'
post_mock.return_value = r
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buf = [sample_message]
- test_buf = self.handler.buf.copy()
- self.handler.try_lock_and_do_flush_request()
+ handler.buf = [sample_message]
+ test_buf = handler.buf.copy()
+ handler.try_lock_and_do_flush_request()
post_mock.assert_called_with(
- url=self.handler.url,
+ url=handler.url,
json={
'e': 'ls',
'ls': test_buf
},
params={
- 'hostname': self.handler.hostname,
- 'ip': self.handler.ip,
- 'mac': self.handler.mac,
- 'tags': self.handler.tags,
+ 'hostname': handler.hostname,
+ 'ip': handler.ip,
+ 'mac': handler.mac,
+ 'tags': handler.tags,
'now': int(now * 1000)
},
stream=True,
allow_redirects=True,
- timeout=self.handler.request_timeout,
+ timeout=handler.request_timeout,
headers={
- 'user-agent': self.handler.user_agent,
+ 'user-agent': handler.user_agent,
'apikey': LOGDNA_API_KEY})
self.assertTrue(post_mock.call_count, 1)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_try_request_500(self):
with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
r = requests.Response()
r.status_code = 500
r.reason = 'Internal Server Error'
post_mock.return_value = r
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buf = [sample_message]
- self.handler.try_request([])
+ handler.buf = [sample_message]
+ handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_try_request_502(self):
with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
r = requests.Response()
r.status_code = 502
r.reason = 'Bad Gateway'
post_mock.return_value = r
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buf = [sample_message]
- self.handler.try_request([])
+ handler.buf = [sample_message]
+ handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_try_request_504(self):
with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
r = requests.Response()
r.status_code = 504
r.reason = 'Gateway Timeout'
post_mock.return_value = r
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buf = [sample_message]
- self.handler.try_request([])
+ handler.buf = [sample_message]
+ handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_try_request_429(self):
with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
r = requests.Response()
r.status_code = 429
r.reason = 'Too Many Requests'
post_mock.return_value = r
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buf = [sample_message]
- self.handler.try_request([])
+ handler.buf = [sample_message]
+ handler.try_request([])
self.assertTrue(post_mock.call_count, 3)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_try_request_403(self):
with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
r = requests.Response()
r.status_code = 403
r.reason = 'Forbidden'
post_mock.return_value = r
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buf = [sample_message]
- self.handler.try_request([])
+ handler.buf = [sample_message]
+ handler.try_request([])
self.assertTrue(post_mock.call_count, 1)
@mock.patch('time.time', unittest.mock.MagicMock(return_value=now))
def test_try_request_403_log_response(self):
with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
r = requests.Response()
r.status_code = 403
r.reason = 'Forbidden'
post_mock.return_value = r
sample_options['log_error_response'] = True
sample_message['timestamp'] = unittest.mock.ANY
- self.handler.buf = [sample_message]
- self.handler.try_request([])
+ handler.buf = [sample_message]
+ handler.try_request([])
self.assertTrue(post_mock.call_count, 1)
def test_close(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
close_flusher_mock = unittest.mock.Mock()
- close_flusher_mock.side_effect = self.handler.close_flusher
- self.handler.close_flusher = close_flusher_mock
- self.handler.schedule_flush_sync = unittest.mock.Mock()
- self.handler.close()
- self.handler.close_flusher.assert_called_once_with()
- self.handler.schedule_flush_sync.assert_called_once_with(
+ close_flusher_mock.side_effect = handler.close_flusher
+ handler.close_flusher = close_flusher_mock
+ handler.schedule_flush_sync = unittest.mock.Mock()
+ handler.close()
+ handler.close_flusher.assert_called_once_with()
+ handler.schedule_flush_sync.assert_called_once_with(
should_block=True)
- self.assertIsNone(self.handler.worker_thread_pool)
- self.assertIsNone(self.handler.request_thread_pool)
+ self.assertIsNone(handler.worker_thread_pool)
+ self.assertIsNone(handler.request_thread_pool)
def test_flush(self):
- self.handler.worker_thread_pool = MockThreadPoolExecutor()
- self.handler.request_thread_pool = MockThreadPoolExecutor()
- self.handler.buf = [sample_message]
- self.handler.buf_size += len(self.handler.buf)
- self.handler.try_request = unittest.mock.Mock()
- self.handler.flush()
- self.handler.try_request.assert_called_once_with([sample_message])
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ handler.worker_thread_pool = MockThreadPoolExecutor()
+ handler.request_thread_pool = MockThreadPoolExecutor()
+ handler.buf = [sample_message]
+ handler.buf_size += len(handler.buf)
+ handler.try_request = unittest.mock.Mock()
+ handler.flush()
+ handler.try_request.assert_called_once_with([sample_message])
def test_buffer_log(self):
- self.handler.worker_thread_pool = MockThreadPoolExecutor()
- self.handler.request_thread_pool = MockThreadPoolExecutor()
- self.handler.flush = unittest.mock.Mock()
- sample_message['timestamp'] = unittest.mock.ANY
- self.handler.flush_limit = 0
- self.handler.buffer_log(sample_message)
- self.handler.flush.assert_called_once_with()
- self.assertEqual(self.handler.buf, [sample_message])
- self.assertEqual(self.handler.buf_size, len(sample_message['line']))
+ with patch('requests.post') as post_mock:
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ r = requests.Response()
+ r.status_code = 200
+ r.reason = 'OK'
+ post_mock.return_value = r
+ handler.worker_thread_pool = MockThreadPoolExecutor()
+ handler.request_thread_pool = MockThreadPoolExecutor()
+ handler.flush = unittest.mock.Mock()
+ sample_message['timestamp'] = now
+ handler.flush_limit = 0
+ handler.buffer_log(sample_message)
+ handler.flush.assert_called_once_with()
+ self.assertEqual(handler.buf, [sample_message])
+ self.assertEqual(handler.buf_size, len(sample_message['line']))
# Attempts to reproduce the specific scenario that resulted in
# https://mezmo.atlassian.net/browse/LOG-15414 where log messages
@@ -314,12 +332,13 @@ def get_sample_record(id):
sinfo='')
with patch('requests.post', side_effect=append_received):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
for i in range(num_logs):
- self.handler.emit(get_sample_record(i))
- self.handler.close()
+ handler.emit(get_sample_record(i))
+ handler.close()
self.assertEqual(len(received), num_logs)
- self.assertEquals(set(received), set(range(num_logs)))
+ self.assertEqual(set(received), set(range(num_logs)))
def test_when_handlerShutDown_then_handlerDoesNotHang(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
From b2af94824823d72caaba66de6118b1c34c64deea Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Wed, 26 Jul 2023 16:22:38 +0000
Subject: [PATCH 53/55] release: Version 1.18.11 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44a7ab8..13e061d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.11 (2023-07-26)
+### Fix
+* Remove Thread/event to fix Django regression ([#107](https://github.com/logdna/python/issues/107)) ([`0337a09`](https://github.com/logdna/python/commit/0337a09433953227dabb0b65da8583e8f9273986))
+
## v1.18.10 (2023-07-26)
### Fix
* Utilize apikey header for auth to make compatible with pipelines ([#104](https://github.com/logdna/python/issues/104)) ([`5394e97`](https://github.com/logdna/python/commit/5394e9714779878cd415a4566cd44d9183e150b9))
diff --git a/logdna/VERSION b/logdna/VERSION
index 0150af1..6961fed 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.10
+1.18.11
diff --git a/pyproject.toml b/pyproject.toml
index 9c28b2e..c84222e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.10"
+version = "1.18.11"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"
From 5b04d72b42686d926fb5e73dadb2d7a1ba16d9c3 Mon Sep 17 00:00:00 2001
From: Stephen Hawkins <139154814+shawkinsmezmo@users.noreply.github.com>
Date: Thu, 27 Jul 2023 14:37:44 -0700
Subject: [PATCH 54/55] fix: Don't overwrite base logging Handler class lock
var (#108)
Co-authored-by: Stephen Hawkins
---
logdna/logdna.py | 13 +++++++------
tests/test_logger.py | 15 ++++++++++++++-
2 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/logdna/logdna.py b/logdna/logdna.py
index 6022c07..e9f6128 100644
--- a/logdna/logdna.py
+++ b/logdna/logdna.py
@@ -74,7 +74,7 @@ def __init__(self, key, options={}):
max_workers=self.max_concurrent_requests)
self.setLevel(logging.DEBUG)
- self.lock = threading.RLock()
+ self._lock = threading.RLock()
self.flusher = None
@@ -100,7 +100,7 @@ def buffer_log(self, message):
def buffer_log_sync(self, message):
# Attempt to acquire lock to write to buffer
- if self.lock.acquire(blocking=True):
+ if self._lock.acquire(blocking=True):
try:
msglen = len(message['line'])
if self.buf_size + msglen < self.buf_retention_limit:
@@ -119,7 +119,7 @@ def buffer_log_sync(self, message):
except Exception as e:
self.internalLogger.exception(f'Error in buffer_log_sync: {e}')
finally:
- self.lock.release()
+ self._lock.release()
def flush(self):
self.schedule_flush_sync()
@@ -137,10 +137,10 @@ def schedule_flush_sync(self, should_block=False):
def try_lock_and_do_flush_request(self, should_block=False):
local_buf = []
- if self.lock.acquire(blocking=should_block):
+ if self._lock.acquire(blocking=should_block):
if not self.buf:
self.close_flusher()
- self.lock.release()
+ self._lock.release()
return
local_buf = self.buf.copy()
@@ -148,7 +148,7 @@ def try_lock_and_do_flush_request(self, should_block=False):
self.buf_size = 0
if local_buf:
self.close_flusher()
- self.lock.release()
+ self._lock.release()
if local_buf:
self.try_request(local_buf)
@@ -351,4 +351,5 @@ def close(self):
if self.request_thread_pool:
self.request_thread_pool.shutdown(wait=True)
self.request_thread_pool = None
+
logging.Handler.close(self)
diff --git a/tests/test_logger.py b/tests/test_logger.py
index 6fe68e3..0a610c2 100644
--- a/tests/test_logger.py
+++ b/tests/test_logger.py
@@ -255,8 +255,8 @@ def test_close(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
close_flusher_mock = unittest.mock.Mock()
close_flusher_mock.side_effect = handler.close_flusher
- handler.close_flusher = close_flusher_mock
handler.schedule_flush_sync = unittest.mock.Mock()
+ handler.close_flusher = close_flusher_mock
handler.close()
handler.close_flusher.assert_called_once_with()
handler.schedule_flush_sync.assert_called_once_with(
@@ -264,6 +264,19 @@ def test_close(self):
self.assertIsNone(handler.worker_thread_pool)
self.assertIsNone(handler.request_thread_pool)
+ # These should be separate objects, since there is already
+ # a variable in the base class named self.lock. We want
+ # to make sure that a separate lock is created for the
+ # locking semantics of the LogDNA Handler
+ def test_lock_var_separate_from_local_lock_var(self):
+ handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
+ self.assertIsNotNone(handler)
+
+ # Test that we did not replace the base class' instance var.
+ self.assertIsNotNone(handler._lock)
+ self.assertIsNotNone(handler.lock)
+ self.assertNotEquals(handler.lock, handler._lock)
+
def test_flush(self):
handler = LogDNAHandler(LOGDNA_API_KEY, sample_options)
handler.worker_thread_pool = MockThreadPoolExecutor()
From 4968bca3f57b6a57af9b64745d6ecb4c1c771a97 Mon Sep 17 00:00:00 2001
From: LogDNA Bot
Date: Thu, 27 Jul 2023 21:39:21 +0000
Subject: [PATCH 55/55] release: Version 1.18.12 [skip ci]
Automatically generated by python-semantic-release
---
CHANGELOG.md | 4 ++++
logdna/VERSION | 2 +-
pyproject.toml | 2 +-
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13e061d..b52389a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
+## v1.18.12 (2023-07-27)
+### Fix
+* Don't overwrite base logging Handler class lock var ([#108](https://github.com/logdna/python/issues/108)) ([`5b04d72`](https://github.com/logdna/python/commit/5b04d72b42686d926fb5e73dadb2d7a1ba16d9c3))
+
## v1.18.11 (2023-07-26)
### Fix
* Remove Thread/event to fix Django regression ([#107](https://github.com/logdna/python/issues/107)) ([`0337a09`](https://github.com/logdna/python/commit/0337a09433953227dabb0b65da8583e8f9273986))
diff --git a/logdna/VERSION b/logdna/VERSION
index 6961fed..43bae12 100644
--- a/logdna/VERSION
+++ b/logdna/VERSION
@@ -1 +1 @@
-1.18.11
+1.18.12
diff --git a/pyproject.toml b/pyproject.toml
index c84222e..241a341 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "logdna"
-version = "1.18.11"
+version = "1.18.12"
description = 'A Python Package for Sending Logs to LogDNA'
authors = ["logdna "]
license = "MIT"