Initial release
This commit is contained in:
parent
1760b496c6
commit
7bcb700dfa
12 changed files with 865 additions and 26 deletions
9
.coveragerc
Normal file
9
.coveragerc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[run]
|
||||
source = ./ip_listener/
|
||||
omit =
|
||||
**/tests/**
|
||||
**/tests.py
|
||||
**/__init__.py
|
||||
|
||||
[html]
|
||||
directory = coverage
|
||||
17
.gitlab-ci.yml
Normal file
17
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
stages:
|
||||
- test
|
||||
- release
|
||||
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
|
||||
cache:
|
||||
key: "$CI_COMMIT_REF_SLUG"
|
||||
paths:
|
||||
- .venv/
|
||||
- .cache/pip
|
||||
- .cache/poetry
|
||||
|
||||
include:
|
||||
- local: '/gitlab-ci/test.yml'
|
||||
- local: '/gitlab-ci/release.yml'
|
||||
10
.isort.cfg
Normal file
10
.isort.cfg
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[settings]
|
||||
include_trailing_comma = true
|
||||
line_length = 88
|
||||
multi_line_output = 3
|
||||
skip = env/, venv/
|
||||
default_section = THIRDPARTY
|
||||
known_first_party = ip_listener
|
||||
sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
|
||||
lines_between_types=1
|
||||
lines_after_imports=2
|
||||
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
## 0.1
|
||||
|
||||
- Initial release
|
||||
12
gitlab/release.yml
Normal file
12
gitlab/release.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
release:
|
||||
stage: release
|
||||
image: registry.gitlab.com/gitlab-org/release-cli:latest
|
||||
rules:
|
||||
- if: $CI_COMMIT_TAG
|
||||
script:
|
||||
- echo 'running release job'
|
||||
release:
|
||||
name: 'Release $CI_COMMIT_TAG'
|
||||
description: './CHANGELOG.md'
|
||||
tag_name: '$CI_COMMIT_TAG'
|
||||
ref: '$CI_COMMIT_TAG'
|
||||
12
gitlab/test.yml
Normal file
12
gitlab/test.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
tests:
|
||||
stage: test
|
||||
coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
|
||||
image: python:3.7
|
||||
before_script:
|
||||
- pip install poetry --quiet
|
||||
- poetry config cache-dir .cache/poetry
|
||||
- poetry config virtualenvs.in-project true
|
||||
- poetry install --no-interaction --quiet
|
||||
script:
|
||||
- poetry run coverage run --module unittest
|
||||
- poetry run coverage report
|
||||
39
ip_listener/__init__.py
Normal file
39
ip_listener/__init__.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import os
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
env_path = Path("..") / ".env"
|
||||
|
||||
load_dotenv(dotenv_path=env_path)
|
||||
|
||||
|
||||
def get_current_version():
|
||||
if "VERSION" in os.environ:
|
||||
return os.environ["VERSION"]
|
||||
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["git", "describe", "--tags"], universal_newlines=True
|
||||
)
|
||||
return output.strip()
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return ""
|
||||
|
||||
|
||||
VERSION = get_current_version()
|
||||
|
||||
# Optionally use sentry integration
|
||||
try:
|
||||
from sentry_sdk import init as sentry_init
|
||||
|
||||
sentry_init(
|
||||
dsn=os.environ.get("SENTRY_DSN"),
|
||||
send_default_pii=False,
|
||||
release=VERSION,
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
21
ip_listener/cli.py
Normal file
21
ip_listener/cli.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import click
|
||||
|
||||
from ip_listener.main import detect
|
||||
|
||||
DEFAULT_DNS = "myip.opendns.com"
|
||||
DEFAULT_DNS_NAME = "@resolver1.opendns.com"
|
||||
DEFAULT_API_URL = "https://api.transip.nl/v6"
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("domains", envvar="DOMAINS", nargs=-1)
|
||||
@click.argument("token", envvar="TOKEN")
|
||||
@click.option("--dns", envvar="DNS", default=DEFAULT_DNS)
|
||||
@click.option("--dns-name", envvar="DNS_NAME", default=DEFAULT_DNS_NAME)
|
||||
@click.option("--api-url", envvar="API_URL", default=DEFAULT_API_URL)
|
||||
@click.option("--read-only/--write", envvar="READ_ONLY", default=False)
|
||||
def run(domains, token, dns, dns_name, api_url, read_only):
|
||||
if not domains:
|
||||
raise ValueError("No domain(s) specified")
|
||||
|
||||
detect(domains, (dns, dns_name), api_url, token, read_only)
|
||||
103
ip_listener/main.py
Normal file
103
ip_listener/main.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
import requests
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_ip(resolvers):
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["dig", "+short", *resolvers],
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise OSError("Unable to retrieve current IP") from e
|
||||
|
||||
return output.decode("utf-8").strip()
|
||||
|
||||
|
||||
def _get_domain(domain, token, api_url):
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
return requests.get(f"{api_url}/domains/{domain}/dns", headers=headers)
|
||||
|
||||
|
||||
def _get_domain_data(domains, token, api_url):
|
||||
with ThreadPoolExecutor(max_workers=10) as executor:
|
||||
futures = {
|
||||
executor.submit(_get_domain, domain, token, api_url): domain
|
||||
for domain in domains
|
||||
}
|
||||
|
||||
for future in as_completed(futures):
|
||||
response = future.result()
|
||||
domain = futures[future]
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except requests.HTTPError as e:
|
||||
logger.exception(f"Failed retrieving information for {domain}")
|
||||
continue
|
||||
|
||||
yield {"domain": domain, **response.json()}
|
||||
|
||||
|
||||
def _update_domain(domain, payload, api_url, token):
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
return requests.put(
|
||||
f"{api_url}/domains/{domain}/dns", data=json.dumps(payload), headers=headers
|
||||
)
|
||||
|
||||
|
||||
def _update_domains(updated_domains, api_url, token, read_only):
|
||||
if read_only:
|
||||
return
|
||||
|
||||
with ThreadPoolExecutor(max_workers=10) as executor:
|
||||
futures = {
|
||||
executor.submit(_update_domain, domain, entries, api_url, token): domain
|
||||
for domain, entries in updated_domains.items()
|
||||
}
|
||||
|
||||
for future in as_completed(futures):
|
||||
response = future.result()
|
||||
domain = futures[future]
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except request.HTTPError as e:
|
||||
logger.exception(f"Unable to update domain {domain}")
|
||||
continue
|
||||
|
||||
logger.info(f"Updated domain {domain}")
|
||||
|
||||
|
||||
def detect(domains, resolvers, api_url, token, read_only):
|
||||
ip = _get_ip(resolvers)
|
||||
domain_data = _get_domain_data(domains, token, api_url)
|
||||
updated_domains = {}
|
||||
|
||||
for data in domain_data:
|
||||
dns_entries = data["dnsEntries"]
|
||||
domain = data["domain"]
|
||||
updated_entries = []
|
||||
|
||||
for dns_entry in dns_entries:
|
||||
updated_entries.append(
|
||||
{
|
||||
**dns_entry,
|
||||
"content": ip,
|
||||
}
|
||||
)
|
||||
|
||||
if dns_entries == updated_entries:
|
||||
continue
|
||||
|
||||
updated_domains[domain] = {"dnsEntries": updated_entries}
|
||||
|
||||
_update_domains(updated_domains, api_url, token, read_only)
|
||||
405
ip_listener/tests.py
Normal file
405
ip_listener/tests.py
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
import json
|
||||
|
||||
from unittest import TestCase, skipIf
|
||||
from unittest.mock import call, patch
|
||||
|
||||
from click.testing import CliRunner
|
||||
from pkg_resources import get_distribution
|
||||
from requests import HTTPError
|
||||
|
||||
from ip_listener.cli import DEFAULT_API_URL, run
|
||||
|
||||
|
||||
class RunTestCase(TestCase):
|
||||
def setUp(self):
|
||||
patcher = patch("ip_listener.main.subprocess.check_output")
|
||||
self.mocked_dns = patcher.start()
|
||||
|
||||
patcher = patch("ip_listener.main.requests.get")
|
||||
self.mocked_get = patcher.start()
|
||||
|
||||
patcher = patch("ip_listener.main.requests.put")
|
||||
self.mocked_put = patcher.start()
|
||||
|
||||
self.runner = CliRunner()
|
||||
|
||||
def test_simple(self):
|
||||
self.mocked_dns.return_value = b"111.420\n"
|
||||
self.mocked_get.return_value.json.return_value = {
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.421",
|
||||
}
|
||||
],
|
||||
"_links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com/dns",
|
||||
},
|
||||
{
|
||||
"rel": "domain",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
with self.assertLogs("ip_listener.main", level="INFO") as logger:
|
||||
result = self.runner.invoke(run, ["foobar.com", "TOKEN"])
|
||||
|
||||
self.assertEqual(
|
||||
logger.output, ["INFO:ip_listener.main:Updated domain foobar.com"]
|
||||
)
|
||||
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
self.mocked_get.assert_called_with(
|
||||
f"{DEFAULT_API_URL}/domains/foobar.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
expected_json = json.dumps(
|
||||
{
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.420",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
self.mocked_put.assert_called_with(
|
||||
f"{DEFAULT_API_URL}/domains/foobar.com/dns",
|
||||
data=expected_json,
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
def test_error_response(self):
|
||||
self.mocked_dns.return_value = b"111.420\n"
|
||||
self.mocked_get.return_value.raise_for_status.side_effect = HTTPError
|
||||
|
||||
with self.assertLogs("ip_listener.main", level="INFO") as logger:
|
||||
result = self.runner.invoke(run, ["foobar.com", "TOKEN"])
|
||||
|
||||
error_log = logger.output[0]
|
||||
|
||||
self.assertIn("Failed retrieving information for foobar.com", error_log)
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
self.mocked_get.assert_called_with(
|
||||
f"{DEFAULT_API_URL}/domains/foobar.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
self.mocked_put.assert_not_called()
|
||||
|
||||
def test_matching_ip(self):
|
||||
self.mocked_dns.return_value = b"111.420\n"
|
||||
self.mocked_get.return_value.json.return_value = {
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.420",
|
||||
}
|
||||
],
|
||||
"_links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com/dns",
|
||||
},
|
||||
{
|
||||
"rel": "domain",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
result = self.runner.invoke(run, ["foobar.com", "TOKEN"])
|
||||
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
self.mocked_get.assert_called_with(
|
||||
f"{DEFAULT_API_URL}/domains/foobar.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
self.mocked_put.assert_not_called()
|
||||
|
||||
def test_readonly(self):
|
||||
self.mocked_dns.return_value = b"111.420\n"
|
||||
self.mocked_get.return_value.json.return_value = {
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.421",
|
||||
}
|
||||
],
|
||||
"_links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com/dns",
|
||||
},
|
||||
{
|
||||
"rel": "domain",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
result = self.runner.invoke(run, ["foobar.com", "TOKEN", "--read-only"])
|
||||
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
self.mocked_get.assert_called_with(
|
||||
f"{DEFAULT_API_URL}/domains/foobar.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
self.mocked_put.assert_not_called()
|
||||
|
||||
def test_different_api_url(self):
|
||||
self.mocked_dns.return_value = b"111.420\n"
|
||||
self.mocked_get.return_value.json.return_value = {
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.421",
|
||||
}
|
||||
],
|
||||
"_links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com/dns",
|
||||
},
|
||||
{
|
||||
"rel": "domain",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
with self.assertLogs("ip_listener.main", level="INFO") as logger:
|
||||
result = self.runner.invoke(
|
||||
run, ["foobar.com", "TOKEN", "--api-url", "https://other-provider.com"]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
logger.output, ["INFO:ip_listener.main:Updated domain foobar.com"]
|
||||
)
|
||||
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
self.mocked_get.assert_called_with(
|
||||
f"https://other-provider.com/domains/foobar.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
expected_json = json.dumps(
|
||||
{
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.420",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
self.mocked_put.assert_called_with(
|
||||
f"https://other-provider.com/domains/foobar.com/dns",
|
||||
data=expected_json,
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
def test_env_var(self):
|
||||
self.mocked_dns.return_value = b"111.420\n"
|
||||
self.mocked_get.return_value.json.return_value = {
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.421",
|
||||
}
|
||||
],
|
||||
"_links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com/dns",
|
||||
},
|
||||
{
|
||||
"rel": "domain",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
with self.assertLogs("ip_listener.main", level="INFO") as logger:
|
||||
result = self.runner.invoke(
|
||||
run, ["foobar.com", "TOKEN"], env={"API_URL": "https://new-api.com"}
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
logger.output, ["INFO:ip_listener.main:Updated domain foobar.com"]
|
||||
)
|
||||
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
self.mocked_get.assert_called_with(
|
||||
"https://new-api.com/domains/foobar.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
expected_json = json.dumps(
|
||||
{
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.420",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
self.mocked_put.assert_called_with(
|
||||
"https://new-api.com/domains/foobar.com/dns",
|
||||
data=expected_json,
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
)
|
||||
|
||||
# see https://click.palletsprojects.com/en/master/changelog/#version-8-0
|
||||
@skipIf(
|
||||
get_distribution("click").version < "8",
|
||||
"Multiple arguments through env variables not supported",
|
||||
)
|
||||
def test_multi_arg_env_var(self):
|
||||
self.mocked_dns.return_value = b"111.420\n"
|
||||
self.mocked_get.return_value.json.side_effect = [
|
||||
{
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.421",
|
||||
}
|
||||
],
|
||||
"_links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com/dns",
|
||||
},
|
||||
{
|
||||
"rel": "domain",
|
||||
"link": "https://api.transip.nl/v6/domains/foobar.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.421",
|
||||
}
|
||||
],
|
||||
"_links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"link": "https://api.transip.nl/v6/domains/foofoo.com/dns",
|
||||
},
|
||||
{
|
||||
"rel": "domain",
|
||||
"link": "https://api.transip.nl/v6/domains/foofoo.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
with self.assertLogs("ip_listener.main", level="INFO") as logger:
|
||||
result = self.runner.invoke(
|
||||
run, ["TOKEN"], env={"DOMAINS": "foobar.com foofoo.com"}
|
||||
)
|
||||
|
||||
self.assertIsNone(result.exception)
|
||||
self.assertEqual(
|
||||
logger.output,
|
||||
[
|
||||
"INFO:ip_listener.main:Updated domain foobar.com",
|
||||
"INFO:ip_listener.main:Updated domain foofoo.com",
|
||||
],
|
||||
)
|
||||
|
||||
self.assertEqual(result.exit_code, 0)
|
||||
|
||||
expected_calls = [
|
||||
call(
|
||||
f"{DEFAULT_API_URL}/domains/foobar.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
),
|
||||
call().raise_for_status(),
|
||||
call().json(),
|
||||
call(
|
||||
f"{DEFAULT_API_URL}/domains/foofoo.com/dns",
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
),
|
||||
call().raise_for_status(),
|
||||
call().json(),
|
||||
]
|
||||
|
||||
self.mocked_get.assert_has_calls(expected_calls)
|
||||
|
||||
expected_json = json.dumps(
|
||||
{
|
||||
"dnsEntries": [
|
||||
{
|
||||
"name": "@",
|
||||
"expire": 60,
|
||||
"type": "A",
|
||||
"content": "111.420",
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
expected_calls = [
|
||||
call(
|
||||
f"{DEFAULT_API_URL}/domains/foobar.com/dns",
|
||||
data=expected_json,
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
),
|
||||
call().raise_for_status(),
|
||||
call(
|
||||
f"{DEFAULT_API_URL}/domains/foofoo.com/dns",
|
||||
data=expected_json,
|
||||
headers={"Authorization": "Bearer TOKEN"},
|
||||
),
|
||||
call().raise_for_status(),
|
||||
]
|
||||
|
||||
self.mocked_put.assert_has_calls(expected_calls)
|
||||
|
||||
def test_no_domains(self):
|
||||
result = self.runner.invoke(run, ["TOKEN"])
|
||||
|
||||
self.assertEqual(result.exit_code, 1)
|
||||
self.assertEqual(str(result.exception), "No domain(s) specified")
|
||||
|
||||
self.mocked_get.assert_not_called()
|
||||
self.mocked_put.assert_not_called()
|
||||
248
poetry.lock
generated
248
poetry.lock
generated
|
|
@ -39,6 +39,22 @@ toml = ">=0.10.1"
|
|||
typed-ast = ">=1.4.0"
|
||||
typing-extensions = ">=3.7.4"
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2020.12.5"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "4.0.0"
|
||||
description = "Universal encoding detector for Python 2 and 3"
|
||||
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"
|
||||
|
|
@ -47,9 +63,28 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "5.3.1"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
toml = ["toml"]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "2.10"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.6.4"
|
||||
version = "5.7.0"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
|
@ -84,6 +119,17 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "0.15.0"
|
||||
description = "Add .env support to your django/flask apps in development and deployments"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "2020.11.13"
|
||||
|
|
@ -92,6 +138,52 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.25.1"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[package.extras]
|
||||
security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
|
||||
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2017.4.17"
|
||||
chardet = ">=3.0.2,<5"
|
||||
idna = ">=2.5,<3"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "0.19.5"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
category = "main"
|
||||
optional = true
|
||||
python-versions = "*"
|
||||
|
||||
[package.extras]
|
||||
aiohttp = ["aiohttp (>=3.5)"]
|
||||
beam = ["apache-beam (>=2.12)"]
|
||||
bottle = ["bottle (>=0.12.13)"]
|
||||
celery = ["celery (>=3)"]
|
||||
chalice = ["chalice (>=1.16.0)"]
|
||||
django = ["django (>=1.8)"]
|
||||
falcon = ["falcon (>=1.4)"]
|
||||
flask = ["flask (>=0.11)", "blinker (>=1.1)"]
|
||||
pure_eval = ["pure-eval", "executing", "asttokens"]
|
||||
pyspark = ["pyspark (>=2.4.4)"]
|
||||
rq = ["rq (>=0.6)"]
|
||||
sanic = ["sanic (>=0.8)"]
|
||||
sqlalchemy = ["sqlalchemy (>=1.2)"]
|
||||
tornado = ["tornado (>=5)"]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
urllib3 = ">=1.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
|
|
@ -102,7 +194,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
|||
|
||||
[[package]]
|
||||
name = "typed-ast"
|
||||
version = "1.4.1"
|
||||
version = "1.4.2"
|
||||
description = "a fork of Python 2 and 3 ast modules with type comment support"
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
|
@ -116,10 +208,26 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.2"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
|
||||
|
||||
[extras]
|
||||
sentry = ["sentry_sdk"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.0"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "7bf49667b7bd270cfd8e9773ac7d6d5cbfbbc55b60b8b2b569eb6bea6a288349"
|
||||
content-hash = "c068a970d2c0953cbb7ec9a46be718dc83ebac533aea3ac6687014638ee5b1fd"
|
||||
|
||||
[metadata.files]
|
||||
appdirs = [
|
||||
|
|
@ -132,13 +240,76 @@ autoflake = [
|
|||
black = [
|
||||
{file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
|
||||
{file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"},
|
||||
{file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"},
|
||||
{file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"},
|
||||
{file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"},
|
||||
{file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"},
|
||||
{file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"},
|
||||
{file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"},
|
||||
{file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"},
|
||||
{file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"},
|
||||
{file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"},
|
||||
{file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"},
|
||||
{file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"},
|
||||
{file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"},
|
||||
{file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"},
|
||||
{file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"},
|
||||
{file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"},
|
||||
{file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"},
|
||||
{file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"},
|
||||
{file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"},
|
||||
{file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"},
|
||||
{file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"},
|
||||
{file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"},
|
||||
{file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"},
|
||||
{file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"},
|
||||
{file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"},
|
||||
{file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"},
|
||||
{file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"},
|
||||
{file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"},
|
||||
{file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"},
|
||||
{file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"},
|
||||
{file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"},
|
||||
{file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"},
|
||||
{file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"},
|
||||
{file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"},
|
||||
{file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"},
|
||||
{file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"},
|
||||
{file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"},
|
||||
{file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"},
|
||||
{file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"},
|
||||
{file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"},
|
||||
{file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"},
|
||||
{file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"},
|
||||
{file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"},
|
||||
{file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"},
|
||||
{file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"},
|
||||
{file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"},
|
||||
{file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"},
|
||||
{file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"},
|
||||
{file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
||||
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
||||
]
|
||||
isort = [
|
||||
{file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"},
|
||||
{file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"},
|
||||
{file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"},
|
||||
{file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||
|
|
@ -152,6 +323,10 @@ pyflakes = [
|
|||
{file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
|
||||
{file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
|
||||
]
|
||||
python-dotenv = [
|
||||
{file = "python-dotenv-0.15.0.tar.gz", hash = "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"},
|
||||
{file = "python_dotenv-0.15.0-py2.py3-none-any.whl", hash = "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e"},
|
||||
]
|
||||
regex = [
|
||||
{file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"},
|
||||
{file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"},
|
||||
|
|
@ -195,35 +370,56 @@ regex = [
|
|||
{file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"},
|
||||
{file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
|
||||
{file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
|
||||
]
|
||||
sentry-sdk = [
|
||||
{file = "sentry-sdk-0.19.5.tar.gz", hash = "sha256:737a094e49a529dd0fdcaafa9e97cf7c3d5eb964bd229821d640bc77f3502b3f"},
|
||||
{file = "sentry_sdk-0.19.5-py2.py3-none-any.whl", hash = "sha256:0a711ec952441c2ec89b8f5d226c33bc697914f46e876b44a4edd3e7864cf4d0"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
typed-ast = [
|
||||
{file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"},
|
||||
{file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"},
|
||||
{file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"},
|
||||
{file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"},
|
||||
{file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"},
|
||||
{file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"},
|
||||
{file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"},
|
||||
{file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"},
|
||||
{file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"},
|
||||
{file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"},
|
||||
{file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"},
|
||||
{file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"},
|
||||
{file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"},
|
||||
{file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"},
|
||||
{file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"},
|
||||
{file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"},
|
||||
{file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"},
|
||||
{file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"},
|
||||
{file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"},
|
||||
{file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"},
|
||||
{file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"},
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"},
|
||||
{file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"},
|
||||
{file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"},
|
||||
{file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"},
|
||||
{file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"},
|
||||
{file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"},
|
||||
]
|
||||
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"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"},
|
||||
{file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,11 +8,21 @@ license = "GPL-3.0"
|
|||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
click = "^7.1.2"
|
||||
python-dotenv = "^0.15.0"
|
||||
requests = "^2.25.1"
|
||||
sentry_sdk = {version = "^0.19.5", optional = true}
|
||||
|
||||
[tool.poetry.extras]
|
||||
sentry = ["sentry_sdk"]
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^20.8b1"
|
||||
isort = "^5.6.4"
|
||||
autoflake = "^1.4"
|
||||
coverage = "^5.3.1"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
listen = "ip_listener.cli:run"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue