From b95c44e3db69034d1eb3fcca4362eaa6fbcdf298 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Tue, 29 Sep 2020 19:17:56 +0200 Subject: [PATCH 01/33] Add dot after DKIM To prevent the domain name being added after it --- app/dashboard/templates/dashboard/domain_detail/dns.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/dashboard/templates/dashboard/domain_detail/dns.html b/app/dashboard/templates/dashboard/domain_detail/dns.html index d869b28d..0fdc7f2d 100644 --- a/app/dashboard/templates/dashboard/domain_detail/dns.html +++ b/app/dashboard/templates/dashboard/domain_detail/dns.html @@ -178,7 +178,7 @@ title="Click to copy" class="clipboard" data-clipboard-text="{{ dkim_cname }}" style="overflow-wrap: break-word"> - {{ dkim_cname }} + {{ dkim_cname }}. From 89c91f38434603d0ff758e6528f42bab1164b12a Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 30 Sep 2020 03:23:40 +0000 Subject: [PATCH 02/33] fix: upgrade @sentry/browser from 5.21.4 to 5.22.3 Snyk has created this PR to upgrade @sentry/browser from 5.21.4 to 5.22.3. See this package in npm: https://www.npmjs.com/package/@sentry/browser See this project in Snyk: https://app.snyk.io/org/nguyenkims/project/72f25afd-ac84-4504-a9bd-dc5ead29b930?utm_source=github&utm_medium=upgrade-pr --- static/package-lock.json | 60 ++++++++++++++++++++-------------------- static/package.json | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/static/package-lock.json b/static/package-lock.json index 37de73e7..95daaa11 100644 --- a/static/package-lock.json +++ b/static/package-lock.json @@ -5,59 +5,59 @@ "requires": true, "dependencies": { "@sentry/browser": { - "version": "5.21.4", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.21.4.tgz", - "integrity": "sha512-/bRGMNjJc4Qt9Me9qLobZe0pREUAMFQAR7GOF9HbgzxUc49qVvmPRglvwzwhPJ6XKPg0NH/C6MOn+yuIRjfMag==", + "version": "5.22.3", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.22.3.tgz", + "integrity": "sha512-2TzE/CoBa5ZkvxJizDdi1Iz1ldmXSJpFQ1mL07PIXBjCt0Wxf+WOuFSj5IP4L40XHfJE5gU8wEvSH0VDR8nXtA==", "requires": { - "@sentry/core": "5.21.4", - "@sentry/types": "5.21.4", - "@sentry/utils": "5.21.4", + "@sentry/core": "5.22.3", + "@sentry/types": "5.22.3", + "@sentry/utils": "5.22.3", "tslib": "^1.9.3" } }, "@sentry/core": { - "version": "5.21.4", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.21.4.tgz", - "integrity": "sha512-2hB0shKL6RUuLqqmnDUPvwiV25OSnchxkJ6NbLqnn2DYLqLARfZuVcw2II4wb/Jlw7SDnbkQIPs0/ax7GPe1Nw==", + "version": "5.22.3", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.22.3.tgz", + "integrity": "sha512-eGL5uUarw3o4i9QUb9JoFHnhriPpWCaqeaIBB06HUpdcvhrjoowcKZj1+WPec5lFg5XusE35vez7z/FPzmJUDw==", "requires": { - "@sentry/hub": "5.21.4", - "@sentry/minimal": "5.21.4", - "@sentry/types": "5.21.4", - "@sentry/utils": "5.21.4", + "@sentry/hub": "5.22.3", + "@sentry/minimal": "5.22.3", + "@sentry/types": "5.22.3", + "@sentry/utils": "5.22.3", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.21.4", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.21.4.tgz", - "integrity": "sha512-bgEgBHK6OWoAkrnYwVsIOw+sR4MWpe5/CB7H7r+GBJsSnBysncbSaBgndKmtb1GTWdzMxMlvXU16zC6TR5JX5Q==", + "version": "5.22.3", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.22.3.tgz", + "integrity": "sha512-INo47m6N5HFEs/7GMP9cqxOIt7rmRxdERunA3H2L37owjcr77MwHVeeJ9yawRS6FMtbWXplgWTyTIWIYOuqVbw==", "requires": { - "@sentry/types": "5.21.4", - "@sentry/utils": "5.21.4", + "@sentry/types": "5.22.3", + "@sentry/utils": "5.22.3", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.21.4", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.21.4.tgz", - "integrity": "sha512-pIpIH2ZTwdijGTw6VwfkTETAEoc9k/Aejz6mAjFDMzlOPb3bCx+W8EbGzFOxuwOsiE84bysd2UPVgFY4YSLV/g==", + "version": "5.22.3", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.22.3.tgz", + "integrity": "sha512-HoINpYnVYCpNjn2XIPIlqH5o4BAITpTljXjtAftOx6Hzj+Opjg8tR8PWliyKDvkXPpc4kXK9D6TpEDw8MO0wZA==", "requires": { - "@sentry/hub": "5.21.4", - "@sentry/types": "5.21.4", + "@sentry/hub": "5.22.3", + "@sentry/types": "5.22.3", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "5.21.4", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.21.4.tgz", - "integrity": "sha512-uJTRxW//NPO0UJJzRQOtYHg5tiSBvn1dRk5FvURXmeXt9d9XtwmRhHWDwI51uAkyv+51tun3v+0OZQfLvAI+gQ==" + "version": "5.22.3", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.22.3.tgz", + "integrity": "sha512-cv+VWK0YFgCVDvD1/HrrBWOWYG3MLuCUJRBTkV/Opdy7nkdNjhCAJQrEyMM9zX0sac8FKWKOHT0sykNh8KgmYw==" }, "@sentry/utils": { - "version": "5.21.4", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.21.4.tgz", - "integrity": "sha512-zY8OvaE/lU+DCzTSFrDZNXZmBLM/0URUlyYD4RubqzrgKY/eP1pSbEsDzYYhc+OrBr8TjG66N+5T3gMZX0BfNg==", + "version": "5.22.3", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.22.3.tgz", + "integrity": "sha512-AHNryXMBvIkIE+GQxTlmhBXD0Ksh+5w1SwM5qi6AttH+1qjWLvV6WB4+4pvVvEoS8t5F+WaVUZPQLmCCWp6zKw==", "requires": { - "@sentry/types": "5.21.4", + "@sentry/types": "5.22.3", "tslib": "^1.9.3" } }, diff --git a/static/package.json b/static/package.json index d7e7c7c3..84d83819 100644 --- a/static/package.json +++ b/static/package.json @@ -16,7 +16,7 @@ }, "homepage": "https://github.com/simple-login/app#readme", "dependencies": { - "@sentry/browser": "^5.21.4", + "@sentry/browser": "^5.22.3", "bootbox": "^5.4.0", "font-awesome": "^4.7.0", "intro.js": "^2.9.3", From 19c61fa6561f0483f99d50eaa4ac656aed1d5d55 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 10:19:22 +0200 Subject: [PATCH 03/33] upgrade sentry-sdk to 0.18.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b197925b..4e5c97d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -117,7 +117,7 @@ requests==2.22.0 # via facebook-sdk, requests-oauthlib rsa==4.0 # via google-auth ruamel.yaml==0.15.97 # via strictyaml s3transfer==0.2.1 # via boto3 -sentry-sdk==0.14.1 # via -r requirements.in +sentry-sdk==0.18.0 # via -r requirements.in simplejson==3.17.0 # via flask-profiler six==1.12.0 # via bcrypt, cryptography, flask-cors, flask-limiter, google-api-python-client, google-auth, limits, packaging, pip-tools, prompt-toolkit, pyopenssl, pytest, python-dateutil, sqlalchemy-utils, traitlets, virtualenv, webauthn sqlalchemy-utils==0.36.1 # via -r requirements.in From f2eedfd3d16129cd0eff609e2b084f6d54282a5c Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 10:22:10 +0200 Subject: [PATCH 04/33] enable sentry AioHttpIntegration --- server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server.py b/server.py index 9289e065..96d3f518 100644 --- a/server.py +++ b/server.py @@ -20,6 +20,7 @@ from flask_admin import Admin from flask_cors import cross_origin, CORS from flask_debugtoolbar import DebugToolbarExtension from flask_login import current_user +from sentry_sdk.integrations.aiohttp import AioHttpIntegration from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from werkzeug.middleware.proxy_fix import ProxyFix @@ -86,6 +87,7 @@ if SENTRY_DSN: integrations=[ FlaskIntegration(), SqlalchemyIntegration(), + AioHttpIntegration(), ], ) From 078368362c59c1bc9aa1d34b54a7f61f9e5faa64 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 10:29:52 +0200 Subject: [PATCH 05/33] copy spamassassin client code from https://github.com/petermat/spamassassin_client --- app/spamassassin_utils.py | 118 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 app/spamassassin_utils.py diff --git a/app/spamassassin_utils.py b/app/spamassassin_utils.py new file mode 100644 index 00000000..9ddaa5e5 --- /dev/null +++ b/app/spamassassin_utils.py @@ -0,0 +1,118 @@ +"""Inspired from +https://github.com/petermat/spamassassin_client +""" +import socket, select, re, logging +from io import BytesIO + +divider_pattern = re.compile(br'^(.*?)\r?\n(.*?)\r?\n\r?\n', re.DOTALL) +first_line_pattern = re.compile(br'^SPAMD/[^ ]+ 0 EX_OK$') + + +class SpamAssassin(object): + def __init__(self, message, timeout=20): + self.score = None + self.symbols = None + + # Connecting + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + client.settimeout(timeout) + client.connect(('127.0.0.1', 783)) + + # Sending + client.sendall(self._build_message(message)) + client.shutdown(socket.SHUT_WR) + + # Reading + resfp = BytesIO() + while True: + ready = select.select([client], [], [], timeout) + if ready[0] is None: + # Kill with Timeout! + logging.info('[SpamAssassin] - Timeout ({0}s)!'.format(str(timeout))) + break + + data = client.recv(4096) + if data == b'': + break + + resfp.write(data) + + # Closing + client.close() + client = None + + self._parse_response(resfp.getvalue()) + + def _build_message(self, message): + reqfp = BytesIO() + data_len = str(len(message)).encode() + reqfp.write(b'REPORT SPAMC/1.2\r\n') + reqfp.write(b'Content-Length: ' + data_len + b'\r\n') + reqfp.write(b'User: cx42\r\n\r\n') + reqfp.write(message) + return reqfp.getvalue() + + def _parse_response(self, response): + if response == b'': + logging.info("[SPAM ASSASSIN] Empty response") + return None + + match = divider_pattern.match(response) + if not match: + logging.error("[SPAM ASSASSIN] Response error:") + logging.error(response) + return None + + first_line = match.group(1) + headers = match.group(2) + body = response[match.end(0):] + + # Checking response is good + match = first_line_pattern.match(first_line) + if not match: + logging.error("[SPAM ASSASSIN] invalid response:") + logging.error(first_line) + return None + + report_list = [s.strip() for s in body.decode('utf-8').strip().split('\n')] + linebreak_num = report_list.index([s for s in report_list if "---" in s][0]) + tablelists = [s for s in report_list[linebreak_num + 1:]] + + self.report_fulltext = '\n'.join(report_list) + + + # join line when current one is only wrap of previous + tablelists_temp = [] + if tablelists: + for counter, tablelist in enumerate(tablelists): + if len(tablelist)>1: + if (tablelist[0].isnumeric() or tablelist[0] == '-') and (tablelist[1].isnumeric() or tablelist[1] == '.'): + tablelists_temp.append(tablelist) + else: + if tablelists_temp: + tablelists_temp[-1] += " " + tablelist + tablelists = tablelists_temp + + # create final json + self.report_json = dict() + for tablelist in tablelists: + wordlist = re.split('\s+', tablelist) + self.report_json[wordlist[1]] = {'partscore': float(wordlist[0]), 'description': ' '.join(wordlist[1:])} + + headers = headers.decode('utf-8').replace(' ', '').replace(':', ';').replace('/', ';').split(';') + self.score = float(headers[2]) + + def get_report_json(self): + return self.report_json + + def get_score(self): + return self.score + + def is_spam(self, level=5): + return self.score is None or self.score > level + + def get_fulltext(self): + return self.report_fulltext + + + From 91e3cc5dcbd015aa4c5dc81bd6970a57d774ea61 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 10:32:21 +0200 Subject: [PATCH 06/33] able to set a different host than 127.0.0.1 and apply black format --- app/spamassassin_utils.py | 53 ++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/app/spamassassin_utils.py b/app/spamassassin_utils.py index 9ddaa5e5..d4b6f991 100644 --- a/app/spamassassin_utils.py +++ b/app/spamassassin_utils.py @@ -4,19 +4,19 @@ https://github.com/petermat/spamassassin_client import socket, select, re, logging from io import BytesIO -divider_pattern = re.compile(br'^(.*?)\r?\n(.*?)\r?\n\r?\n', re.DOTALL) -first_line_pattern = re.compile(br'^SPAMD/[^ ]+ 0 EX_OK$') +divider_pattern = re.compile(br"^(.*?)\r?\n(.*?)\r?\n\r?\n", re.DOTALL) +first_line_pattern = re.compile(br"^SPAMD/[^ ]+ 0 EX_OK$") class SpamAssassin(object): - def __init__(self, message, timeout=20): + def __init__(self, message, timeout=20, host="127.0.0.1"): self.score = None self.symbols = None # Connecting client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.settimeout(timeout) - client.connect(('127.0.0.1', 783)) + client.connect((host, 783)) # Sending client.sendall(self._build_message(message)) @@ -28,11 +28,11 @@ class SpamAssassin(object): ready = select.select([client], [], [], timeout) if ready[0] is None: # Kill with Timeout! - logging.info('[SpamAssassin] - Timeout ({0}s)!'.format(str(timeout))) + logging.info("[SpamAssassin] - Timeout ({0}s)!".format(str(timeout))) break data = client.recv(4096) - if data == b'': + if data == b"": break resfp.write(data) @@ -46,14 +46,14 @@ class SpamAssassin(object): def _build_message(self, message): reqfp = BytesIO() data_len = str(len(message)).encode() - reqfp.write(b'REPORT SPAMC/1.2\r\n') - reqfp.write(b'Content-Length: ' + data_len + b'\r\n') - reqfp.write(b'User: cx42\r\n\r\n') + reqfp.write(b"REPORT SPAMC/1.2\r\n") + reqfp.write(b"Content-Length: " + data_len + b"\r\n") + reqfp.write(b"User: cx42\r\n\r\n") reqfp.write(message) return reqfp.getvalue() def _parse_response(self, response): - if response == b'': + if response == b"": logging.info("[SPAM ASSASSIN] Empty response") return None @@ -65,7 +65,7 @@ class SpamAssassin(object): first_line = match.group(1) headers = match.group(2) - body = response[match.end(0):] + body = response[match.end(0) :] # Checking response is good match = first_line_pattern.match(first_line) @@ -74,19 +74,20 @@ class SpamAssassin(object): logging.error(first_line) return None - report_list = [s.strip() for s in body.decode('utf-8').strip().split('\n')] + report_list = [s.strip() for s in body.decode("utf-8").strip().split("\n")] linebreak_num = report_list.index([s for s in report_list if "---" in s][0]) - tablelists = [s for s in report_list[linebreak_num + 1:]] - - self.report_fulltext = '\n'.join(report_list) + tablelists = [s for s in report_list[linebreak_num + 1 :]] + self.report_fulltext = "\n".join(report_list) # join line when current one is only wrap of previous tablelists_temp = [] if tablelists: for counter, tablelist in enumerate(tablelists): - if len(tablelist)>1: - if (tablelist[0].isnumeric() or tablelist[0] == '-') and (tablelist[1].isnumeric() or tablelist[1] == '.'): + if len(tablelist) > 1: + if (tablelist[0].isnumeric() or tablelist[0] == "-") and ( + tablelist[1].isnumeric() or tablelist[1] == "." + ): tablelists_temp.append(tablelist) else: if tablelists_temp: @@ -96,10 +97,19 @@ class SpamAssassin(object): # create final json self.report_json = dict() for tablelist in tablelists: - wordlist = re.split('\s+', tablelist) - self.report_json[wordlist[1]] = {'partscore': float(wordlist[0]), 'description': ' '.join(wordlist[1:])} + wordlist = re.split("\s+", tablelist) + self.report_json[wordlist[1]] = { + "partscore": float(wordlist[0]), + "description": " ".join(wordlist[1:]), + } - headers = headers.decode('utf-8').replace(' ', '').replace(':', ';').replace('/', ';').split(';') + headers = ( + headers.decode("utf-8") + .replace(" ", "") + .replace(":", ";") + .replace("/", ";") + .split(";") + ) self.score = float(headers[2]) def get_report_json(self): @@ -113,6 +123,3 @@ class SpamAssassin(object): def get_fulltext(self): return self.report_fulltext - - - From abc42df0fb39dd355b92ad482b02d74b86d1101a Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 11:05:21 +0200 Subject: [PATCH 07/33] create get_spam_score() as a sync function, use a simpler version for running MailHandler. Remove async/await --- email_handler.py | 115 ++++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 40 deletions(-) diff --git a/email_handler.py b/email_handler.py index e7ebdff6..46fe13a7 100644 --- a/email_handler.py +++ b/email_handler.py @@ -49,6 +49,7 @@ import aiosmtpd import aiospamc import arrow import spf +from aiosmtpd.controller import Controller from aiosmtpd.smtp import Envelope from sqlalchemy.exc import IntegrityError @@ -109,6 +110,7 @@ from app.models import ( Mailbox, ) from app.pgp_utils import PGPException +from app.spamassassin_utils import SpamAssassin from app.utils import random_string from init_app import load_pgp_public_keys from server import create_app, create_light_app @@ -436,9 +438,7 @@ def handle_email_sent_to_ourself(alias, mailbox, msg: Message, user): ) -async def handle_forward( - envelope, msg: Message, rcpt_to: str -) -> List[Tuple[bool, str]]: +def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str]]: """return an array of SMTP status (is_success, smtp_status) is_success indicates whether an email has been delivered and smtp_status is the SMTP Status ("250 Message accepted", "550 Non-existent email address", etc) @@ -490,7 +490,7 @@ async def handle_forward( return [(False, "550 SL E18 unverified mailbox")] else: ret.append( - await forward_email_to_mailbox( + forward_email_to_mailbox( alias, msg, email_log, contact, envelope, mailbox, user ) ) @@ -502,7 +502,7 @@ async def handle_forward( ret.append((False, "550 SL E19 unverified mailbox")) else: ret.append( - await forward_email_to_mailbox( + forward_email_to_mailbox( alias, copy(msg), email_log, @@ -516,7 +516,7 @@ async def handle_forward( return ret -async def forward_email_to_mailbox( +def forward_email_to_mailbox( alias, msg: Message, email_log: EmailLog, @@ -566,7 +566,7 @@ async def forward_email_to_mailbox( if SPAMASSASSIN_HOST: start = time.time() - spam_score = await get_spam_score(msg) + spam_score = get_spam_score(msg) LOG.d( "%s -> %s - spam score %s in %s seconds", contact, @@ -684,7 +684,7 @@ async def forward_email_to_mailbox( return True, "250 Message accepted for delivery" -async def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): +def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): """ return whether an email has been delivered and the smtp status ("250 Message accepted", "550 Non-existent email address", etc) @@ -762,7 +762,7 @@ async def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): # do not use user.max_spam_score here if SPAMASSASSIN_HOST: start = time.time() - spam_score = await get_spam_score(msg) + spam_score = get_spam_score(msg) LOG.d( "%s -> %s - spam score %s in %s seconds", alias, @@ -1418,7 +1418,7 @@ def handle_sender_email(envelope: Envelope): return "250 email to sender accepted" -async def handle(envelope: Envelope) -> str: +def handle(envelope: Envelope) -> str: """Return SMTP status""" # sanitize mail_from, rcpt_tos @@ -1455,7 +1455,7 @@ async def handle(envelope: Envelope) -> str: # recipient starts with "reply+" or "ra+" (ra=reverse-alias) prefix if rcpt_to.startswith("reply+") or rcpt_to.startswith("ra+"): LOG.debug(">>> Reply phase %s(%s) -> %s", mail_from, msg["From"], rcpt_to) - is_delivered, smtp_status = await handle_reply(envelope, msg, rcpt_to) + is_delivered, smtp_status = handle_reply(envelope, msg, rcpt_to) res.append((is_delivered, smtp_status)) else: # Forward case LOG.debug( @@ -1464,9 +1464,7 @@ async def handle(envelope: Envelope) -> str: msg["From"], rcpt_to, ) - for is_delivered, smtp_status in await handle_forward( - envelope, msg, rcpt_to - ): + for is_delivered, smtp_status in handle_forward(envelope, msg, rcpt_to): res.append((is_delivered, smtp_status)) for (is_success, smtp_status) in res: @@ -1478,7 +1476,7 @@ async def handle(envelope: Envelope) -> str: return res[0][1] -async def get_spam_score(message: Message) -> float: +async def get_spam_score_async(message: Message) -> float: LOG.debug("get spam score for %s", message[_MESSAGE_ID]) sa_input = to_bytes(message) @@ -1502,6 +1500,24 @@ async def get_spam_score(message: Message) -> float: return -999 +def get_spam_score(message: Message) -> float: + LOG.debug("get spam score for %s", message[_MESSAGE_ID]) + sa_input = to_bytes(message) + + # Spamassassin requires to have an ending linebreak + if not sa_input.endswith(b"\n"): + LOG.d("add linebreak to spamassassin input") + sa_input += b"\n" + + try: + sa = SpamAssassin(sa_input, host=SPAMASSASSIN_HOST) + return sa.get_score() + except Exception: + LOG.exception("SpamAssassin exception") + # return a negative score so the message is always considered as ham + return -999 + + def sl_sendmail(from_addr, to_addr, msg: Message, mail_options, rcpt_options): """replace smtp.sendmail""" if POSTFIX_SUBMISSION_TLS: @@ -1522,12 +1538,9 @@ def sl_sendmail(from_addr, to_addr, msg: Message, mail_options, rcpt_options): class MailHandler: - def __init__(self, lock): - self.lock = lock - async def handle_DATA(self, server, session, envelope: Envelope): try: - ret = await self._handle(envelope) + ret = self._handle(envelope) return ret except Exception: LOG.exception( @@ -1537,31 +1550,42 @@ class MailHandler: ) return "421 SL Retry later" - async def _handle(self, envelope: Envelope): - async with self.lock: - start = time.time() - LOG.info( - "===>> New message, mail from %s, rctp tos %s ", - envelope.mail_from, - envelope.rcpt_tos, - ) + def _handle(self, envelope: Envelope): + start = time.time() + LOG.info( + "===>> New message, mail from %s, rctp tos %s ", + envelope.mail_from, + envelope.rcpt_tos, + ) - app = new_app() - with app.app_context(): - ret = await handle(envelope) - LOG.info("takes %s seconds <<===", time.time() - start) - return ret + app = new_app() + with app.app_context(): + ret = handle(envelope) + LOG.info("takes %s seconds <<===", time.time() - start) + return ret -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "-p", "--port", help="SMTP port to listen for", type=int, default=20381 - ) - args = parser.parse_args() +def main(port: int): + """Use aiosmtpd Controller""" + controller = Controller(MailHandler(), hostname="0.0.0.0", port=port) - LOG.info("Listen for port %s", args.port) + controller.start() + LOG.d("Start mail controller %s %s", controller.hostname, controller.port) + if LOAD_PGP_EMAIL_HANDLER: + LOG.warning("LOAD PGP keys") + app = create_app() + with app.app_context(): + load_pgp_public_keys() + + while True: + time.sleep(2) + + +def asyncio_main(port: int): + """ + Main entrypoint using asyncio directly without passing by aiosmtpd Controller + """ if LOAD_PGP_EMAIL_HANDLER: LOG.warning("LOAD PGP keys") app = create_app() @@ -1577,7 +1601,7 @@ if __name__ == "__main__": return aiosmtpd.smtp.SMTP(handler, enable_SMTPUTF8=True) server = loop.run_until_complete( - loop.create_server(factory, host="0.0.0.0", port=args.port) + loop.create_server(factory, host="0.0.0.0", port=port) ) try: @@ -1590,3 +1614,14 @@ if __name__ == "__main__": server.close() loop.run_until_complete(server.wait_closed()) loop.close() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "-p", "--port", help="SMTP port to listen for", type=int, default=20381 + ) + args = parser.parse_args() + + LOG.info("Listen for port %s", args.port) + main(port=args.port) From 8517e7d35673b8b5ad3b0a1cf28c868b506875be Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 11:51:08 +0200 Subject: [PATCH 08/33] refactor com emails: remove non-uses, move to the right location --- app/email_utils.py | 9 --------- shell.py | 10 ---------- templates/emails/com/new-app.html | 11 ----------- templates/emails/com/new-app.txt | 10 ---------- .../emails/com/{ => newsletter}/safari-extension.html | 0 .../emails/com/{ => newsletter}/safari-extension.txt | 0 6 files changed, 40 deletions(-) delete mode 100644 templates/emails/com/new-app.html delete mode 100644 templates/emails/com/new-app.txt rename templates/emails/com/{ => newsletter}/safari-extension.html (100%) rename templates/emails/com/{ => newsletter}/safari-extension.txt (100%) diff --git a/app/email_utils.py b/app/email_utils.py index 45d56ff8..07cc37f3 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -135,15 +135,6 @@ def send_change_email(new_email, current_email, name, link): ) -def send_new_app_email(email, name): - send_email( - email, - f"Any question/feedback for SimpleLogin {name}?", - render("com/new-app.txt", name=name), - render("com/new-app.html", name=name), - ) - - def send_test_email_alias(email, name): send_email( email, diff --git a/shell.py b/shell.py index a4343320..a0f85ef9 100644 --- a/shell.py +++ b/shell.py @@ -41,16 +41,6 @@ def reset_db(): create_db() -def send_safari_extension_newsletter(): - for user in User.query.all(): - send_email( - user.email, - "Quickly create alias with our Safari extension", - render("com/safari-extension.txt", user=user), - render("com/safari-extension.html", user=user), - ) - - def send_mailbox_newsletter(): for user in User.query.order_by(User.id).all(): if user.notification and user.activated: diff --git a/templates/emails/com/new-app.html b/templates/emails/com/new-app.html deleted file mode 100644 index 15f5326e..00000000 --- a/templates/emails/com/new-app.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "base.html" %} - -{% block content %} - {{ render_text("Hi " + name) }} - {{ render_text("This is Son, SimpleLogin founder.") }} - {{ render_text("Even though I lead the company, Iā€™m the *product person* and the user experience you get from our product means a lot to me.") }} - {{ render_text('Our users and developers love SimpleLogin and its simplicity (hence the "simple" in the name), but if there\'s anything that\'s bugging you, even the smallest of issues that could be done better, I want to hear about it - so hit the reply button.') }} - {{ render_button("SimpleLogin documentation", "https://docs.simplelogin.io") }} - {{ render_text('Thanks,
SimpleLogin Team.') }} -{% endblock %} - diff --git a/templates/emails/com/new-app.txt b/templates/emails/com/new-app.txt deleted file mode 100644 index af63f92f..00000000 --- a/templates/emails/com/new-app.txt +++ /dev/null @@ -1,10 +0,0 @@ -Hi {{name}} - -This is Son, SimpleLogin Founder šŸ˜Š. - -Even though I lead the company, Iā€™m the "product person" and the user experience you get from our product means a lot to me. - -Our users and developers love SimpleLogin and its simplicity (hence the "simple" in the name šŸ˜‰), but if there's anything that's bugging you, even the smallest of issues that could be done better, I want to hear about it - so hit the reply button. - -Thanks! -SimpleLogin Team. diff --git a/templates/emails/com/safari-extension.html b/templates/emails/com/newsletter/safari-extension.html similarity index 100% rename from templates/emails/com/safari-extension.html rename to templates/emails/com/newsletter/safari-extension.html diff --git a/templates/emails/com/safari-extension.txt b/templates/emails/com/newsletter/safari-extension.txt similarity index 100% rename from templates/emails/com/safari-extension.txt rename to templates/emails/com/newsletter/safari-extension.txt From c8e92af4d3fdc660699091a99bd449b4ab018196 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 11:53:07 +0200 Subject: [PATCH 09/33] improve onboarding emails wordings --- job_runner.py | 8 ++++---- templates/emails/com/onboarding/browser-extension.html | 6 +++++- templates/emails/com/onboarding/mailbox.html | 6 +++++- templates/emails/com/onboarding/pgp.html | 6 +++++- templates/emails/com/onboarding/send-from-alias.html | 6 +++--- 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/job_runner.py b/job_runner.py index f0925160..048c727f 100644 --- a/job_runner.py +++ b/job_runner.py @@ -58,7 +58,7 @@ def onboarding_send_from_alias(user): send_email( to_email, - f"Do you know you can send emails from your alias?", + f"SimpleLogin Tip: Send emails from your alias", render("com/onboarding/send-from-alias.txt", user=user, to_email=to_email), render("com/onboarding/send-from-alias.html", user=user, to_email=to_email), ) @@ -71,7 +71,7 @@ def onboarding_pgp(user): send_email( to_email, - f"Do you know you can encrypt your emails so only you can read them?", + f"SimpleLogin Tip: Secure your emails with PGP", render("com/onboarding/pgp.txt", user=user, to_email=to_email), render("com/onboarding/pgp.html", user=user, to_email=to_email), ) @@ -84,7 +84,7 @@ def onboarding_browser_extension(user): send_email( to_email, - f"Have you tried SimpleLogin Chrome/Firefox extensions and Android/iOS apps?", + f"SimpleLogin Tip: Chrome/Firefox/Safari extensions and Android/iOS apps", render("com/onboarding/browser-extension.txt", user=user, to_email=to_email), render("com/onboarding/browser-extension.html", user=user, to_email=to_email), ) @@ -97,7 +97,7 @@ def onboarding_mailbox(user): send_email( to_email, - f"Do you know you can have multiple mailboxes on SimpleLogin?", + f"SimpleLogin Tip: Multiple mailboxes", render("com/onboarding/mailbox.txt", user=user, to_email=to_email), render("com/onboarding/mailbox.html", user=user, to_email=to_email), ) diff --git a/templates/emails/com/onboarding/browser-extension.html b/templates/emails/com/onboarding/browser-extension.html index 3a278fd9..7e18dc11 100644 --- a/templates/emails/com/onboarding/browser-extension.html +++ b/templates/emails/com/onboarding/browser-extension.html @@ -1,7 +1,11 @@ {% extends "base.html" %} {% block content %} - {{ render_text("Hi " + user.name) }} + {% call text() %} +

+ Download SimpleLogin browser extensions and mobile apps to create aliases on-the-fly. +

+ {% endcall %} {% call text() %} If you want to quickly create aliases without going to SimpleLogin website, you can do that with SimpleLogin diff --git a/templates/emails/com/onboarding/mailbox.html b/templates/emails/com/onboarding/mailbox.html index 48c68f2b..0a15a56c 100644 --- a/templates/emails/com/onboarding/mailbox.html +++ b/templates/emails/com/onboarding/mailbox.html @@ -1,7 +1,11 @@ {% extends "base.html" %} {% block content %} - {{ render_text("Hi " + user.name) }} + {% call text() %} +

+ Add other mailboxes to SimpleLogin. +

+ {% endcall %} {% call text() %} If you have several email addresses, e.g. Gmail for work and Protonmail for personal uses, diff --git a/templates/emails/com/onboarding/pgp.html b/templates/emails/com/onboarding/pgp.html index d33ccb22..cac2bbae 100644 --- a/templates/emails/com/onboarding/pgp.html +++ b/templates/emails/com/onboarding/pgp.html @@ -1,7 +1,11 @@ {% extends "base.html" %} {% block content %} - {{ render_text("Hi " + user.name) }} + {% call text() %} +

+ Secure your emails with PGP. +

+ {% endcall %} {% call text() %} If you use Gmail, Yahoo, Outlook, etc, you might want to use diff --git a/templates/emails/com/onboarding/send-from-alias.html b/templates/emails/com/onboarding/send-from-alias.html index ff2ef3f6..f294dda1 100644 --- a/templates/emails/com/onboarding/send-from-alias.html +++ b/templates/emails/com/onboarding/send-from-alias.html @@ -1,10 +1,10 @@ {% extends "base.html" %} {% block content %} - {{ render_text("Hi " + user.name) }} - {% call text() %} - Do you know you can send emails from your alias?
+

+ Send emails from your alias. +

{% endcall %} {% call text() %} From 8ed619687ffa908836a0e33427531efcda4d3b92 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 12:00:05 +0200 Subject: [PATCH 10/33] ignore UnicodeDecodeError when parsing SpamAssassin response --- app/spamassassin_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/spamassassin_utils.py b/app/spamassassin_utils.py index d4b6f991..b972d1e9 100644 --- a/app/spamassassin_utils.py +++ b/app/spamassassin_utils.py @@ -74,7 +74,9 @@ class SpamAssassin(object): logging.error(first_line) return None - report_list = [s.strip() for s in body.decode("utf-8").strip().split("\n")] + report_list = [ + s.strip() for s in body.decode("utf-8", errors="ignore").strip().split("\n") + ] linebreak_num = report_list.index([s for s in report_list if "---" in s][0]) tablelists = [s for s in report_list[linebreak_num + 1 :]] From 772a2e735516e33bdba2c28adc616f0c4dc3adc0 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 12:20:18 +0200 Subject: [PATCH 11/33] add "export alias" button --- .../templates/dashboard/setting.html | 21 +++++++++++++--- app/dashboard/views/setting.py | 25 +++++++++++++++++-- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/app/dashboard/templates/dashboard/setting.html b/app/dashboard/templates/dashboard/setting.html index b9e11bea..034bd587 100644 --- a/app/dashboard/templates/dashboard/setting.html +++ b/app/dashboard/templates/dashboard/setting.html @@ -315,10 +315,23 @@
You can download all aliases you have created on SimpleLogin along with other data.
-
- - -
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+ + diff --git a/app/dashboard/views/setting.py b/app/dashboard/views/setting.py index 5b0e79b2..2282285d 100644 --- a/app/dashboard/views/setting.py +++ b/app/dashboard/views/setting.py @@ -1,8 +1,17 @@ +import csv import json -from io import BytesIO +from io import BytesIO, StringIO import arrow -from flask import render_template, request, redirect, url_for, flash, Response +from flask import ( + render_template, + request, + redirect, + url_for, + flash, + Response, + make_response, +) from flask_login import login_required, current_user, logout_user from flask_wtf import FlaskForm from flask_wtf.file import FileField @@ -270,6 +279,18 @@ def setting(): mimetype="text/json", headers={"Content-Disposition": "attachment;filename=data.json"}, ) + elif request.form.get("form-name") == "export-alias": + data = [["alias", "note", "enabled"]] + for alias in Alias.filter_by(user_id=current_user.id).all(): # type: Alias + data.append([alias.email, alias.note, alias.enabled]) + + si = StringIO() + cw = csv.writer(si) + cw.writerows(data) + output = make_response(si.getvalue()) + output.headers["Content-Disposition"] = "attachment; filename=aliases.csv" + output.headers["Content-type"] = "text/csv" + return output manual_sub = ManualSubscription.get_by(user_id=current_user.id) return render_template( From 6a4622fca94091e595509af3f858476cbc48cc92 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 12:47:39 +0200 Subject: [PATCH 12/33] replace cx42 by spamd for SpamAssassin get rid of "info: spamd: handle_user (userdir) unable to find user: 'cx42'" error --- app/spamassassin_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/spamassassin_utils.py b/app/spamassassin_utils.py index b972d1e9..55fa0b4b 100644 --- a/app/spamassassin_utils.py +++ b/app/spamassassin_utils.py @@ -48,7 +48,7 @@ class SpamAssassin(object): data_len = str(len(message)).encode() reqfp.write(b"REPORT SPAMC/1.2\r\n") reqfp.write(b"Content-Length: " + data_len + b"\r\n") - reqfp.write(b"User: cx42\r\n\r\n") + reqfp.write(b"User: spamd\r\n\r\n") reqfp.write(message) return reqfp.getvalue() From 770b15aba3fca47bb0ac1793c298318f4dc9593c Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 14:01:49 +0200 Subject: [PATCH 13/33] do not hardcode spamd user --- app/spamassassin_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/spamassassin_utils.py b/app/spamassassin_utils.py index 55fa0b4b..2657c794 100644 --- a/app/spamassassin_utils.py +++ b/app/spamassassin_utils.py @@ -9,9 +9,10 @@ first_line_pattern = re.compile(br"^SPAMD/[^ ]+ 0 EX_OK$") class SpamAssassin(object): - def __init__(self, message, timeout=20, host="127.0.0.1"): + def __init__(self, message, timeout=20, host="127.0.0.1", spamd_user="spamd"): self.score = None self.symbols = None + self.spamd_user = spamd_user # Connecting client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -48,7 +49,7 @@ class SpamAssassin(object): data_len = str(len(message)).encode() reqfp.write(b"REPORT SPAMC/1.2\r\n") reqfp.write(b"Content-Length: " + data_len + b"\r\n") - reqfp.write(b"User: spamd\r\n\r\n") + reqfp.write(f"User: {self.spamd_user}\r\n\r\n".encode()) reqfp.write(message) return reqfp.getvalue() From 6253a4eb237925b84412672604673f772dd6aed1 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 14:03:19 +0200 Subject: [PATCH 14/33] set SpamAssassin timeout to 300s --- email_handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/email_handler.py b/email_handler.py index 46fe13a7..b1d68f39 100644 --- a/email_handler.py +++ b/email_handler.py @@ -1510,7 +1510,8 @@ def get_spam_score(message: Message) -> float: sa_input += b"\n" try: - sa = SpamAssassin(sa_input, host=SPAMASSASSIN_HOST) + # wait for at max 300s which is the default spamd timeout-child + sa = SpamAssassin(sa_input, host=SPAMASSASSIN_HOST, timeout=300) return sa.get_score() except Exception: LOG.exception("SpamAssassin exception") From cbdcab7d242971122b3ed2e6c7fffc68e39a6d87 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Wed, 30 Sep 2020 17:24:03 +0200 Subject: [PATCH 15/33] handle the ValueError in SpamAssassin --- app/spamassassin_utils.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/spamassassin_utils.py b/app/spamassassin_utils.py index 2657c794..0a05f745 100644 --- a/app/spamassassin_utils.py +++ b/app/spamassassin_utils.py @@ -4,6 +4,8 @@ https://github.com/petermat/spamassassin_client import socket, select, re, logging from io import BytesIO +from app.log import LOG + divider_pattern = re.compile(br"^(.*?)\r?\n(.*?)\r?\n\r?\n", re.DOTALL) first_line_pattern = re.compile(br"^SPAMD/[^ ]+ 0 EX_OK$") @@ -101,10 +103,13 @@ class SpamAssassin(object): self.report_json = dict() for tablelist in tablelists: wordlist = re.split("\s+", tablelist) - self.report_json[wordlist[1]] = { - "partscore": float(wordlist[0]), - "description": " ".join(wordlist[1:]), - } + try: + self.report_json[wordlist[1]] = { + "partscore": float(wordlist[0]), + "description": " ".join(wordlist[1:]), + } + except ValueError: + LOG.warning("Cannot parse %s %s", wordlist[0], wordlist) headers = ( headers.decode("utf-8") From 8b63d302e45e807e99de2705c75a432cec258261 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Thu, 1 Oct 2020 03:23:45 +0000 Subject: [PATCH 16/33] fix: upgrade @sentry/browser from 5.22.3 to 5.23.0 Snyk has created this PR to upgrade @sentry/browser from 5.22.3 to 5.23.0. See this package in npm: https://www.npmjs.com/package/@sentry/browser See this project in Snyk: https://app.snyk.io/org/nguyenkims/project/72f25afd-ac84-4504-a9bd-dc5ead29b930?utm_source=github&utm_medium=upgrade-pr --- static/package-lock.json | 60 ++++++++++++++++++++-------------------- static/package.json | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/static/package-lock.json b/static/package-lock.json index 95daaa11..5a5caefa 100644 --- a/static/package-lock.json +++ b/static/package-lock.json @@ -5,59 +5,59 @@ "requires": true, "dependencies": { "@sentry/browser": { - "version": "5.22.3", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.22.3.tgz", - "integrity": "sha512-2TzE/CoBa5ZkvxJizDdi1Iz1ldmXSJpFQ1mL07PIXBjCt0Wxf+WOuFSj5IP4L40XHfJE5gU8wEvSH0VDR8nXtA==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.23.0.tgz", + "integrity": "sha512-lBBHb/NFDOy1K5E/noDkgaibTtxp8F8gmAaVhhpGvOjlcBp1wzNJhWRePYKWgjJ7yFudxGi4Qbferdhm9RwzbA==", "requires": { - "@sentry/core": "5.22.3", - "@sentry/types": "5.22.3", - "@sentry/utils": "5.22.3", + "@sentry/core": "5.23.0", + "@sentry/types": "5.23.0", + "@sentry/utils": "5.23.0", "tslib": "^1.9.3" } }, "@sentry/core": { - "version": "5.22.3", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.22.3.tgz", - "integrity": "sha512-eGL5uUarw3o4i9QUb9JoFHnhriPpWCaqeaIBB06HUpdcvhrjoowcKZj1+WPec5lFg5XusE35vez7z/FPzmJUDw==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.23.0.tgz", + "integrity": "sha512-K8Wp/g1opaauKJh2w5Z1Vw/YdudHQgH6Ng5fBazHZxA7zB9R8EbVKDsjy8XEcyHsWB7fTSlYX/7coqmZNOADdg==", "requires": { - "@sentry/hub": "5.22.3", - "@sentry/minimal": "5.22.3", - "@sentry/types": "5.22.3", - "@sentry/utils": "5.22.3", + "@sentry/hub": "5.23.0", + "@sentry/minimal": "5.23.0", + "@sentry/types": "5.23.0", + "@sentry/utils": "5.23.0", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.22.3", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.22.3.tgz", - "integrity": "sha512-INo47m6N5HFEs/7GMP9cqxOIt7rmRxdERunA3H2L37owjcr77MwHVeeJ9yawRS6FMtbWXplgWTyTIWIYOuqVbw==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.23.0.tgz", + "integrity": "sha512-P0sevLI9qAQc1J+AcHzNXwj83aG3GKiABVQJp0rgCUMtrXqLawa+j8pOHg8p7QWroHM7TKDMKeny9WemXBgzBQ==", "requires": { - "@sentry/types": "5.22.3", - "@sentry/utils": "5.22.3", + "@sentry/types": "5.23.0", + "@sentry/utils": "5.23.0", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.22.3", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.22.3.tgz", - "integrity": "sha512-HoINpYnVYCpNjn2XIPIlqH5o4BAITpTljXjtAftOx6Hzj+Opjg8tR8PWliyKDvkXPpc4kXK9D6TpEDw8MO0wZA==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.23.0.tgz", + "integrity": "sha512-/w/B7ShMVu/tLI0/A5X+w6GfdZIQdFQihWyIK1vXaYS5NS6biGI3K6DcACuMrD/h4BsqlfgdXSOHHrmCJcyCXQ==", "requires": { - "@sentry/hub": "5.22.3", - "@sentry/types": "5.22.3", + "@sentry/hub": "5.23.0", + "@sentry/types": "5.23.0", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "5.22.3", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.22.3.tgz", - "integrity": "sha512-cv+VWK0YFgCVDvD1/HrrBWOWYG3MLuCUJRBTkV/Opdy7nkdNjhCAJQrEyMM9zX0sac8FKWKOHT0sykNh8KgmYw==" + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.23.0.tgz", + "integrity": "sha512-PbN5MVWxrq05sZ707lc8lleV0xSsI6jWr9h9snvbAuMjcauE0lmdWmjoWKY3PAz2s1mGYFh55kIo8SmQuVwbYg==" }, "@sentry/utils": { - "version": "5.22.3", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.22.3.tgz", - "integrity": "sha512-AHNryXMBvIkIE+GQxTlmhBXD0Ksh+5w1SwM5qi6AttH+1qjWLvV6WB4+4pvVvEoS8t5F+WaVUZPQLmCCWp6zKw==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.23.0.tgz", + "integrity": "sha512-D5gQDM0wEjKxhE+YNvCuCHo/6JuaORF2/3aOhoJBR+dy9EACRspg7kp3+9KF44xd2HVEXkSVCJkv8/+sHePYRQ==", "requires": { - "@sentry/types": "5.22.3", + "@sentry/types": "5.23.0", "tslib": "^1.9.3" } }, diff --git a/static/package.json b/static/package.json index 84d83819..cff89796 100644 --- a/static/package.json +++ b/static/package.json @@ -16,7 +16,7 @@ }, "homepage": "https://github.com/simple-login/app#readme", "dependencies": { - "@sentry/browser": "^5.22.3", + "@sentry/browser": "^5.23.0", "bootbox": "^5.4.0", "font-awesome": "^4.7.0", "intro.js": "^2.9.3", From 5985c7f655947952baf06e13ba49bbe9c032a06a Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 1 Oct 2020 11:25:19 +0200 Subject: [PATCH 17/33] upgrade yacron --- requirements.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4e5c97d9..828995a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ # pip-compile # aiocontextvars==0.2.2 # via -r requirements.in -aiohttp==3.5.4 # via raven-aiohttp, yacron +aiohttp==3.5.4 # via yacron aiosmtpd==1.2 # via -r requirements.in aiosmtplib==1.0.6 # via yacron aiospamc==0.6.1 # via -r requirements.in @@ -32,7 +32,7 @@ cfgv==3.2.0 # via pre-commit chardet==3.0.4 # via aiohttp, requests click==7.1.2 # via black, flask, pip-tools coloredlogs==10.0 # via -r requirements.in -crontab==0.22.5 # via yacron +crontab==0.22.8 # via yacron cryptography==2.7 # via jwcrypto, pyopenssl, webauthn decorator==4.4.0 # via ipython, traitlets distlib==0.3.1 # via virtualenv @@ -63,7 +63,7 @@ httplib2==0.17.0 # via google-api-python-client, google-auth-httplib2 humanfriendly==4.18 # via coloredlogs identify==1.5.0 # via pre-commit idna==2.8 # via requests, yarl -importlib-metadata==0.18 # via pluggy, pre-commit, pytest, virtualenv +importlib-metadata==0.18 # via pluggy, pytest ipython-genutils==0.2.0 # via traitlets ipython==7.5.0 # via -r requirements.in itsdangerous==1.1.0 # via flask, flask-debugtoolbar @@ -108,16 +108,15 @@ python-dateutil==2.8.0 # via alembic, arrow, botocore, strictyaml python-dotenv==0.10.3 # via -r requirements.in python-editor==1.0.4 # via alembic python-gnupg==0.4.5 # via -r requirements.in +pytz==2020.1 # via yacron pyyaml==5.3.1 # via pre-commit -raven-aiohttp==0.7.0 # via yacron -raven==6.10.0 # via raven-aiohttp, yacron regex==2020.7.14 # via black requests-oauthlib==1.2.0 # via -r requirements.in requests==2.22.0 # via facebook-sdk, requests-oauthlib rsa==4.0 # via google-auth ruamel.yaml==0.15.97 # via strictyaml s3transfer==0.2.1 # via boto3 -sentry-sdk==0.18.0 # via -r requirements.in +sentry-sdk==0.18.0 # via -r requirements.in, yacron simplejson==3.17.0 # via flask-profiler six==1.12.0 # via bcrypt, cryptography, flask-cors, flask-limiter, google-api-python-client, google-auth, limits, packaging, pip-tools, prompt-toolkit, pyopenssl, pytest, python-dateutil, sqlalchemy-utils, traitlets, virtualenv, webauthn sqlalchemy-utils==0.36.1 # via -r requirements.in @@ -136,7 +135,7 @@ wcwidth==0.1.7 # via prompt-toolkit, pytest webauthn==0.4.7 # via -r requirements.in werkzeug==0.15.4 # via flask, flask-debugtoolbar wtforms==2.2.1 # via -r requirements.in, flask-admin, flask-wtf -yacron==0.9.0 # via -r requirements.in +yacron==0.11.1 # via -r requirements.in yarl==1.3.0 # via aiohttp zipp==0.5.1 # via importlib-metadata zope.event==4.4 # via gevent From 9aa460d47f1b490a4097110ac76949f0ec938515 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 1 Oct 2020 11:25:36 +0200 Subject: [PATCH 18/33] upgrade ruamel.yaml to avoid installation issue --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 828995a1..2cbb600b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -114,7 +114,7 @@ regex==2020.7.14 # via black requests-oauthlib==1.2.0 # via -r requirements.in requests==2.22.0 # via facebook-sdk, requests-oauthlib rsa==4.0 # via google-auth -ruamel.yaml==0.15.97 # via strictyaml +ruamel.yaml==0.16.12 # via strictyaml s3transfer==0.2.1 # via boto3 sentry-sdk==0.18.0 # via -r requirements.in, yacron simplejson==3.17.0 # via flask-profiler From 351adc57f5eb69dda1044b792f48f475d203c666 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 1 Oct 2020 12:13:28 +0200 Subject: [PATCH 19/33] use poetry instead of pip --- .github/workflows/main.yml | 11 +- Dockerfile | 11 +- README.md | 10 +- poetry.lock | 2696 ++++++++++++++++++++++++++++++++++++ pyproject.toml | 66 + requirements.in | 49 - requirements.txt | 146 -- 7 files changed, 2782 insertions(+), 207 deletions(-) create mode 100644 poetry.lock delete mode 100644 requirements.in delete mode 100644 requirements.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3b69eed7..cdaffb8d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,15 +22,16 @@ jobs: - uses: actions/cache@v1 with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + path: ~/.cache/poetry + key: ${{ runner.os }}-poetry-${{ hashFiles('**/peotry.lock') }} restore-keys: | - ${{ runner.os }}-pip- + ${{ runner.os }}-poetry- - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -r requirements.txt + python -m pip install poetry==1.0.10 + poetry config virtualenvs.create false + poetry install - name: Test formatting run: | diff --git a/Dockerfile b/Dockerfile index 6dd167fc..d53054e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,16 +4,19 @@ WORKDIR /code COPY ./static/package*.json /code/static/ RUN cd /code/static && npm install - +# Main image FROM python:3.7 -WORKDIR /code # install some utility packages RUN apt update && apt install -y vim telnet +RUN pip3 install poetry==1.0.10 + # install dependencies -COPY ./requirements.txt ./ -RUN pip3 install --no-cache-dir -r requirements.txt +WORKDIR /code +COPY poetry.lock pyproject.toml ./ +RUN poetry config virtualenvs.create false \ + && poetry install # copy npm packages COPY --from=npm /code /code diff --git a/README.md b/README.md index 232f7f55..4249f128 100644 --- a/README.md +++ b/README.md @@ -591,10 +591,15 @@ All work on SimpleLogin happens directly on GitHub. ### Run code locally -The project uses Python 3.7+ and Node v10. First, install all dependencies by running the following command. Feel free to use `virtualenv` or similar tools to isolate development environment. +The project uses +- Python 3.7+ and [poetry](https://python-poetry.org/) to manage dependencies +- Node v10 for front-end. + +First, install all dependencies by running the following command. +Feel free to use `virtualenv` or similar tools to isolate development environment. ```bash -pip3 install -r requirements.txt +poetry install ``` You also need to install `gpg`, on Mac it can be done with: @@ -603,7 +608,6 @@ You also need to install `gpg`, on Mac it can be done with: brew install gnupg ``` - Then make sure all tests pass ```bash diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..fe86f16e --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2696 @@ +[[package]] +category = "main" +description = "Asyncio support for PEP-567 contextvars backport." +name = "aiocontextvars" +optional = false +python-versions = ">=3.5" +version = "0.2.2" + +[[package]] +category = "main" +description = "Async http client/server framework (asyncio)" +name = "aiohttp" +optional = false +python-versions = ">=3.5.3" +version = "3.6.2" + +[package.dependencies] +async-timeout = ">=3.0,<4.0" +attrs = ">=17.3.0" +chardet = ">=2.0,<4.0" +multidict = ">=4.5,<5.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["aiodns", "brotlipy", "cchardet"] + +[[package]] +category = "main" +description = "aiosmtpd - asyncio based SMTP server" +name = "aiosmtpd" +optional = false +python-versions = "*" +version = "1.2" + +[package.dependencies] +atpublic = "*" + +[[package]] +category = "main" +description = "asyncio SMTP client" +name = "aiosmtplib" +optional = false +python-versions = ">=3.5.2,<4.0.0" +version = "1.1.4" + +[package.extras] +docs = ["sphinx (>=2,<4)", "sphinx_autodoc_typehints (>=1.7.0,<2.0.0)"] +uvloop = ["uvloop (>=0.13,<0.15)"] + +[[package]] +category = "main" +description = "An asyncio-based library to communicate with SpamAssassin's SPAMD service." +name = "aiospamc" +optional = false +python-versions = ">=3.5,<4.0" +version = "0.6.1" + +[package.dependencies] +certifi = ">=2019.9,<2020.0" + +[[package]] +category = "main" +description = "A database migration tool for SQLAlchemy." +name = "alembic" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.3" + +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.1.0" +python-dateutil = "*" +python-editor = ">=0.3" + +[[package]] +category = "dev" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "appdirs" +optional = false +python-versions = "*" +version = "1.4.4" + +[[package]] +category = "main" +description = "Disable App Nap on OS X 10.9" +marker = "sys_platform == \"darwin\"" +name = "appnope" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "main" +description = "Better dates & times for Python" +name = "arrow" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.16.0" + +[package.dependencies] +python-dateutil = ">=2.7.0" + +[[package]] +category = "main" +description = "Timeout context manager for asyncio programs" +name = "async-timeout" +optional = false +python-versions = ">=3.5.3" +version = "3.0.1" + +[[package]] +category = "dev" +description = "Atomic file writes." +marker = "sys_platform == \"win32\"" +name = "atomicwrites" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.0" + +[[package]] +category = "main" +description = "public -- @public for populating __all__" +name = "atpublic" +optional = false +python-versions = ">=3.6" +version = "2.0" + +[package.dependencies] +[package.dependencies.typing_extensions] +python = "<3.8" +version = "*" + +[[package]] +category = "main" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.2.0" + +[package.extras] +dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +category = "main" +description = "Specifications for callback functions passed in to an API" +name = "backcall" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "main" +description = "Modern password hashing for your software and your servers" +name = "bcrypt" +optional = false +python-versions = ">=3.6" +version = "3.2.0" + +[package.dependencies] +cffi = ">=1.1" +six = ">=1.4.1" + +[package.extras] +tests = ["pytest (>=3.2.1,<3.3.0 || >3.3.0)"] +typecheck = ["mypy"] + +[[package]] +category = "dev" +description = "The uncompromising code formatter." +name = "black" +optional = false +python-versions = ">=3.6" +version = "20.8b1" + +[package.dependencies] +appdirs = "*" +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.6,<1" +regex = ">=2020.1.8" +toml = ">=0.10.1" +typed-ast = ">=1.4.0" +typing-extensions = ">=3.7.4" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] + +[[package]] +category = "main" +description = "Fast, simple object-to-object and broadcast signaling" +name = "blinker" +optional = false +python-versions = "*" +version = "1.4" + +[[package]] +category = "main" +description = "The AWS SDK for Python" +name = "boto3" +optional = false +python-versions = "*" +version = "1.15.9" + +[package.dependencies] +botocore = ">=1.18.9,<1.19.0" +jmespath = ">=0.7.1,<1.0.0" +s3transfer = ">=0.3.0,<0.4.0" + +[[package]] +category = "main" +description = "Low-level, data-driven core of boto 3." +name = "botocore" +optional = false +python-versions = "*" +version = "1.18.9" + +[package.dependencies] +jmespath = ">=0.7.1,<1.0.0" +python-dateutil = ">=2.1,<3.0.0" + +[package.dependencies.urllib3] +python = "<3.4.0 || >=3.5.0" +version = ">=1.20,<1.26" + +[[package]] +category = "main" +description = "Extensible memoizing collections and decorators" +name = "cachetools" +optional = false +python-versions = "~=3.5" +version = "4.1.1" + +[[package]] +category = "main" +description = "Pure Python CBOR (de)serializer with extensive tag support" +name = "cbor2" +optional = false +python-versions = "*" +version = "5.2.0" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +category = "main" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2019.11.28" + +[[package]] +category = "main" +description = "Foreign Function Interface for Python calling C code." +name = "cffi" +optional = false +python-versions = "*" +version = "1.14.3" + +[package.dependencies] +pycparser = "*" + +[[package]] +category = "dev" +description = "Validate configuration and produce human readable error messages." +name = "cfgv" +optional = false +python-versions = ">=3.6.1" +version = "3.2.0" + +[[package]] +category = "main" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + +[[package]] +category = "main" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "7.1.2" + +[[package]] +category = "main" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.3" + +[[package]] +category = "main" +description = "Colored terminal output for Python's logging module" +name = "coloredlogs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "14.0" + +[package.dependencies] +humanfriendly = ">=7.1" + +[package.extras] +cron = ["capturer (>=2.4)"] + +[[package]] +category = "main" +description = "Parse and use crontab schedules in Python" +name = "crontab" +optional = false +python-versions = "*" +version = "0.22.8" + +[[package]] +category = "main" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +name = "cryptography" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +version = "3.1.1" + +[package.dependencies] +cffi = ">=1.8,<1.11.3 || >1.11.3" +six = ">=1.4.1" + +[package.extras] +docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0,<3.1.0 || >3.1.0,<3.1.1 || >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"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] + +[[package]] +category = "main" +description = "Decorators for Humans" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.4.2" + +[[package]] +category = "dev" +description = "Distribution utilities" +name = "distlib" +optional = false +python-versions = "*" +version = "0.3.1" + +[[package]] +category = "main" +description = "DKIM (DomainKeys Identified Mail), ARC (Authenticated Receive Chain), and TLSRPT (TLS Report) email signing and verification" +name = "dkimpy" +optional = false +python-versions = "*" +version = "1.0.5" + +[package.dependencies] +dnspython = ">=1.16.0" + +[package.extras] +ARC = ["authres"] +asyncio = ["aiodns"] +ed25519 = ["pynacl"] +testing = ["authres", "pynacl"] + +[[package]] +category = "main" +description = "DNS toolkit" +name = "dnspython" +optional = false +python-versions = ">=3.6" +version = "2.0.0" + +[package.extras] +curio = ["curio (>=1.2)", "sniffio (>=1.1)"] +dnssec = ["cryptography (>=2.6)"] +doh = ["requests", "requests-toolbelt"] +idna = ["idna (>=2.1)"] +trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"] + +[[package]] +category = "main" +description = "This client library is designed to support the Facebook Graph API and the official Facebook JavaScript SDK, which is the canonical way to implement Facebook authentication." +name = "facebook-sdk" +optional = false +python-versions = "*" +version = "3.1.0" + +[package.dependencies] +requests = "*" + +[[package]] +category = "dev" +description = "A platform independent file lock." +name = "filelock" +optional = false +python-versions = "*" +version = "3.0.12" + +[[package]] +category = "main" +description = "A simple framework for building complex web applications." +name = "flask" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "1.1.2" + +[package.dependencies] +Jinja2 = ">=2.10.1" +Werkzeug = ">=0.15" +click = ">=5.1" +itsdangerous = ">=0.24" + +[package.extras] +dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] +docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] +dotenv = ["python-dotenv"] + +[[package]] +category = "main" +description = "Simple and extensible admin interface framework for Flask" +name = "flask-admin" +optional = false +python-versions = "*" +version = "1.5.6" + +[package.dependencies] +Flask = ">=0.7" +wtforms = "*" + +[package.extras] +aws = ["boto"] +azure = ["azure-storage-blob"] + +[[package]] +category = "main" +description = "A Flask extension adding a decorator for CORS support" +name = "flask-cors" +optional = false +python-versions = "*" +version = "3.0.9" + +[package.dependencies] +Flask = ">=0.9" +Six = "*" + +[[package]] +category = "main" +description = "A toolbar overlay for debugging Flask applications." +name = "flask-debugtoolbar" +optional = false +python-versions = "*" +version = "0.11.0" + +[package.dependencies] +Blinker = "*" +Flask = ">=0.8" +itsdangerous = "*" +werkzeug = "*" + +[[package]] +category = "main" +description = "Basic and Digest HTTP authentication for Flask routes" +name = "flask-httpauth" +optional = false +python-versions = "*" +version = "4.1.0" + +[package.dependencies] +Flask = "*" + +[[package]] +category = "main" +description = "Rate limiting for flask applications" +name = "flask-limiter" +optional = false +python-versions = "*" +version = "1.4" + +[package.dependencies] +Flask = ">=0.8" +limits = "*" +six = ">=1.4.1" + +[[package]] +category = "main" +description = "User session management for Flask" +name = "flask-login" +optional = false +python-versions = "*" +version = "0.5.0" + +[package.dependencies] +Flask = "*" + +[[package]] +category = "main" +description = "SQLAlchemy database migrations for Flask applications using Alembic" +name = "flask-migrate" +optional = false +python-versions = "*" +version = "2.5.3" + +[package.dependencies] +Flask = ">=0.9" +Flask-SQLAlchemy = ">=1.0" +alembic = ">=0.7" + +[[package]] +category = "main" +description = "API endpoint profiler for Flask framework" +name = "flask-profiler" +optional = false +python-versions = "*" +version = "1.8.1" + +[package.dependencies] +Flask = "*" +Flask-HTTPAuth = "*" +simplejson = "*" + +[[package]] +category = "main" +description = "Adds SQLAlchemy support to your Flask application." +name = "flask-sqlalchemy" +optional = false +python-versions = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*" +version = "2.4.4" + +[package.dependencies] +Flask = ">=0.10" +SQLAlchemy = ">=0.8.0" + +[[package]] +category = "main" +description = "Simple integration of Flask and WTForms." +name = "flask-wtf" +optional = false +python-versions = "*" +version = "0.14.3" + +[package.dependencies] +Flask = "*" +WTForms = "*" +itsdangerous = "*" + +[[package]] +category = "main" +description = "Clean single-source support for Python 3 and 2" +name = "future" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.18.2" + +[[package]] +category = "main" +description = "Coroutine-based network library" +name = "gevent" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +version = "20.9.0" + +[package.dependencies] +cffi = ">=1.12.2" +greenlet = ">=0.4.17" +setuptools = "*" +"zope.event" = "*" +"zope.interface" = "*" + +[package.extras] +dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] +docs = ["repoze.sphinx.autointerface", "sphinxcontrib-programoutput"] +monitor = ["psutil (>=5.7.0)"] +recommended = ["dnspython (>=1.16.0,<2.0)", "idna", "cffi (>=1.12.2)", "selectors2", "backports.socketpair", "psutil (>=5.7.0)"] +test = ["dnspython (>=1.16.0,<2.0)", "idna", "requests", "objgraph", "cffi (>=1.12.2)", "selectors2", "futures", "mock", "backports.socketpair", "contextvars (2.4)", "coverage (<5.0)", "coveralls (>=1.7.0)", "psutil (>=5.7.0)"] + +[[package]] +category = "main" +description = "Google API client core library" +name = "google-api-core" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.22.2" + +[package.dependencies] +google-auth = ">=1.21.1,<2.0dev" +googleapis-common-protos = ">=1.6.0,<2.0dev" +protobuf = ">=3.12.0" +pytz = "*" +requests = ">=2.18.0,<3.0.0dev" +setuptools = ">=34.0.0" +six = ">=1.10.0" + +[package.extras] +grpc = ["grpcio (>=1.29.0,<2.0dev)"] +grpcgcp = ["grpcio-gcp (>=0.2.2)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] + +[[package]] +category = "main" +description = "Google API Client Library for Python" +name = "google-api-python-client" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.12.3" + +[package.dependencies] +google-api-core = ">=1.21.0,<2dev" +google-auth = ">=1.16.0" +google-auth-httplib2 = ">=0.0.3" +httplib2 = ">=0.15.0,<1dev" +six = ">=1.13.0,<2dev" +uritemplate = ">=3.0.0,<4dev" + +[[package]] +category = "main" +description = "Google Authentication Library" +name = "google-auth" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.22.0" + +[package.dependencies] +cachetools = ">=2.0.0,<5.0" +pyasn1-modules = ">=0.2.1" +setuptools = ">=40.3.0" +six = ">=1.9.0" + +[package.dependencies.aiohttp] +python = ">=3.6" +version = ">=3.6.2,<4.0.0dev" + +[package.dependencies.rsa] +python = ">=3.5" +version = ">=3.1.4,<5" + +[[package]] +category = "main" +description = "Google Authentication Library: httplib2 transport" +name = "google-auth-httplib2" +optional = false +python-versions = "*" +version = "0.0.4" + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.9.1" +six = "*" + +[[package]] +category = "main" +description = "Common protobufs used in Google APIs" +name = "googleapis-common-protos" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.52.0" + +[package.dependencies] +protobuf = ">=3.6.0" + +[package.extras] +grpc = ["grpcio (>=1.0.0)"] + +[[package]] +category = "main" +description = "Lightweight in-process concurrent programming" +marker = "platform_python_implementation == \"CPython\"" +name = "greenlet" +optional = false +python-versions = "*" +version = "0.4.17" + +[[package]] +category = "main" +description = "WSGI HTTP Server for UNIX" +name = "gunicorn" +optional = false +python-versions = ">=3.4" +version = "20.0.4" + +[package.dependencies] +setuptools = ">=3.0" + +[package.extras] +eventlet = ["eventlet (>=0.9.7)"] +gevent = ["gevent (>=0.13)"] +setproctitle = ["setproctitle"] +tornado = ["tornado (>=0.2)"] + +[[package]] +category = "main" +description = "A comprehensive HTTP client library." +name = "httplib2" +optional = false +python-versions = "*" +version = "0.18.1" + +[[package]] +category = "main" +description = "Human friendly output for text interfaces using Python" +name = "humanfriendly" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "8.2" + +[package.dependencies] +pyreadline = "*" + +[[package]] +category = "dev" +description = "File identification library for Python" +name = "identify" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "1.5.5" + +[package.extras] +license = ["editdistance"] + +[[package]] +category = "main" +description = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.10" + +[[package]] +category = "dev" +description = "Read metadata from Python packages" +marker = "python_version < \"3.8\"" +name = "importlib-metadata" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "1.7.0" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "rst.linker"] +testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] + +[[package]] +category = "dev" +description = "iniconfig: brain-dead simple config-ini parsing" +name = "iniconfig" +optional = false +python-versions = "*" +version = "1.0.1" + +[[package]] +category = "main" +description = "IPython: Productive Interactive Computing" +name = "ipython" +optional = false +python-versions = ">=3.7" +version = "7.18.1" + +[package.dependencies] +appnope = "*" +backcall = "*" +colorama = "*" +decorator = "*" +jedi = ">=0.10" +pexpect = ">4.3" +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] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["notebook", "ipywidgets"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] + +[[package]] +category = "main" +description = "Vestigial utilities from IPython" +name = "ipython-genutils" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "main" +description = "Various helpers to pass data to untrusted environments and back." +name = "itsdangerous" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.1.0" + +[[package]] +category = "main" +description = "An autocompletion tool for Python that can be used for text editors." +name = "jedi" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.17.2" + +[package.dependencies] +parso = ">=0.7.0,<0.8.0" + +[package.extras] +qa = ["flake8 (3.7.9)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] + +[[package]] +category = "main" +description = "A very fast and expressive template engine." +name = "jinja2" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.11.2" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[package.extras] +i18n = ["Babel (>=0.8)"] + +[[package]] +category = "main" +description = "JSON Matching Expressions" +name = "jmespath" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.10.0" + +[[package]] +category = "main" +description = "Implementation of JOSE Web standards" +name = "jwcrypto" +optional = false +python-versions = "*" +version = "0.8" + +[package.dependencies] +cryptography = ">=2.3" + +[[package]] +category = "main" +description = "Rate limiting utilities" +name = "limits" +optional = false +python-versions = "*" +version = "1.5.1" + +[package.dependencies] +six = ">=1.4.1" + +[[package]] +category = "main" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +name = "mako" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.1.3" + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["babel"] +lingua = ["lingua"] + +[[package]] +category = "main" +description = "Safely add untrusted strings to HTML/XML markup." +name = "markupsafe" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.1.1" + +[[package]] +category = "main" +description = "A module for monitoring memory usage of a python program" +name = "memory-profiler" +optional = false +python-versions = "*" +version = "0.57.0" + +[package.dependencies] +psutil = "*" + +[[package]] +category = "main" +description = "multidict implementation" +name = "multidict" +optional = false +python-versions = ">=3.5" +version = "4.7.6" + +[[package]] +category = "dev" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +name = "mypy-extensions" +optional = false +python-versions = "*" +version = "0.4.3" + +[[package]] +category = "dev" +description = "Node.js virtual environment builder" +name = "nodeenv" +optional = false +python-versions = "*" +version = "1.5.0" + +[[package]] +category = "main" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +name = "oauthlib" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.1.0" + +[package.extras] +rsa = ["cryptography"] +signals = ["blinker"] +signedtoken = ["cryptography", "pyjwt (>=1.0.0)"] + +[[package]] +category = "dev" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.4" + +[package.dependencies] +pyparsing = ">=2.0.2" +six = "*" + +[[package]] +category = "main" +description = "A Python Parser" +name = "parso" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.7.1" + +[package.extras] +testing = ["docopt", "pytest (>=3.0.7)"] + +[[package]] +category = "dev" +description = "Utility library for gitignore style pattern matching of file paths." +name = "pathspec" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.8.0" + +[[package]] +category = "main" +description = "Pexpect allows easy control of interactive console applications." +marker = "sys_platform != \"win32\"" +name = "pexpect" +optional = false +python-versions = "*" +version = "4.8.0" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +category = "main" +description = "a port of the serialize and unserialize functions of php to python." +name = "phpserialize" +optional = false +python-versions = "*" +version = "1.3" + +[[package]] +category = "main" +description = "Tiny 'shelve'-like database with concurrency support" +name = "pickleshare" +optional = false +python-versions = "*" +version = "0.7.5" + +[[package]] +category = "dev" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.13.1" + +[package.dependencies] +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +category = "dev" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +name = "pre-commit" +optional = false +python-versions = ">=3.6.1" +version = "2.7.1" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + +[[package]] +category = "main" +description = "Library for building powerful interactive command lines in Python" +name = "prompt-toolkit" +optional = false +python-versions = ">=3.6.1" +version = "3.0.7" + +[package.dependencies] +wcwidth = "*" + +[[package]] +category = "main" +description = "Protocol Buffers" +name = "protobuf" +optional = false +python-versions = "*" +version = "3.13.0" + +[package.dependencies] +setuptools = "*" +six = ">=1.9" + +[[package]] +category = "main" +description = "Cross-platform lib for process and system monitoring in Python." +name = "psutil" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "5.7.2" + +[package.extras] +test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] + +[[package]] +category = "main" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +name = "psycopg2-binary" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "2.8.6" + +[[package]] +category = "main" +description = "Run a subprocess in a pseudo terminal" +marker = "sys_platform != \"win32\"" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + +[[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.9.0" + +[[package]] +category = "main" +description = "ASN.1 types and codecs" +name = "pyasn1" +optional = false +python-versions = "*" +version = "0.4.8" + +[[package]] +category = "main" +description = "A collection of ASN.1-based protocols modules." +name = "pyasn1-modules" +optional = false +python-versions = "*" +version = "0.2.8" + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.5.0" + +[[package]] +category = "main" +description = "C parser in Python" +name = "pycparser" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.20" + +[[package]] +category = "main" +description = "Cryptographic library for Python" +name = "pycryptodome" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.9.8" + +[[package]] +category = "main" +description = "Pygments is a syntax highlighting package written in Python." +name = "pygments" +optional = false +python-versions = ">=3.5" +version = "2.7.1" + +[[package]] +category = "main" +description = "Python wrapper module around the OpenSSL library" +name = "pyopenssl" +optional = false +python-versions = "*" +version = "19.1.0" + +[package.dependencies] +cryptography = ">=2.8" +six = ">=1.5.2" + +[package.extras] +docs = ["sphinx", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + +[[package]] +category = "main" +description = "Python One Time Password Library" +name = "pyotp" +optional = false +python-versions = "*" +version = "2.4.0" + +[[package]] +category = "dev" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.7" + +[[package]] +category = "main" +description = "A python implmementation of GNU readline." +marker = "sys_platform == \"win32\"" +name = "pyreadline" +optional = false +python-versions = "*" +version = "2.1" + +[[package]] +category = "main" +description = "SPF (Sender Policy Framework) implemented in Python." +name = "pyspf" +optional = false +python-versions = "*" +version = "2.0.14" + +[[package]] +category = "dev" +description = "pytest: simple powerful testing with Python" +name = "pytest" +optional = false +python-versions = ">=3.5" +version = "6.1.0" + +[package.dependencies] +atomicwrites = ">=1.0" +attrs = ">=17.4.0" +colorama = "*" +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.8.2" +toml = "*" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12" + +[package.extras] +checkqa_mypy = ["mypy (0.780)"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +name = "python-dateutil" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "main" +description = "Add .env support to your django/flask apps in development and deployments" +name = "python-dotenv" +optional = false +python-versions = "*" +version = "0.14.0" + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +category = "main" +description = "Programmatically open an editor, capture the result." +name = "python-editor" +optional = false +python-versions = "*" +version = "1.0.4" + +[[package]] +category = "main" +description = "A wrapper for the Gnu Privacy Guard (GPG or GnuPG)" +name = "python-gnupg" +optional = false +python-versions = "*" +version = "0.4.6" + +[[package]] +category = "main" +description = "World timezone definitions, modern and historical" +name = "pytz" +optional = false +python-versions = "*" +version = "2020.1" + +[[package]] +category = "dev" +description = "YAML parser and emitter for Python" +name = "pyyaml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "5.3.1" + +[[package]] +category = "dev" +description = "Alternative regular expression module, to replace re." +name = "regex" +optional = false +python-versions = "*" +version = "2020.9.27" + +[[package]] +category = "main" +description = "Python HTTP for Humans." +name = "requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.24.0" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<4" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" + +[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]] +category = "main" +description = "OAuthlib authentication support for Requests." +name = "requests-oauthlib" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.0" + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib (>=3.0.0)"] + +[[package]] +category = "main" +description = "Pure-Python RSA implementation" +marker = "python_version >= \"3.5\"" +name = "rsa" +optional = false +python-versions = ">=3.5, <4" +version = "4.6" + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +category = "main" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +name = "ruamel.yaml" +optional = false +python-versions = "*" +version = "0.16.12" + +[package.dependencies] +[package.dependencies."ruamel.yaml.clib"] +python = "<3.9" +version = ">=0.1.2" + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +category = "main" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +marker = "platform_python_implementation == \"CPython\" and python_version < \"3.9\"" +name = "ruamel.yaml.clib" +optional = false +python-versions = "*" +version = "0.2.2" + +[[package]] +category = "main" +description = "An Amazon S3 Transfer Manager" +name = "s3transfer" +optional = false +python-versions = "*" +version = "0.3.3" + +[package.dependencies] +botocore = ">=1.12.36,<2.0a.0" + +[[package]] +category = "main" +description = "Python client for Sentry (https://sentry.io)" +name = "sentry-sdk" +optional = false +python-versions = "*" +version = "0.18.0" + +[package.dependencies] +certifi = "*" +urllib3 = ">=1.10.0" + +[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]] +category = "main" +description = "Simple, fast, extensible JSON encoder/decoder for Python" +name = "simplejson" +optional = false +python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" +version = "3.17.2" + +[[package]] +category = "main" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.15.0" + +[[package]] +category = "main" +description = "Database Abstraction Library" +name = "sqlalchemy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.3.19" + +[package.extras] +mssql = ["pyodbc"] +mssql_pymssql = ["pymssql"] +mssql_pyodbc = ["pyodbc"] +mysql = ["mysqlclient"] +oracle = ["cx-oracle"] +postgresql = ["psycopg2"] +postgresql_pg8000 = ["pg8000"] +postgresql_psycopg2binary = ["psycopg2-binary"] +postgresql_psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql"] + +[[package]] +category = "main" +description = "Various utility functions for SQLAlchemy." +name = "sqlalchemy-utils" +optional = false +python-versions = "*" +version = "0.36.8" + +[package.dependencies] +SQLAlchemy = ">=1.0" +six = "*" + +[package.extras] +anyjson = ["anyjson (>=0.3.3)"] +arrow = ["arrow (>=0.3.4)"] +babel = ["Babel (>=1.3)"] +color = ["colour (>=0.0.4)"] +encrypted = ["cryptography (>=0.6)"] +intervals = ["intervals (>=0.7.1)"] +password = ["passlib (>=1.6,<2.0)"] +pendulum = ["pendulum (>=2.0.5)"] +phone = ["phonenumbers (>=5.9.2)"] +test = ["pytest (>=2.7.1)", "Pygments (>=1.2)", "Jinja2 (>=2.3)", "docutils (>=0.10)", "flexmock (>=0.9.7)", "mock (2.0.0)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pg8000 (>=1.12.4)", "pytz (>=2014.2)", "python-dateutil (>=2.6)", "pymysql", "flake8 (>=2.4.0)", "isort (>=4.2.2)", "pyodbc"] +test_all = ["anyjson (>=0.3.3)", "arrow (>=0.3.4)", "Babel (>=1.3)", "colour (>=0.0.4)", "cryptography (>=0.6)", "intervals (>=0.7.1)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "phonenumbers (>=5.9.2)", "pytest (>=2.7.1)", "Pygments (>=1.2)", "Jinja2 (>=2.3)", "docutils (>=0.10)", "flexmock (>=0.9.7)", "mock (2.0.0)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pg8000 (>=1.12.4)", "pytz (>=2014.2)", "python-dateutil (>=2.6)", "pymysql", "flake8 (>=2.4.0)", "isort (>=4.2.2)", "pyodbc", "python-dateutil", "furl (>=0.4.1)"] +timezone = ["python-dateutil"] +url = ["furl (>=0.4.1)"] + +[[package]] +category = "main" +description = "Strict, typed YAML parser" +name = "strictyaml" +optional = false +python-versions = "*" +version = "1.1.0" + +[package.dependencies] +python-dateutil = ">=2.6.0" +"ruamel.yaml" = ">=0.14.2" + +[[package]] +category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.1" + +[[package]] +category = "main" +description = "Traitlets Python configuration system" +name = "traitlets" +optional = false +python-versions = ">=3.7" +version = "5.0.4" + +[package.dependencies] +ipython-genutils = "*" + +[package.extras] +test = ["pytest"] + +[[package]] +category = "dev" +description = "a fork of Python 2 and 3 ast modules with type comment support" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.4.1" + +[[package]] +category = "main" +description = "Backported and Experimental Type Hints for Python 3.5+" +name = "typing-extensions" +optional = false +python-versions = "*" +version = "3.7.4.3" + +[[package]] +category = "main" +description = "ASCII transliterations of Unicode text" +name = "unidecode" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.1.1" + +[[package]] +category = "main" +description = "URI templates" +name = "uritemplate" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.0.1" + +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.25.10" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[[package]] +category = "dev" +description = "Virtual Python Environment builder" +name = "virtualenv" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "20.0.31" + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.1,<1" +filelock = ">=3.0.0,<4" +six = ">=1.9.0,<2" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = ">=0.12,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=5)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] + +[[package]] +category = "main" +description = "Python CloudWatch Logging" +name = "watchtower" +optional = false +python-versions = "*" +version = "0.8.0" + +[package.dependencies] +boto3 = ">=1.9.253,<2" + +[[package]] +category = "main" +description = "Measures the displayed width of unicode strings in a terminal" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.2.5" + +[[package]] +category = "main" +description = "A WebAuthn Python module." +name = "webauthn" +optional = false +python-versions = "*" +version = "0.4.7" + +[package.dependencies] +cbor2 = ">=4.0.1" +cryptography = ">=2.3.1" +future = ">=0.17.1" +pyOpenSSL = ">=16.0.0" +six = ">=1.11.0" + +[[package]] +category = "main" +description = "The comprehensive WSGI web application library." +name = "werkzeug" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "1.0.1" + +[package.extras] +dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] +watchdog = ["watchdog"] + +[[package]] +category = "main" +description = "A flexible forms validation and rendering library for Python web development." +name = "wtforms" +optional = false +python-versions = "*" +version = "2.3.3" + +[package.dependencies] +MarkupSafe = "*" + +[package.extras] +email = ["email-validator"] +ipaddress = ["ipaddress"] +locale = ["Babel (>=1.3)"] + +[[package]] +category = "main" +description = "A modern Cron replacement that is Docker-friendly" +name = "yacron" +optional = false +python-versions = ">=3.5" +version = "0.11.1" + +[package.dependencies] +aiohttp = ">=3.0" +aiosmtplib = "*" +crontab = "0.22.8" +jinja2 = "*" +pytz = "*" +sentry-sdk = "*" +strictyaml = ">=0.7.2" + +[[package]] +category = "main" +description = "Yet another URL library" +name = "yarl" +optional = false +python-versions = ">=3.5" +version = "1.6.0" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[package.dependencies.typing-extensions] +python = "<3.8" +version = ">=3.7.4" + +[[package]] +category = "dev" +description = "Backport of pathlib-compatible object wrapper for zip files" +marker = "python_version < \"3.8\"" +name = "zipp" +optional = false +python-versions = ">=3.6" +version = "3.2.0" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,<3.7.3 || >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"] + +[[package]] +category = "main" +description = "Very basic event publishing system" +name = "zope.event" +optional = false +python-versions = "*" +version = "4.5.0" + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["sphinx"] +test = ["zope.testrunner"] + +[[package]] +category = "main" +description = "Interfaces for Python" +name = "zope.interface" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "5.1.1" + +[package.dependencies] +setuptools = "*" + +[package.extras] +docs = ["sphinx", "repoze.sphinx.autointerface"] +test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] +testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] + +[metadata] +content-hash = "5303557051852045309a428fbcb5ca0e706aecfacfd5b398dbd7c38b72537efc" +lock-version = "1.0" +python-versions = "3.7" + +[metadata.files] +aiocontextvars = [ + {file = "aiocontextvars-0.2.2-py2.py3-none-any.whl", hash = "sha256:885daf8261818767d8f7cbd79f9d4482d118f024b6586ef6e67980236a27bfa3"}, + {file = "aiocontextvars-0.2.2.tar.gz", hash = "sha256:f027372dc48641f683c559f247bd84962becaacdc9ba711d583c3871fb5652aa"}, +] +aiohttp = [ + {file = "aiohttp-3.6.2-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e"}, + {file = "aiohttp-3.6.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec"}, + {file = "aiohttp-3.6.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48"}, + {file = "aiohttp-3.6.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59"}, + {file = "aiohttp-3.6.2-cp36-cp36m-win32.whl", hash = "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a"}, + {file = "aiohttp-3.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17"}, + {file = "aiohttp-3.6.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a"}, + {file = "aiohttp-3.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd"}, + {file = "aiohttp-3.6.2-cp37-cp37m-win32.whl", hash = "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965"}, + {file = "aiohttp-3.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654"}, + {file = "aiohttp-3.6.2-py3-none-any.whl", hash = "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4"}, + {file = "aiohttp-3.6.2.tar.gz", hash = "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326"}, +] +aiosmtpd = [ + {file = "aiosmtpd-1.2.tar.gz", hash = "sha256:b7ea7ee663f3b8514d3224d55c4e8827148277b124ea862a0bbfca1bc899aef5"}, +] +aiosmtplib = [ + {file = "aiosmtplib-1.1.4-py3-none-any.whl", hash = "sha256:93e53edac183f1a608bc34464efeef23902e59e949017b1682014f59ecdcd37d"}, + {file = "aiosmtplib-1.1.4.tar.gz", hash = "sha256:8270d0a06475aa05b9276fc954fbd08a1f6c59d0452b4899413d8bca1db24541"}, +] +aiospamc = [ + {file = "aiospamc-0.6.1-py3-none-any.whl", hash = "sha256:63b7d213d6af01058b855ddcde2147485ea4e685d6d13ee682ad12cb1fa87ca6"}, + {file = "aiospamc-0.6.1.tar.gz", hash = "sha256:4923bf3d1bf5a07151a3a9ea8be7862d9dcdef37a858035668ad1c726b7b98c1"}, +] +alembic = [ + {file = "alembic-1.4.3-py2.py3-none-any.whl", hash = "sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c"}, + {file = "alembic-1.4.3.tar.gz", hash = "sha256:5334f32314fb2a56d86b4c4dd1ae34b08c03cae4cb888bc699942104d66bc245"}, +] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +appnope = [ + {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, + {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, +] +arrow = [ + {file = "arrow-0.16.0-py2.py3-none-any.whl", hash = "sha256:98184d8dd3e5d30b96c2df4596526f7de679ccb467f358b82b0f686436f3a6b8"}, + {file = "arrow-0.16.0.tar.gz", hash = "sha256:92aac856ea5175c804f7ccb96aca4d714d936f1c867ba59d747a8096ec30e90a"}, +] +async-timeout = [ + {file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"}, + {file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +atpublic = [ + {file = "atpublic-2.0.tar.gz", hash = "sha256:ebeb62b71a5c683a84c1b16bbf415708af5a46841b142b85ac3a22ec2d7613b0"}, +] +attrs = [ + {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"}, + {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"}, +] +backcall = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] +bcrypt = [ + {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, + {file = "bcrypt-3.2.0-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7"}, + {file = "bcrypt-3.2.0-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1"}, + {file = "bcrypt-3.2.0-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d"}, + {file = "bcrypt-3.2.0-cp36-abi3-win32.whl", hash = "sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55"}, + {file = "bcrypt-3.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34"}, + {file = "bcrypt-3.2.0.tar.gz", hash = "sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29"}, +] +black = [ + {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, +] +blinker = [ + {file = "blinker-1.4.tar.gz", hash = "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"}, +] +boto3 = [ + {file = "boto3-1.15.9-py2.py3-none-any.whl", hash = "sha256:e0a1dbc0a0e460dc6de2f4144b5015edad3ab5c17ee83c6194b1a010d815bc60"}, + {file = "boto3-1.15.9.tar.gz", hash = "sha256:02f5f7a2b1349760b030c34f90a9cb4600bf8fe3cbc76b801d122bc4cecf3a7f"}, +] +botocore = [ + {file = "botocore-1.18.9-py2.py3-none-any.whl", hash = "sha256:dc3244170254cbba7dfde00b0489f830069d93dd6a9e555178d989072d7ee7c2"}, + {file = "botocore-1.18.9.tar.gz", hash = "sha256:35b06b8801eb2dd7e708de35581f9c0304740645874f3af5b8b0c1648f8d6365"}, +] +cachetools = [ + {file = "cachetools-4.1.1-py3-none-any.whl", hash = "sha256:513d4ff98dd27f85743a8dc0e92f55ddb1b49e060c2d5961512855cda2c01a98"}, + {file = "cachetools-4.1.1.tar.gz", hash = "sha256:bbaa39c3dede00175df2dc2b03d0cf18dd2d32a7de7beb68072d13043c9edb20"}, +] +cbor2 = [ + {file = "cbor2-5.2.0.tar.gz", hash = "sha256:a33aa2e5534fd74401ac95686886e655e3b2ce6383b3f958199b6e70a87c94bf"}, +] +certifi = [ + {file = "certifi-2019.11.28-py2.py3-none-any.whl", hash = "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3"}, + {file = "certifi-2019.11.28.tar.gz", hash = "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"}, +] +cffi = [ + {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"}, + {file = "cffi-1.14.3-2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768"}, + {file = "cffi-1.14.3-2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d"}, + {file = "cffi-1.14.3-2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1"}, + {file = "cffi-1.14.3-2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca"}, + {file = "cffi-1.14.3-2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a"}, + {file = "cffi-1.14.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c"}, + {file = "cffi-1.14.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730"}, + {file = "cffi-1.14.3-cp27-cp27m-win32.whl", hash = "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d"}, + {file = "cffi-1.14.3-cp27-cp27m-win_amd64.whl", hash = "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05"}, + {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b"}, + {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171"}, + {file = "cffi-1.14.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f"}, + {file = "cffi-1.14.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4"}, + {file = "cffi-1.14.3-cp35-cp35m-win32.whl", hash = "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d"}, + {file = "cffi-1.14.3-cp35-cp35m-win_amd64.whl", hash = "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d"}, + {file = "cffi-1.14.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3"}, + {file = "cffi-1.14.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808"}, + {file = "cffi-1.14.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537"}, + {file = "cffi-1.14.3-cp36-cp36m-win32.whl", hash = "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0"}, + {file = "cffi-1.14.3-cp36-cp36m-win_amd64.whl", hash = "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e"}, + {file = "cffi-1.14.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1"}, + {file = "cffi-1.14.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579"}, + {file = "cffi-1.14.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394"}, + {file = "cffi-1.14.3-cp37-cp37m-win32.whl", hash = "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc"}, + {file = "cffi-1.14.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869"}, + {file = "cffi-1.14.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e"}, + {file = "cffi-1.14.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828"}, + {file = "cffi-1.14.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9"}, + {file = "cffi-1.14.3-cp38-cp38-win32.whl", hash = "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522"}, + {file = "cffi-1.14.3-cp38-cp38-win_amd64.whl", hash = "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15"}, + {file = "cffi-1.14.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d"}, + {file = "cffi-1.14.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c"}, + {file = "cffi-1.14.3-cp39-cp39-win32.whl", hash = "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b"}, + {file = "cffi-1.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3"}, + {file = "cffi-1.14.3.tar.gz", hash = "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"}, +] +cfgv = [ + {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, + {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, +] +chardet = [ + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] +colorama = [ + {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, + {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, +] +coloredlogs = [ + {file = "coloredlogs-14.0-py2.py3-none-any.whl", hash = "sha256:346f58aad6afd48444c2468618623638dadab76e4e70d5e10822676f2d32226a"}, + {file = "coloredlogs-14.0.tar.gz", hash = "sha256:a1fab193d2053aa6c0a97608c4342d031f1f93a3d1218432c59322441d31a505"}, +] +crontab = [ + {file = "crontab-0.22.8.tar.gz", hash = "sha256:1ac977fb1b8ba5b7b58e6f713cd7df36e61d7aee4c2b809abcf76adddd2deeaf"}, +] +cryptography = [ + {file = "cryptography-3.1.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:65beb15e7f9c16e15934569d29fb4def74ea1469d8781f6b3507ab896d6d8719"}, + {file = "cryptography-3.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:983c0c3de4cb9fcba68fd3f45ed846eb86a2a8b8d8bc5bb18364c4d00b3c61fe"}, + {file = "cryptography-3.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e97a3b627e3cb63c415a16245d6cef2139cca18bb1183d1b9375a1c14e83f3b3"}, + {file = "cryptography-3.1.1-cp27-cp27m-win32.whl", hash = "sha256:cb179acdd4ae1e4a5a160d80b87841b3d0e0be84af46c7bb2cd7ece57a39c4ba"}, + {file = "cryptography-3.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:b372026ebf32fe2523159f27d9f0e9f485092e43b00a5adacf732192a70ba118"}, + {file = "cryptography-3.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:680da076cad81cdf5ffcac50c477b6790be81768d30f9da9e01960c4b18a66db"}, + {file = "cryptography-3.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5d52c72449bb02dd45a773a203196e6d4fae34e158769c896012401f33064396"}, + {file = "cryptography-3.1.1-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:f0e099fc4cc697450c3dd4031791559692dd941a95254cb9aeded66a7aa8b9bc"}, + {file = "cryptography-3.1.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a7597ffc67987b37b12e09c029bd1dc43965f75d328076ae85721b84046e9ca7"}, + {file = "cryptography-3.1.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:4549b137d8cbe3c2eadfa56c0c858b78acbeff956bd461e40000b2164d9167c6"}, + {file = "cryptography-3.1.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:89aceb31cd5f9fc2449fe8cf3810797ca52b65f1489002d58fe190bfb265c536"}, + {file = "cryptography-3.1.1-cp35-cp35m-win32.whl", hash = "sha256:559d622aef2a2dff98a892eef321433ba5bc55b2485220a8ca289c1ecc2bd54f"}, + {file = "cryptography-3.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:451cdf60be4dafb6a3b78802006a020e6cd709c22d240f94f7a0696240a17154"}, + {file = "cryptography-3.1.1-cp36-abi3-win32.whl", hash = "sha256:762bc5a0df03c51ee3f09c621e1cee64e3a079a2b5020de82f1613873d79ee70"}, + {file = "cryptography-3.1.1-cp36-abi3-win_amd64.whl", hash = "sha256:b12e715c10a13ca1bd27fbceed9adc8c5ff640f8e1f7ea76416352de703523c8"}, + {file = "cryptography-3.1.1-cp36-cp36m-win32.whl", hash = "sha256:21b47c59fcb1c36f1113f3709d37935368e34815ea1d7073862e92f810dc7499"}, + {file = "cryptography-3.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:48ee615a779ffa749d7d50c291761dc921d93d7cf203dca2db663b4f193f0e49"}, + {file = "cryptography-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:b2bded09c578d19e08bd2c5bb8fed7f103e089752c9cf7ca7ca7de522326e921"}, + {file = "cryptography-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f99317a0fa2e49917689b8cf977510addcfaaab769b3f899b9c481bbd76730c2"}, + {file = "cryptography-3.1.1-cp38-cp38-win32.whl", hash = "sha256:ab010e461bb6b444eaf7f8c813bb716be2d78ab786103f9608ffd37a4bd7d490"}, + {file = "cryptography-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:99d4984aabd4c7182050bca76176ce2dbc9fa9748afe583a7865c12954d714ba"}, + {file = "cryptography-3.1.1.tar.gz", hash = "sha256:9d9fc6a16357965d282dd4ab6531013935425d0dc4950df2e0cf2a1b1ac1017d"}, +] +decorator = [ + {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, + {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, +] +distlib = [ + {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, + {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, +] +dkimpy = [ + {file = "dkimpy-1.0.5.tar.gz", hash = "sha256:9a2420bf09af686736773153fca32a02ae11ecbe24b540c26104628959f91121"}, +] +dnspython = [ + {file = "dnspython-2.0.0-py3-none-any.whl", hash = "sha256:40bb3c24b9d4ec12500f0124288a65df232a3aa749bb0c39734b782873a2544d"}, + {file = "dnspython-2.0.0.zip", hash = "sha256:044af09374469c3a39eeea1a146e8cac27daec951f1f1f157b1962fc7cb9d1b7"}, +] +facebook-sdk = [ + {file = "facebook-sdk-3.1.0.tar.gz", hash = "sha256:cabcd2e69ea3d9f042919c99b353df7aa1e2be86d040121f6e9f5e63c1cf0f8d"}, + {file = "facebook_sdk-3.1.0-py2.py3-none-any.whl", hash = "sha256:2e987b3e0f466a6f4ee77b935eb023dba1384134f004a2af21f1cfff7fe0806e"}, +] +filelock = [ + {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, + {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, +] +flask = [ + {file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"}, + {file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"}, +] +flask-admin = [ + {file = "Flask-Admin-1.5.6.tar.gz", hash = "sha256:68c761d8582d59b1f7702013e944a7ad11d7659a72f3006b89b68b0bd8df61b8"}, +] +flask-cors = [ + {file = "Flask-Cors-3.0.9.tar.gz", hash = "sha256:6bcfc100288c5d1bcb1dbb854babd59beee622ffd321e444b05f24d6d58466b8"}, + {file = "Flask_Cors-3.0.9-py2.py3-none-any.whl", hash = "sha256:cee4480aaee421ed029eaa788f4049e3e26d15b5affb6a880dade6bafad38324"}, +] +flask-debugtoolbar = [ + {file = "Flask-DebugToolbar-0.11.0.tar.gz", hash = "sha256:3c4e79d354ede014e6657c545a536d4fb273cc89e3fd6b4835b02e346dd3aab4"}, + {file = "Flask_DebugToolbar-0.11.0-py2.py3-none-any.whl", hash = "sha256:0e9a80d4c599233c68376e81cc99976200b5ac5248cfb24f18935cc5b69ac5b3"}, +] +flask-httpauth = [ + {file = "Flask-HTTPAuth-4.1.0.tar.gz", hash = "sha256:9e028e4375039a49031eb9ecc40be4761f0540476040f6eff329a31dabd4d000"}, + {file = "Flask_HTTPAuth-4.1.0-py2.py3-none-any.whl", hash = "sha256:29e0288869a213c7387f0323b6bf2c7191584fb1da8aa024d9af118e5cd70de7"}, +] +flask-limiter = [ + {file = "Flask-Limiter-1.4.tar.gz", hash = "sha256:021279c905a1e24f181377ab3be711be7541734b494f4e6db2b8edeba7601e48"}, + {file = "Flask_Limiter-1.4-py3-none-any.whl", hash = "sha256:f8a65a7874f48ff8df2ea5e86d5b85b48fcbae065ebeb5271b317fe68fcfa979"}, + {file = "Flask_Limiter-1.4-py3.7.egg", hash = "sha256:055a388a89f4d5768c64025443f1f41e3babcbbbf315c728413c27b4975af239"}, +] +flask-login = [ + {file = "Flask-Login-0.5.0.tar.gz", hash = "sha256:6d33aef15b5bcead780acc339464aae8a6e28f13c90d8b1cf9de8b549d1c0b4b"}, + {file = "Flask_Login-0.5.0-py2.py3-none-any.whl", hash = "sha256:7451b5001e17837ba58945aead261ba425fdf7b4f0448777e597ddab39f4fba0"}, +] +flask-migrate = [ + {file = "Flask-Migrate-2.5.3.tar.gz", hash = "sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee"}, + {file = "Flask_Migrate-2.5.3-py2.py3-none-any.whl", hash = "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732"}, +] +flask-profiler = [ + {file = "flask_profiler-1.8.1-py3-none-any.whl", hash = "sha256:56b51e47c98e9f36150fafffd449b075c58a9b404389a071d405222a35183758"}, + {file = "flask_profiler-1.8.1.tar.gz", hash = "sha256:fc9f2875a4f22223ddc04ffacd75792854162c4cdbef165598a51f898521ac51"}, +] +flask-sqlalchemy = [ + {file = "Flask-SQLAlchemy-2.4.4.tar.gz", hash = "sha256:bfc7150eaf809b1c283879302f04c42791136060c6eeb12c0c6674fb1291fae5"}, + {file = "Flask_SQLAlchemy-2.4.4-py2.py3-none-any.whl", hash = "sha256:05b31d2034dd3f2a685cbbae4cfc4ed906b2a733cff7964ada450fd5e462b84e"}, +] +flask-wtf = [ + {file = "Flask-WTF-0.14.3.tar.gz", hash = "sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720"}, + {file = "Flask_WTF-0.14.3-py2.py3-none-any.whl", hash = "sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2"}, +] +future = [ + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +] +gevent = [ + {file = "gevent-20.9.0-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:1628a403fc9c3ea9b35924638a4d4fbe236f60ecdf4e22ed133fbbaf0bc7cb6b"}, + {file = "gevent-20.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:283a021a2e14adfad718346f18982b80569d9c3a59e97cfae1b7d4c5b017941a"}, + {file = "gevent-20.9.0-cp27-cp27m-win32.whl", hash = "sha256:315a63a35068183dfb9bc0331c7bb3c265ee7db8a11797cbe98dadbdb45b5d35"}, + {file = "gevent-20.9.0-cp27-cp27m-win_amd64.whl", hash = "sha256:324808a8558c733f7a9734525483795d52ca3bbd5662b24b361d81c075414b1f"}, + {file = "gevent-20.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:2aa70726ad1883fe7c17774e5ccc91ac6e30334efa29bafb9b8fe8ca6091b219"}, + {file = "gevent-20.9.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:dd4c6b2f540b25c3d0f277a725bc1a900ce30a681b90a081216e31f814be453b"}, + {file = "gevent-20.9.0-cp35-cp35m-win32.whl", hash = "sha256:1cfa3674866294623e324fa5b76eba7b96744d1956a605cfe24d26c5cd890f91"}, + {file = "gevent-20.9.0-cp35-cp35m-win_amd64.whl", hash = "sha256:906175e3fb25f377a0b581e79d3ed5a7d925c136ff92fd022bb3013e25f5f3a9"}, + {file = "gevent-20.9.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:fb33dc1ab27557bccd64ad4bf81e68c8b0d780fe937b1e2c0814558798137229"}, + {file = "gevent-20.9.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:eba19bae532d0c48d489fa16815b242ce074b1f4b63e8a8e663232cbe311ead9"}, + {file = "gevent-20.9.0-cp36-cp36m-win32.whl", hash = "sha256:db208e74a32cff7f55f5aa1ba5d7d1c1a086a6325c8702ae78a5c741155552ff"}, + {file = "gevent-20.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2269574444113cb4ca1c1808ab9460a87fe25e1c34a6e36d975d4af46e4afff9"}, + {file = "gevent-20.9.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:adbb267067f56696b2babced3d0856aa39dcf14b8ccd2dffa1fab587b00c6f80"}, + {file = "gevent-20.9.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:9bb477f514cf39dc20651b479bf1ad4f38b9a679be2bfa3e162ec0c3785dfa2a"}, + {file = "gevent-20.9.0-cp37-cp37m-win32.whl", hash = "sha256:10110d4881aec04f218c316cb796b18c8b2cac67ae0eb5b0c5780056757268a2"}, + {file = "gevent-20.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e11de4b4d107ca2f35000eb08e9c4c4621c153103b400f48a9ea95b96d8c7e0b"}, + {file = "gevent-20.9.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:a8733a01974433d91308f8c44fa6cc13428b15bb39d46540657e260ff8852cb1"}, + {file = "gevent-20.9.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:afc177c37de41ce9c27d351ac84cbaf34407effcab5d6641645838f39d365be1"}, + {file = "gevent-20.9.0-cp38-cp38-win32.whl", hash = "sha256:93980e51dd2e5f81899d644a0b6ef4a73008c679fcedd50e3b21cc3451ba2424"}, + {file = "gevent-20.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:b2948566003a1030e47507755fe1f446995e8671c0c67571091539e01faf94cc"}, + {file = "gevent-20.9.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b07fcbca3e819296979d82fac3d8b44f0d5ced57b9a04dffcfd194da99c8eb2d"}, + {file = "gevent-20.9.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33a63f230755c6813fca39d9cea2a8894df32df2ee58fd69d8bf8fcc1d8e018e"}, + {file = "gevent-20.9.0-pp27-pypy_73-win32.whl", hash = "sha256:8d338cd6d040fe2607e5305dd7991b5960b3780ae01f804c2ac5760d31d3b2c6"}, + {file = "gevent-20.9.0.tar.gz", hash = "sha256:5f6d48051d336561ec08995431ee4d265ac723a64bba99cc58c3eb1a4d4f5c8d"}, +] +google-api-core = [ + {file = "google-api-core-1.22.2.tar.gz", hash = "sha256:779107f17e0fef8169c5239d56a8fbff03f9f72a3893c0c9e5842ec29dfedd54"}, + {file = "google_api_core-1.22.2-py2.py3-none-any.whl", hash = "sha256:67e33a852dcca7cb7eff49abc35c8cc2c0bb8ab11397dc8306d911505cae2990"}, +] +google-api-python-client = [ + {file = "google-api-python-client-1.12.3.tar.gz", hash = "sha256:844ef76bda585ea0ea2d5e7f8f9a0eb10d6e2eba66c4fea0210ec7843941cb1a"}, + {file = "google_api_python_client-1.12.3-py2.py3-none-any.whl", hash = "sha256:5b3f4908c041f3109135841cf7e49fc4477f26f7d02b6db72753a17f8b338d01"}, +] +google-auth = [ + {file = "google-auth-1.22.0.tar.gz", hash = "sha256:a73e6fb6d232ed1293ef9a5301e6f8aada7880d19c65d7f63e130dc50ec05593"}, + {file = "google_auth-1.22.0-py2.py3-none-any.whl", hash = "sha256:e86e72142d939a8d90a772947268aacc127ab7a1d1d6f3e0fecca7a8d74d8257"}, +] +google-auth-httplib2 = [ + {file = "google-auth-httplib2-0.0.4.tar.gz", hash = "sha256:8d092cc60fb16517b12057ec0bba9185a96e3b7169d86ae12eae98e645b7bc39"}, + {file = "google_auth_httplib2-0.0.4-py2.py3-none-any.whl", hash = "sha256:aeaff501738b289717fac1980db9711d77908a6c227f60e4aa1923410b43e2ee"}, +] +googleapis-common-protos = [ + {file = "googleapis-common-protos-1.52.0.tar.gz", hash = "sha256:560716c807117394da12cecb0a54da5a451b5cf9866f1d37e9a5e2329a665351"}, + {file = "googleapis_common_protos-1.52.0-py2.py3-none-any.whl", hash = "sha256:c8961760f5aad9a711d37b675be103e0cc4e9a39327e0d6d857872f698403e24"}, +] +greenlet = [ + {file = "greenlet-0.4.17-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:75e4c27188f28149b74e7685809f9227410fd15432a4438fc48627f518577fa5"}, + {file = "greenlet-0.4.17-cp27-cp27m-win32.whl", hash = "sha256:3af587e9813f9bd8be9212722321a5e7be23b2bc37e6323a90e592ab0c2ef117"}, + {file = "greenlet-0.4.17-cp27-cp27m-win_amd64.whl", hash = "sha256:ccd62f09f90b2730150d82f2f2ffc34d73c6ce7eac234aed04d15dc8a3023994"}, + {file = "greenlet-0.4.17-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:13037e2d7ab2145300676852fa069235512fdeba4ed1e3bb4b0677a04223c525"}, + {file = "greenlet-0.4.17-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e495096e3e2e8f7192afb6aaeba19babc4fb2bdf543d7b7fed59e00c1df7f170"}, + {file = "greenlet-0.4.17-cp35-cp35m-win32.whl", hash = "sha256:124a3ae41215f71dc91d1a3d45cbf2f84e46b543e5d60b99ecc20e24b4c8f272"}, + {file = "greenlet-0.4.17-cp35-cp35m-win_amd64.whl", hash = "sha256:5494e3baeacc371d988345fbf8aa4bd15555b3077c40afcf1994776bb6d77eaf"}, + {file = "greenlet-0.4.17-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bee111161420f341a346731279dd976be161b465c1286f82cc0779baf7b729e8"}, + {file = "greenlet-0.4.17-cp36-cp36m-win32.whl", hash = "sha256:ac85db59aa43d78547f95fc7b6fd2913e02b9e9b09e2490dfb7bbdf47b2a4914"}, + {file = "greenlet-0.4.17-cp36-cp36m-win_amd64.whl", hash = "sha256:4481002118b2f1588fa3d821936ffdc03db80ef21186b62b90c18db4ba5e743b"}, + {file = "greenlet-0.4.17-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:be7a79988b8fdc5bbbeaed69e79cfb373da9759242f1565668be4fb7f3f37552"}, + {file = "greenlet-0.4.17-cp37-cp37m-win32.whl", hash = "sha256:97f2b01ab622a4aa4b3724a3e1fba66f47f054c434fbaa551833fa2b41e3db51"}, + {file = "greenlet-0.4.17-cp37-cp37m-win_amd64.whl", hash = "sha256:d3436110ca66fe3981031cc6aff8cc7a40d8411d173dde73ddaa5b8445385e2d"}, + {file = "greenlet-0.4.17-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a34023b9eabb3525ee059f3bf33a417d2e437f7f17e341d334987d4091ae6072"}, + {file = "greenlet-0.4.17-cp38-cp38-win32.whl", hash = "sha256:e66a824f44892bc4ec66c58601a413419cafa9cec895e63d8da889c8a1a4fa4a"}, + {file = "greenlet-0.4.17-cp38-cp38-win_amd64.whl", hash = "sha256:47825c3a109f0331b1e54c1173d4e57fa000aa6c96756b62852bfa1af91cd652"}, + {file = "greenlet-0.4.17-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1023d7b43ca11264ab7052cb09f5635d4afdb43df55e0854498fc63070a0b206"}, + {file = "greenlet-0.4.17.tar.gz", hash = "sha256:41d8835c69a78de718e466dd0e6bfd4b46125f21a67c3ff6d76d8d8059868d6b"}, +] +gunicorn = [ + {file = "gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"}, + {file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"}, +] +httplib2 = [ + {file = "httplib2-0.18.1-py3-none-any.whl", hash = "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782"}, + {file = "httplib2-0.18.1.tar.gz", hash = "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3"}, +] +humanfriendly = [ + {file = "humanfriendly-8.2-py2.py3-none-any.whl", hash = "sha256:e78960b31198511f45fd455534ae7645a6207d33e512d2e842c766d15d9c8080"}, + {file = "humanfriendly-8.2.tar.gz", hash = "sha256:bf52ec91244819c780341a3438d5d7b09f431d3f113a475147ac9b7b167a3d12"}, +] +identify = [ + {file = "identify-1.5.5-py2.py3-none-any.whl", hash = "sha256:da683bfb7669fa749fc7731f378229e2dbf29a1d1337cbde04106f02236eb29d"}, + {file = "identify-1.5.5.tar.gz", hash = "sha256:7c22c384a2c9b32c5cc891d13f923f6b2653aa83e2d75d8f79be240d6c86c4f4"}, +] +idna = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] +importlib-metadata = [ + {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"}, + {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, +] +iniconfig = [ + {file = "iniconfig-1.0.1-py3-none-any.whl", hash = "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437"}, + {file = "iniconfig-1.0.1.tar.gz", hash = "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"}, +] +ipython = [ + {file = "ipython-7.18.1-py3-none-any.whl", hash = "sha256:2e22c1f74477b5106a6fb301c342ab8c64bb75d702e350f05a649e8cb40a0fb8"}, + {file = "ipython-7.18.1.tar.gz", hash = "sha256:a331e78086001931de9424940699691ad49dfb457cea31f5471eae7b78222d5e"}, +] +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"}, +] +itsdangerous = [ + {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, + {file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, +] +jedi = [ + {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, + {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, +] +jinja2 = [ + {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, + {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, +] +jmespath = [ + {file = "jmespath-0.10.0-py2.py3-none-any.whl", hash = "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"}, + {file = "jmespath-0.10.0.tar.gz", hash = "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9"}, +] +jwcrypto = [ + {file = "jwcrypto-0.8-py2.py3-none-any.whl", hash = "sha256:16e17faa4dce36551ade3a3ccb06236a61e5924ea1db163c9be9827acf935a82"}, + {file = "jwcrypto-0.8.tar.gz", hash = "sha256:b7fee2635bbefdf145399392f5be26ad54161c8271c66b5fe107b4b452f06c24"}, +] +limits = [ + {file = "limits-1.5.1-py2-none-any.whl", hash = "sha256:0e5f8b10f18dd809eb2342f5046eb9aa5e4e69a0258567b5f4aa270647d438b3"}, + {file = "limits-1.5.1.tar.gz", hash = "sha256:f0c3319f032c4bfad68438ed1325c0fac86dac64582c7c25cddc87a0b658fa20"}, +] +mako = [ + {file = "Mako-1.1.3-py2.py3-none-any.whl", hash = "sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9"}, + {file = "Mako-1.1.3.tar.gz", hash = "sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27"}, +] +markupsafe = [ + {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] +memory-profiler = [ + {file = "memory_profiler-0.57.0.tar.gz", hash = "sha256:23b196f91ea9ac9996e30bfab1e82fecc30a4a1d24870e81d1e81625f786a2c3"}, +] +multidict = [ + {file = "multidict-4.7.6-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000"}, + {file = "multidict-4.7.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a"}, + {file = "multidict-4.7.6-cp35-cp35m-win32.whl", hash = "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5"}, + {file = "multidict-4.7.6-cp35-cp35m-win_amd64.whl", hash = "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3"}, + {file = "multidict-4.7.6-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87"}, + {file = "multidict-4.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2"}, + {file = "multidict-4.7.6-cp36-cp36m-win32.whl", hash = "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7"}, + {file = "multidict-4.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463"}, + {file = "multidict-4.7.6-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"}, + {file = "multidict-4.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255"}, + {file = "multidict-4.7.6-cp37-cp37m-win32.whl", hash = "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507"}, + {file = "multidict-4.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c"}, + {file = "multidict-4.7.6-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b"}, + {file = "multidict-4.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7"}, + {file = "multidict-4.7.6-cp38-cp38-win32.whl", hash = "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d"}, + {file = "multidict-4.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19"}, + {file = "multidict-4.7.6.tar.gz", hash = "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +nodeenv = [ + {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, + {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, +] +oauthlib = [ + {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"}, + {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"}, +] +packaging = [ + {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, + {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, +] +parso = [ + {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, + {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, +] +pathspec = [ + {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"}, + {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, +] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +phpserialize = [ + {file = "phpserialize-1.3.tar.gz", hash = "sha256:bf672d312d203d09a84c26366fab8f438a3ffb355c407e69974b7ef2d39a0fa7"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +pre-commit = [ + {file = "pre_commit-2.7.1-py2.py3-none-any.whl", hash = "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a"}, + {file = "pre_commit-2.7.1.tar.gz", hash = "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.7-py3-none-any.whl", hash = "sha256:83074ee28ad4ba6af190593d4d4c607ff525272a504eb159199b6dd9f950c950"}, + {file = "prompt_toolkit-3.0.7.tar.gz", hash = "sha256:822f4605f28f7d2ba6b0b09a31e25e140871e96364d1d377667b547bb3bf4489"}, +] +protobuf = [ + {file = "protobuf-3.13.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9c2e63c1743cba12737169c447374fab3dfeb18111a460a8c1a000e35836b18c"}, + {file = "protobuf-3.13.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1e834076dfef9e585815757a2c7e4560c7ccc5962b9d09f831214c693a91b463"}, + {file = "protobuf-3.13.0-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:df3932e1834a64b46ebc262e951cd82c3cf0fa936a154f0a42231140d8237060"}, + {file = "protobuf-3.13.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8c35bcbed1c0d29b127c886790e9d37e845ffc2725cc1db4bd06d70f4e8359f4"}, + {file = "protobuf-3.13.0-cp35-cp35m-win32.whl", hash = "sha256:339c3a003e3c797bc84499fa32e0aac83c768e67b3de4a5d7a5a9aa3b0da634c"}, + {file = "protobuf-3.13.0-cp35-cp35m-win_amd64.whl", hash = "sha256:361acd76f0ad38c6e38f14d08775514fbd241316cce08deb2ce914c7dfa1184a"}, + {file = "protobuf-3.13.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9edfdc679a3669988ec55a989ff62449f670dfa7018df6ad7f04e8dbacb10630"}, + {file = "protobuf-3.13.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5db9d3e12b6ede5e601b8d8684a7f9d90581882925c96acf8495957b4f1b204b"}, + {file = "protobuf-3.13.0-cp36-cp36m-win32.whl", hash = "sha256:c8abd7605185836f6f11f97b21200f8a864f9cb078a193fe3c9e235711d3ff1e"}, + {file = "protobuf-3.13.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4d1174c9ed303070ad59553f435846a2f877598f59f9afc1b89757bdf846f2a7"}, + {file = "protobuf-3.13.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0bba42f439bf45c0f600c3c5993666fcb88e8441d011fad80a11df6f324eef33"}, + {file = "protobuf-3.13.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c0c5ab9c4b1eac0a9b838f1e46038c3175a95b0f2d944385884af72876bd6bc7"}, + {file = "protobuf-3.13.0-cp37-cp37m-win32.whl", hash = "sha256:f68eb9d03c7d84bd01c790948320b768de8559761897763731294e3bc316decb"}, + {file = "protobuf-3.13.0-cp37-cp37m-win_amd64.whl", hash = "sha256:91c2d897da84c62816e2f473ece60ebfeab024a16c1751aaf31100127ccd93ec"}, + {file = "protobuf-3.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3dee442884a18c16d023e52e32dd34a8930a889e511af493f6dc7d4d9bf12e4f"}, + {file = "protobuf-3.13.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:e7662437ca1e0c51b93cadb988f9b353fa6b8013c0385d63a70c8a77d84da5f9"}, + {file = "protobuf-3.13.0-py2.py3-none-any.whl", hash = "sha256:d69697acac76d9f250ab745b46c725edf3e98ac24763990b24d58c16c642947a"}, + {file = "protobuf-3.13.0.tar.gz", hash = "sha256:6a82e0c8bb2bf58f606040cc5814e07715b2094caeba281e2e7d0b0e2e397db5"}, +] +psutil = [ + {file = "psutil-5.7.2-cp27-none-win32.whl", hash = "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2"}, + {file = "psutil-5.7.2-cp27-none-win_amd64.whl", hash = "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195"}, + {file = "psutil-5.7.2-cp35-cp35m-win32.whl", hash = "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c"}, + {file = "psutil-5.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6"}, + {file = "psutil-5.7.2-cp36-cp36m-win32.whl", hash = "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf"}, + {file = "psutil-5.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8"}, + {file = "psutil-5.7.2-cp37-cp37m-win32.whl", hash = "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818"}, + {file = "psutil-5.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1"}, + {file = "psutil-5.7.2-cp38-cp38-win32.whl", hash = "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498"}, + {file = "psutil-5.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f"}, + {file = "psutil-5.7.2.tar.gz", hash = "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb"}, +] +psycopg2-binary = [ + {file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-win32.whl", hash = "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27m-win_amd64.whl", hash = "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c"}, + {file = "psycopg2_binary-2.8.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1"}, + {file = "psycopg2_binary-2.8.6-cp34-cp34m-win32.whl", hash = "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2"}, + {file = "psycopg2_binary-2.8.6-cp34-cp34m-win_amd64.whl", hash = "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-win32.whl", hash = "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd"}, + {file = "psycopg2_binary-2.8.6-cp35-cp35m-win_amd64.whl", hash = "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-win32.whl", hash = "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2"}, + {file = "psycopg2_binary-2.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-win32.whl", hash = "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67"}, + {file = "psycopg2_binary-2.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-macosx_10_9_x86_64.macosx_10_9_intel.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-win32.whl", hash = "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729"}, + {file = "psycopg2_binary-2.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77"}, + {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52"}, + {file = "psycopg2_binary-2.8.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd"}, +] +ptyprocess = [ + {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, + {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, +] +py = [ + {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, + {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, +] +pyasn1 = [ + {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, + {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, + {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, + {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, + {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, + {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, + {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, + {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, + {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, + {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, + {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, + {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, + {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, +] +pyasn1-modules = [ + {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, + {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"}, + {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"}, + {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"}, + {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"}, + {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, + {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"}, + {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"}, + {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"}, + {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"}, + {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"}, + {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"}, + {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, +] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +pycryptodome = [ + {file = "pycryptodome-3.9.8-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:50348edd283afdccddc0938cdc674484533912ba8a99a27c7bfebb75030aa856"}, + {file = "pycryptodome-3.9.8-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:80d57177a0b7c14d4594c62bbb47fe2f6309ad3b0a34348a291d570925c97a82"}, + {file = "pycryptodome-3.9.8-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:fbe65d5cfe04ff2f7684160d50f5118bdefb01e3af4718eeb618bfed40f19d94"}, + {file = "pycryptodome-3.9.8-cp27-cp27m-win32.whl", hash = "sha256:bcd5b8416e73e4b0d48afba3704d8c826414764dafaed7a1a93c442188d90ccc"}, + {file = "pycryptodome-3.9.8-cp27-cp27m-win_amd64.whl", hash = "sha256:360955eece2cd0fa694a708d10303c6abd7b39614fa2547b6bd245da76198beb"}, + {file = "pycryptodome-3.9.8-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:1e655746f539421d923fd48df8f6f40b3443d80b75532501c0085b64afed9df5"}, + {file = "pycryptodome-3.9.8-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:709b9f144d23e290b9863121d1ace14a72e01f66ea9c903fbdc690520dfdfcf0"}, + {file = "pycryptodome-3.9.8-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:6276478ada411aca97c0d5104916354b3d740d368407912722bd4d11aa9ee4c2"}, + {file = "pycryptodome-3.9.8-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:54bdedd28476dea8a3cd86cb67c0df1f0e3d71cae8022354b0f879c41a3d27b2"}, + {file = "pycryptodome-3.9.8-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f521178e5a991ffd04182ed08f552daca1affcb826aeda0e1945cd989a9d4345"}, + {file = "pycryptodome-3.9.8-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:f2e045224074d5664dc9cbabbf4f4d4d46f1ee90f24780e3a9a668fd096ff17f"}, + {file = "pycryptodome-3.9.8-cp35-cp35m-win32.whl", hash = "sha256:a207231a52426de3ff20f5608f0687261a3329d97a036c51f7d4c606a6f30c23"}, + {file = "pycryptodome-3.9.8-cp35-cp35m-win_amd64.whl", hash = "sha256:2b998dc45ef5f4e5cf5248a6edfcd8d8e9fb5e35df8e4259b13a1b10eda7b16b"}, + {file = "pycryptodome-3.9.8-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:03d5cca8618620f45fd40f827423f82b86b3a202c8d44108601b0f5f56b04299"}, + {file = "pycryptodome-3.9.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f78a68c2c820e4731e510a2df3eef0322f24fde1781ced970bf497b6c7d92982"}, + {file = "pycryptodome-3.9.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:132a56abba24e2e06a479d8e5db7a48271a73a215f605017bbd476d31f8e71c1"}, + {file = "pycryptodome-3.9.8-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:cecbf67e81d6144a50dc615629772859463b2e4f815d0c082fa421db362f040e"}, + {file = "pycryptodome-3.9.8-cp36-cp36m-win32.whl", hash = "sha256:67dcad1b8b201308586a8ca2ffe89df1e4f731d5a4cdd0610cc4ea790351c739"}, + {file = "pycryptodome-3.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:b56638d58a3a4be13229c6a815cd448f9e3ce40c00880a5398471b42ee86f50e"}, + {file = "pycryptodome-3.9.8-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:bec2bcdf7c9ce7f04d718e51887f3b05dc5c1cfaf5d2c2e9065ecddd1b2f6c9a"}, + {file = "pycryptodome-3.9.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:abc2e126c9490e58a36a0f83516479e781d83adfb134576a5cbe5c6af2a3e93c"}, + {file = "pycryptodome-3.9.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ef39c98d9b8c0736d91937d193653e47c3b19ddf4fc3bccdc5e09aaa4b0c5d21"}, + {file = "pycryptodome-3.9.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:87006cf0d81505408f1ae4f55cf8a5d95a8e029a4793360720ae17c6500f7ecc"}, + {file = "pycryptodome-3.9.8-cp37-cp37m-win32.whl", hash = "sha256:4350a42028240c344ee855f032c7d4ad6ff4f813bfbe7121547b7dc579ecc876"}, + {file = "pycryptodome-3.9.8-cp37-cp37m-win_amd64.whl", hash = "sha256:c8bf40cf6e281a4378e25846924327e728a887e8bf0ee83b2604a0f4b61692e8"}, + {file = "pycryptodome-3.9.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d8074c8448cfd0705dfa71ca333277fce9786d0b9cac75d120545de6253f996a"}, + {file = "pycryptodome-3.9.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8063a712fba642f78d3c506b0896846601b6de7f5c3d534e388ad0cc07f5a149"}, + {file = "pycryptodome-3.9.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:dd302b6ae3965afeb5ef1b0d92486f986c0e65183cd7835973f0b593800590e6"}, + {file = "pycryptodome-3.9.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:663f8de2b3df2e744d6e1610506e0ea4e213bde906795953c1e82279c169f0a7"}, + {file = "pycryptodome-3.9.8-cp38-cp38-win32.whl", hash = "sha256:02e51e1d5828d58f154896ddfd003e2e7584869c275e5acbe290443575370fba"}, + {file = "pycryptodome-3.9.8-cp38-cp38-win_amd64.whl", hash = "sha256:55eb61aca2c883db770999f50d091ff7c14016f2769ad7bca3d9b75d1d7c1b68"}, + {file = "pycryptodome-3.9.8-cp39-cp39-manylinux1_i686.whl", hash = "sha256:39ef9fb52d6ec7728fce1f1693cb99d60ce302aeebd59bcedea70ca3203fda60"}, + {file = "pycryptodome-3.9.8-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:de6e1cd75677423ff64712c337521e62e3a7a4fc84caabbd93207752e831a85a"}, + {file = "pycryptodome-3.9.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd"}, + {file = "pycryptodome-3.9.8.tar.gz", hash = "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4"}, +] +pygments = [ + {file = "Pygments-2.7.1-py3-none-any.whl", hash = "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998"}, + {file = "Pygments-2.7.1.tar.gz", hash = "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"}, +] +pyopenssl = [ + {file = "pyOpenSSL-19.1.0-py2.py3-none-any.whl", hash = "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504"}, + {file = "pyOpenSSL-19.1.0.tar.gz", hash = "sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507"}, +] +pyotp = [ + {file = "pyotp-2.4.0-py2.py3-none-any.whl", hash = "sha256:e96c2d725d0b613b793775bb184e90297fda5f4989e210e28acf08a09ac2cc83"}, + {file = "pyotp-2.4.0.tar.gz", hash = "sha256:01eceab573181188fe038d001e42777884a7f5367203080ef5bda0e30fe82d28"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pyreadline = [ + {file = "pyreadline-2.1.win-amd64.exe", hash = "sha256:9ce5fa65b8992dfa373bddc5b6e0864ead8f291c94fbfec05fbd5c836162e67b"}, + {file = "pyreadline-2.1.win32.exe", hash = "sha256:65540c21bfe14405a3a77e4c085ecfce88724743a4ead47c66b84defcf82c32e"}, + {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, +] +pyspf = [ + {file = "pyspf-2.0.14.tar.gz", hash = "sha256:57a7ef01bda090173aafb6af0106251686ed73f03db4e911fcd34c57fc347186"}, +] +pytest = [ + {file = "pytest-6.1.0-py3-none-any.whl", hash = "sha256:1cd09785c0a50f9af72220dd12aa78cfa49cbffc356c61eab009ca189e018a33"}, + {file = "pytest-6.1.0.tar.gz", hash = "sha256:d010e24666435b39a4cf48740b039885642b6c273a3f77be3e7e03554d2806b7"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +python-dotenv = [ + {file = "python-dotenv-0.14.0.tar.gz", hash = "sha256:8c10c99a1b25d9a68058a1ad6f90381a62ba68230ca93966882a4dbc3bc9c33d"}, + {file = "python_dotenv-0.14.0-py2.py3-none-any.whl", hash = "sha256:c10863aee750ad720f4f43436565e4c1698798d763b63234fb5021b6c616e423"}, +] +python-editor = [ + {file = "python-editor-1.0.4.tar.gz", hash = "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b"}, + {file = "python_editor-1.0.4-py2-none-any.whl", hash = "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"}, + {file = "python_editor-1.0.4-py2.7.egg", hash = "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522"}, + {file = "python_editor-1.0.4-py3-none-any.whl", hash = "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d"}, + {file = "python_editor-1.0.4-py3.5.egg", hash = "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77"}, +] +python-gnupg = [ + {file = "python-gnupg-0.4.6.tar.gz", hash = "sha256:3aa0884b3bd414652c2385b9df39e7b87272c2eca1b8fcc3089bc9e58652019a"}, + {file = "python_gnupg-0.4.6-py2.py3-none-any.whl", hash = "sha256:cba3566e8a8fb7bb417d6897a6e17bfc7f9371052e57eb0057783c07d762a679"}, +] +pytz = [ + {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, + {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, +] +pyyaml = [ + {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, + {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, + {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, + {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, +] +regex = [ + {file = "regex-2020.9.27-cp27-cp27m-win32.whl", hash = "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3"}, + {file = "regex-2020.9.27-cp27-cp27m-win_amd64.whl", hash = "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc"}, + {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b"}, + {file = "regex-2020.9.27-cp36-cp36m-win32.whl", hash = "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63"}, + {file = "regex-2020.9.27-cp36-cp36m-win_amd64.whl", hash = "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c"}, + {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100"}, + {file = "regex-2020.9.27-cp37-cp37m-win32.whl", hash = "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707"}, + {file = "regex-2020.9.27-cp37-cp37m-win_amd64.whl", hash = "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux1_i686.whl", hash = "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b"}, + {file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"}, + {file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"}, + {file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"}, + {file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"}, +] +requests = [ + {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, + {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, +] +requests-oauthlib = [ + {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"}, + {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"}, + {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"}, +] +rsa = [ + {file = "rsa-4.6-py3-none-any.whl", hash = "sha256:6166864e23d6b5195a5cfed6cd9fed0fe774e226d8f854fcb23b7bbef0350233"}, + {file = "rsa-4.6.tar.gz", hash = "sha256:109ea5a66744dd859bf16fe904b8d8b627adafb9408753161e766a92e7d681fa"}, +] +"ruamel.yaml" = [ + {file = "ruamel.yaml-0.16.12-py2.py3-none-any.whl", hash = "sha256:012b9470a0ea06e4e44e99e7920277edf6b46eee0232a04487ea73a7386340a5"}, + {file = "ruamel.yaml-0.16.12.tar.gz", hash = "sha256:076cc0bc34f1966d920a49f18b52b6ad559fbe656a0748e3535cf7b3f29ebf9e"}, +] +"ruamel.yaml.clib" = [ + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-win32.whl", hash = "sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-win_amd64.whl", hash = "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f"}, + {file = "ruamel.yaml.clib-0.2.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win32.whl", hash = "sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f"}, + {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win32.whl", hash = "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2"}, + {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win32.whl", hash = "sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6"}, + {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win32.whl", hash = "sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1"}, + {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b"}, + {file = "ruamel.yaml.clib-0.2.2.tar.gz", hash = "sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7"}, +] +s3transfer = [ + {file = "s3transfer-0.3.3-py2.py3-none-any.whl", hash = "sha256:2482b4259524933a022d59da830f51bd746db62f047d6eb213f2f8855dcb8a13"}, + {file = "s3transfer-0.3.3.tar.gz", hash = "sha256:921a37e2aefc64145e7b73d50c71bb4f26f46e4c9f414dc648c6245ff92cf7db"}, +] +sentry-sdk = [ + {file = "sentry-sdk-0.18.0.tar.gz", hash = "sha256:1d91a0059d2d8bb980bec169578035c2f2d4b93cd8a4fb5b85c81904d33e221a"}, + {file = "sentry_sdk-0.18.0-py2.py3-none-any.whl", hash = "sha256:6222cf623e404c3e62b8e0e81c6db866ac2d12a663b7c1f7963350e3f397522a"}, +] +simplejson = [ + {file = "simplejson-3.17.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8"}, + {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6"}, + {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d"}, + {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043"}, + {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4"}, + {file = "simplejson-3.17.2-cp27-cp27m-win32.whl", hash = "sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9"}, + {file = "simplejson-3.17.2-cp27-cp27m-win_amd64.whl", hash = "sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667"}, + {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06"}, + {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f"}, + {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b"}, + {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f"}, + {file = "simplejson-3.17.2-cp33-cp33m-win32.whl", hash = "sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746"}, + {file = "simplejson-3.17.2-cp33-cp33m-win_amd64.whl", hash = "sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748"}, + {file = "simplejson-3.17.2-cp34-cp34m-win32.whl", hash = "sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3"}, + {file = "simplejson-3.17.2-cp34-cp34m-win_amd64.whl", hash = "sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b"}, + {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d"}, + {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956"}, + {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d"}, + {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971"}, + {file = "simplejson-3.17.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a"}, + {file = "simplejson-3.17.2-cp35-cp35m-win32.whl", hash = "sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396"}, + {file = "simplejson-3.17.2-cp35-cp35m-win_amd64.whl", hash = "sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a"}, + {file = "simplejson-3.17.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb"}, + {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb"}, + {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f"}, + {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45"}, + {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139"}, + {file = "simplejson-3.17.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46"}, + {file = "simplejson-3.17.2-cp36-cp36m-win32.whl", hash = "sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b"}, + {file = "simplejson-3.17.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625"}, + {file = "simplejson-3.17.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25"}, + {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf"}, + {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0"}, + {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f"}, + {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04"}, + {file = "simplejson-3.17.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278"}, + {file = "simplejson-3.17.2-cp37-cp37m-win32.whl", hash = "sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34"}, + {file = "simplejson-3.17.2-cp37-cp37m-win_amd64.whl", hash = "sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0"}, + {file = "simplejson-3.17.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da"}, + {file = "simplejson-3.17.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a"}, + {file = "simplejson-3.17.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995"}, + {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc"}, + {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94"}, + {file = "simplejson-3.17.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8"}, + {file = "simplejson-3.17.2.tar.gz", hash = "sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +sqlalchemy = [ + {file = "SQLAlchemy-1.3.19-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:f2e8a9c0c8813a468aa659a01af6592f71cd30237ec27c4cc0683f089f90dcfc"}, + {file = "SQLAlchemy-1.3.19-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:33d29ae8f1dc7c75b191bb6833f55a19c932514b9b5ce8c3ab9bc3047da5db36"}, + {file = "SQLAlchemy-1.3.19-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3292a28344922415f939ee7f4fc0c186f3d5a0bf02192ceabd4f1129d71b08de"}, + {file = "SQLAlchemy-1.3.19-cp27-cp27m-win32.whl", hash = "sha256:883c9fb62cebd1e7126dd683222b3b919657590c3e2db33bdc50ebbad53e0338"}, + {file = "SQLAlchemy-1.3.19-cp27-cp27m-win_amd64.whl", hash = "sha256:860d0fe234922fd5552b7f807fbb039e3e7ca58c18c8d38aa0d0a95ddf4f6c23"}, + {file = "SQLAlchemy-1.3.19-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:73a40d4fcd35fdedce07b5885905753d5d4edf413fbe53544dd871f27d48bd4f"}, + {file = "SQLAlchemy-1.3.19-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5a49e8473b1ab1228302ed27365ea0fadd4bf44bc0f9e73fe38e10fdd3d6b4fc"}, + {file = "SQLAlchemy-1.3.19-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6547b27698b5b3bbfc5210233bd9523de849b2bb8a0329cd754c9308fc8a05ce"}, + {file = "SQLAlchemy-1.3.19-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:107d4af989831d7b091e382d192955679ec07a9209996bf8090f1f539ffc5804"}, + {file = "SQLAlchemy-1.3.19-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:eb1d71643e4154398b02e88a42fc8b29db8c44ce4134cf0f4474bfc5cb5d4dac"}, + {file = "SQLAlchemy-1.3.19-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:b6ff91356354b7ff3bd208adcf875056d3d886ed7cef90c571aef2ab8a554b12"}, + {file = "SQLAlchemy-1.3.19-cp35-cp35m-win32.whl", hash = "sha256:96f51489ac187f4bab588cf51f9ff2d40b6d170ac9a4270ffaed535c8404256b"}, + {file = "SQLAlchemy-1.3.19-cp35-cp35m-win_amd64.whl", hash = "sha256:618db68745682f64cedc96ca93707805d1f3a031747b5a0d8e150cfd5055ae4d"}, + {file = "SQLAlchemy-1.3.19-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:6557af9e0d23f46b8cd56f8af08eaac72d2e3c632ac8d5cf4e20215a8dca7cea"}, + {file = "SQLAlchemy-1.3.19-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8280f9dae4adb5889ce0bb3ec6a541bf05434db5f9ab7673078c00713d148365"}, + {file = "SQLAlchemy-1.3.19-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:b595e71c51657f9ee3235db8b53d0b57c09eee74dfb5b77edff0e46d2218dc02"}, + {file = "SQLAlchemy-1.3.19-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:51064ee7938526bab92acd049d41a1dc797422256086b39c08bafeffb9d304c6"}, + {file = "SQLAlchemy-1.3.19-cp36-cp36m-win32.whl", hash = "sha256:8afcb6f4064d234a43fea108859942d9795c4060ed0fbd9082b0f280181a15c1"}, + {file = "SQLAlchemy-1.3.19-cp36-cp36m-win_amd64.whl", hash = "sha256:e49947d583fe4d29af528677e4f0aa21f5e535ca2ae69c48270ebebd0d8843c0"}, + {file = "SQLAlchemy-1.3.19-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:9e865835e36dfbb1873b65e722ea627c096c11b05f796831e3a9b542926e979e"}, + {file = "SQLAlchemy-1.3.19-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:276936d41111a501cf4a1a0543e25449108d87e9f8c94714f7660eaea89ae5fe"}, + {file = "SQLAlchemy-1.3.19-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c7adb1f69a80573698c2def5ead584138ca00fff4ad9785a4b0b2bf927ba308d"}, + {file = "SQLAlchemy-1.3.19-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:465c999ef30b1c7525f81330184121521418a67189053bcf585824d833c05b66"}, + {file = "SQLAlchemy-1.3.19-cp37-cp37m-win32.whl", hash = "sha256:aa0554495fe06172b550098909be8db79b5accdf6ffb59611900bea345df5eba"}, + {file = "SQLAlchemy-1.3.19-cp37-cp37m-win_amd64.whl", hash = "sha256:15c0bcd3c14f4086701c33a9e87e2c7ceb3bcb4a246cd88ec54a49cf2a5bd1a6"}, + {file = "SQLAlchemy-1.3.19-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:fe7fe11019fc3e6600819775a7d55abc5446dda07e9795f5954fdbf8a49e1c37"}, + {file = "SQLAlchemy-1.3.19-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c898b3ebcc9eae7b36bd0b4bbbafce2d8076680f6868bcbacee2d39a7a9726a7"}, + {file = "SQLAlchemy-1.3.19-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:072766c3bd09294d716b2d114d46ffc5ccf8ea0b714a4e1c48253014b771c6bb"}, + {file = "SQLAlchemy-1.3.19-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:26c5ca9d09f0e21b8671a32f7d83caad5be1f6ff45eef5ec2f6fd0db85fc5dc0"}, + {file = "SQLAlchemy-1.3.19-cp38-cp38-win32.whl", hash = "sha256:b70bad2f1a5bd3460746c3fb3ab69e4e0eb5f59d977a23f9b66e5bdc74d97b86"}, + {file = "SQLAlchemy-1.3.19-cp38-cp38-win_amd64.whl", hash = "sha256:83469ad15262402b0e0974e612546bc0b05f379b5aa9072ebf66d0f8fef16bea"}, + {file = "SQLAlchemy-1.3.19.tar.gz", hash = "sha256:3bba2e9fbedb0511769780fe1d63007081008c5c2d7d715e91858c94dbaa260e"}, +] +sqlalchemy-utils = [ + {file = "SQLAlchemy-Utils-0.36.8.tar.gz", hash = "sha256:fb66e9956e41340011b70b80f898fde6064ec1817af77199ee21ace71d7d6ab0"}, +] +strictyaml = [ + {file = "strictyaml-1.1.0.tar.gz", hash = "sha256:6b07dbd4f77ab023ed4167c43ffc1b9f9354fb6075cc6ff3b91fefcbb80342ca"}, +] +toml = [ + {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, + {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, +] +traitlets = [ + {file = "traitlets-5.0.4-py3-none-any.whl", hash = "sha256:9664ec0c526e48e7b47b7d14cd6b252efa03e0129011de0a9c1d70315d4309c3"}, + {file = "traitlets-5.0.4.tar.gz", hash = "sha256:86c9351f94f95de9db8a04ad8e892da299a088a64fd283f9f6f18770ae5eae1b"}, +] +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"}, +] +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"}, +] +unidecode = [ + {file = "Unidecode-1.1.1-py2.py3-none-any.whl", hash = "sha256:1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a"}, + {file = "Unidecode-1.1.1.tar.gz", hash = "sha256:2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"}, +] +uritemplate = [ + {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, + {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, +] +urllib3 = [ + {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, + {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, +] +virtualenv = [ + {file = "virtualenv-20.0.31-py2.py3-none-any.whl", hash = "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b"}, + {file = "virtualenv-20.0.31.tar.gz", hash = "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc"}, +] +watchtower = [ + {file = "watchtower-0.8.0-py2.py3-none-any.whl", hash = "sha256:d6704b258494bddc3e9ddda286ef5067e4b09ab4287aea4289afdd035cc4742b"}, + {file = "watchtower-0.8.0.tar.gz", hash = "sha256:9733d1dfc2ab25880386188ee41b19d39893a09c91725bd92b2f782a72890fda"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +webauthn = [ + {file = "webauthn-0.4.7-py2.py3-none-any.whl", hash = "sha256:238391b2e2cc60fb51a2cd2d2d6be149920b9af6184651353d9f95856617a9e7"}, + {file = "webauthn-0.4.7.tar.gz", hash = "sha256:8ad9072ff1d6169f3be30d4dc8733ea563dd266962397bc58b40f674a6af74ac"}, +] +werkzeug = [ + {file = "Werkzeug-1.0.1-py2.py3-none-any.whl", hash = "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43"}, + {file = "Werkzeug-1.0.1.tar.gz", hash = "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"}, +] +wtforms = [ + {file = "WTForms-2.3.3-py2.py3-none-any.whl", hash = "sha256:7b504fc724d0d1d4d5d5c114e778ec88c37ea53144683e084215eed5155ada4c"}, + {file = "WTForms-2.3.3.tar.gz", hash = "sha256:81195de0ac94fbc8368abbaf9197b88c4f3ffd6c2719b5bf5fc9da744f3d829c"}, +] +yacron = [ + {file = "yacron-0.11.1-py3-none-any.whl", hash = "sha256:0fb795d55a712fed68648d8737fe16d4e118f2b4104c94d7fa325eaabc5cd96b"}, + {file = "yacron-0.11.1.tar.gz", hash = "sha256:3d942313576142cb7779a4258efc02880cd43ffc9a54df82dfb2f9aba3a13dd9"}, +] +yarl = [ + {file = "yarl-1.6.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:db9eb8307219d7e09b33bcb43287222ef35cbcf1586ba9472b0a4b833666ada1"}, + {file = "yarl-1.6.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:e31fef4e7b68184545c3d68baec7074532e077bd1906b040ecfba659737df188"}, + {file = "yarl-1.6.0-cp35-cp35m-win32.whl", hash = "sha256:5d84cc36981eb5a8533be79d6c43454c8e6a39ee3118ceaadbd3c029ab2ee580"}, + {file = "yarl-1.6.0-cp35-cp35m-win_amd64.whl", hash = "sha256:5e447e7f3780f44f890360ea973418025e8c0cdcd7d6a1b221d952600fd945dc"}, + {file = "yarl-1.6.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:6f6898429ec3c4cfbef12907047136fd7b9e81a6ee9f105b45505e633427330a"}, + {file = "yarl-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d088ea9319e49273f25b1c96a3763bf19a882cff774d1792ae6fba34bd40550a"}, + {file = "yarl-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b7c199d2cbaf892ba0f91ed36d12ff41ecd0dde46cbf64ff4bfe997a3ebc925e"}, + {file = "yarl-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:67c5ea0970da882eaf9efcf65b66792557c526f8e55f752194eff8ec722c75c2"}, + {file = "yarl-1.6.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:04a54f126a0732af75e5edc9addeaa2113e2ca7c6fce8974a63549a70a25e50e"}, + {file = "yarl-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcbe419805c9b20db9a51d33b942feddbf6e7fb468cb20686fd7089d4164c12a"}, + {file = "yarl-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:c604998ab8115db802cc55cb1b91619b2831a6128a62ca7eea577fc8ea4d3131"}, + {file = "yarl-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c22607421f49c0cb6ff3ed593a49b6a99c6ffdeaaa6c944cdda83c2393c8864d"}, + {file = "yarl-1.6.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:7ce35944e8e61927a8f4eb78f5bc5d1e6da6d40eadd77e3f79d4e9399e263921"}, + {file = "yarl-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c15d71a640fb1f8e98a1423f9c64d7f1f6a3a168f803042eaf3a5b5022fde0c1"}, + {file = "yarl-1.6.0-cp38-cp38-win32.whl", hash = "sha256:3cc860d72ed989f3b1f3abbd6ecf38e412de722fb38b8f1b1a086315cf0d69c5"}, + {file = "yarl-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:e32f0fb443afcfe7f01f95172b66f279938fbc6bdaebe294b0ff6747fb6db020"}, + {file = "yarl-1.6.0.tar.gz", hash = "sha256:61d3ea3c175fe45f1498af868879c6ffeb989d4143ac542163c45538ba5ec21b"}, +] +zipp = [ + {file = "zipp-3.2.0-py3-none-any.whl", hash = "sha256:43f4fa8d8bb313e65d8323a3952ef8756bf40f9a5c3ea7334be23ee4ec8278b6"}, + {file = "zipp-3.2.0.tar.gz", hash = "sha256:b52f22895f4cfce194bc8172f3819ee8de7540aa6d873535a8668b730b8b411f"}, +] +"zope.event" = [ + {file = "zope.event-4.5.0-py2.py3-none-any.whl", hash = "sha256:2666401939cdaa5f4e0c08cf7f20c9b21423b95e88f4675b1443973bdb080c42"}, + {file = "zope.event-4.5.0.tar.gz", hash = "sha256:5e76517f5b9b119acf37ca8819781db6c16ea433f7e2062c4afc2b6fbedb1330"}, +] +"zope.interface" = [ + {file = "zope.interface-5.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:67ecb94206d4575f40e8b7625956f2549d181ad12156b411a24b675ee1456fdd"}, + {file = "zope.interface-5.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:04e0125b2c862b0129c10b0520e4998dbbfc40d60f1d84517d76dbc516ad022d"}, + {file = "zope.interface-5.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f229d52369e9b0a7dd6cf96b531d1c7977a94149417d8bb3d50bdcf7a80387f8"}, + {file = "zope.interface-5.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:95342cf3bca936d2fd876b93dec7f02adfc488a8ff10b36c8ea8078960adface"}, + {file = "zope.interface-5.1.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:58efa73ec3b45a6e89d3e81561bdbd771ac8de8d946b3e652682ce8214ddb513"}, + {file = "zope.interface-5.1.1-cp27-cp27m-win32.whl", hash = "sha256:5c78295ebc782e186c54ca9a374ae47cee49b8207d3ad0b07c2669f6747c3f9c"}, + {file = "zope.interface-5.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:56465ad46ae83291ea3bc1020d70a7570ebce6fb3791c1c75d7f6fbcf12acc6b"}, + {file = "zope.interface-5.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:29ed20dbbfa4e7d48f6a6c923ffe996305617bffbb0c01e7b34dd02f19429bf1"}, + {file = "zope.interface-5.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7346291f434bc2816f4bd22440aee36eeee504d212a121a777e378e924c4c4e8"}, + {file = "zope.interface-5.1.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:a9c5a7e23caf04fe280e0c242b3644d76406c236fae9469be2aa5e5174ce1808"}, + {file = "zope.interface-5.1.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:3e81f8b4a3de63d31a61865c9f4956f0b7a925a2f7a5dc0240e3f356aff175ac"}, + {file = "zope.interface-5.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:46e9cb44736f633647f1e6598c3e049f96c82f7ec1336e4e2326544ea1cf98b0"}, + {file = "zope.interface-5.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1f858ae20e219d4453ce790f618f45d2c2845955f1e017c391e31aaebee065b1"}, + {file = "zope.interface-5.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:410c1a104f44b098521e348aab55708bd00a0e0d38c90444a88bb02d00473185"}, + {file = "zope.interface-5.1.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:3ba8f66a34ce76ee73de99473b1752a79b86b8369db95567422157dba201793f"}, + {file = "zope.interface-5.1.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fdcee72b7020490b21c150c235b95ce7e1e1fe9fe71160936d2eab35d38dfdd2"}, + {file = "zope.interface-5.1.1-cp35-cp35m-win32.whl", hash = "sha256:31063831f233f4cfed1e515c692febea733ee72bece0337dec21b882473e60be"}, + {file = "zope.interface-5.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fb1b094b60cfc762a66bdc6bb07b3c65b441a84999e035e7e78133802776fcd5"}, + {file = "zope.interface-5.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:6fafefc463792b38723a5fe7abf32e5c397d097d813a5bf4f2f657d65309eb9d"}, + {file = "zope.interface-5.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1421739ef86e32f6b6cef91153a998fd5099e4353c9b2cee4c9f983b50096e24"}, + {file = "zope.interface-5.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:7aa5b73788704434e98dde0392503e068819df2b52d55209fae4ada7e86e6a97"}, + {file = "zope.interface-5.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b05ba336dada8d8c4ae8471c026cfa93d734a8c53e345be8b37cc34c5e90a95e"}, + {file = "zope.interface-5.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:0d397c63211e43e63b8407977b1a27c6cf7bd75d8303bf0904a96a83e6bded20"}, + {file = "zope.interface-5.1.1-cp36-cp36m-win32.whl", hash = "sha256:bd152b98ffe7b4bfe01cffafa6a4ca27cd52371de788d99c897c7113384868ec"}, + {file = "zope.interface-5.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8d61add553adaffc1fbab2f763ee88ff00d76fd2e2bbf559e02f7c081a1d2b28"}, + {file = "zope.interface-5.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d44b6eeb414fda374f027cff9e3c6114da801d2a3d9cf054af0febc0aa76c561"}, + {file = "zope.interface-5.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:a04877077eb25d76188929be9e734ce216af9a116c29b0af6775248e9d875d9c"}, + {file = "zope.interface-5.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:09c43731652228d2d4ddd3dfe5457e82254aeeafe606dcc9cbc551756038b8fe"}, + {file = "zope.interface-5.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:5b9dbbcb637f4411b57692acf11321824ab64f152c42e649fc0ebf960be15615"}, + {file = "zope.interface-5.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:eab4ad27c485331f25c1c49c0b483ee500df7bcd0d1e2549b4abfa7cc674ab77"}, + {file = "zope.interface-5.1.1-cp37-cp37m-win32.whl", hash = "sha256:55bc33d3e0b191a16d5d696fe5f2716acc4701a4fdb891281bbd1b4a84a94fde"}, + {file = "zope.interface-5.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:090267317d9e8bf8c1f2cef1d7e9f438e55010a58ba7f514f481c21c3f20f348"}, + {file = "zope.interface-5.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:46294c0b4da40f9d19b84319a7a932bbee68715db925336aea47fc917fcbc28a"}, + {file = "zope.interface-5.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:801591cf7218e26f40aacdcb6655b9bf6e6e01c1d071b7a5b142959b5bb1a681"}, + {file = "zope.interface-5.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d7dd226c385fa65b9d487db0ce77c6270ce271c8684fcfe392787d05c3b4e0ac"}, + {file = "zope.interface-5.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:67f2b706184dde4e194582b8c114ef1f6a990a777f2547f0c79f05f23d0688d0"}, + {file = "zope.interface-5.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:53004603b2e9e4c1e99554bdb603b52564e2a51c1692064d61269f0bcb21ae1c"}, + {file = "zope.interface-5.1.1-cp38-cp38-win32.whl", hash = "sha256:ded12f5819357119ccda08b12815e888077832911ee05cd8a66049c5ce2280cd"}, + {file = "zope.interface-5.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:abf615c103c4427d22af6cc938869fe4e6b355f9bfa20919e142ec3ff860df7a"}, + {file = "zope.interface-5.1.1.tar.gz", hash = "sha256:5e98cd35f2ae34e7830f3c91675bf418e6ee985817568d982c44dabd7345325b"}, +] diff --git a/pyproject.toml b/pyproject.toml index 2c4e6d78..7d567ea7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,3 +16,69 @@ exclude = ''' )/ ) ''' + +[tool.poetry] +name = "SimpleLogin" +version = "0.1.0" +description = "open-source email alias solution" +authors = ["SimpleLogin "] +license = "MIT" +repository = "https://github.com/simple-login/app" +keywords = ["email", "alias", "privacy", "oauth2", "openid"] + +[tool.poetry.dependencies] +python = "^3.7" +flask = "^1.1.2" +flask_sqlalchemy = "^2.4.4" +flask_login = "^0.5.0" +wtforms = "^2.3.3" +unidecode = "^1.1.1" +gunicorn = "^20.0.4" +bcrypt = "^3.2.0" +python-dotenv = "^0.14.0" +ipython = "^7.18.1" +sqlalchemy_utils = "^0.36.8" +psycopg2-binary = "^2.8.6" +sentry_sdk = "^0.18.0" +blinker = "^1.4" +arrow = "^0.16.0" +Flask-WTF = "^0.14.3" +boto3 = "^1.15.9" +Flask-Migrate = "^2.5.3" +flask_admin = "^1.5.6" +flask-cors = "^3.0.9" +watchtower = "^0.8.0" +sqlalchemy-utils = "^0.36.8" +jwcrypto = "^0.8" +yacron = "^0.11.1" +flask-debugtoolbar = "^0.11.0" +requests_oauthlib = "^1.3.0" +pyopenssl = "^19.1.0" +aiosmtpd = "^1.2" +dnspython = "^2.0.0" +coloredlogs = "^14.0" +pycryptodome = "^3.9.8" +phpserialize = "^1.3" +dkimpy = "^1.0.5" +pyotp = "^2.4.0" +flask_profiler = "^1.8.1" +facebook-sdk = "^3.1.0" +google-api-python-client = "^1.12.3" +google-auth-httplib2 = "^0.0.4" +python-gnupg = "^0.4.6" +webauthn = "^0.4.7" +pyspf = "^2.0.14" +Flask-Limiter = "^1.4" +memory_profiler = "^0.57.0" +gevent = "^20.9.0" +aiocontextvars = "^0.2.2" +aiospamc = "^0.6.1" + +[tool.poetry.dev-dependencies] +pytest = "^6.1.0" +black = "^20.8b1" +pre-commit = "^2.7.1" + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/requirements.in b/requirements.in deleted file mode 100644 index 26f83227..00000000 --- a/requirements.in +++ /dev/null @@ -1,49 +0,0 @@ -flask_sqlalchemy -flask -flask_login -wtforms -unidecode -gunicorn -pip-tools -bcrypt -python-dotenv -ipython -sqlalchemy_utils -psycopg2-binary -sentry_sdk -blinker -arrow -Flask-WTF -boto3 -Flask-Migrate -flask_admin -pytest -flask-cors -watchtower -sqlalchemy-utils -jwcrypto -yacron -flask-debugtoolbar -requests_oauthlib -pyopenssl -aiosmtpd -dnspython -coloredlogs -pycryptodome -phpserialize -dkimpy -pyotp -flask_profiler -facebook-sdk -google-api-python-client -google-auth-httplib2 -python-gnupg -webauthn -pyspf -Flask-Limiter -memory_profiler -gevent -aiocontextvars -aiospamc -black -pre-commit \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 2cbb600b..00000000 --- a/requirements.txt +++ /dev/null @@ -1,146 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -# -aiocontextvars==0.2.2 # via -r requirements.in -aiohttp==3.5.4 # via yacron -aiosmtpd==1.2 # via -r requirements.in -aiosmtplib==1.0.6 # via yacron -aiospamc==0.6.1 # via -r requirements.in -alembic==1.0.10 # via flask-migrate -appdirs==1.4.4 # via black, virtualenv -appnope==0.1.0 # via ipython -arrow==0.14.2 # via -r requirements.in -asn1crypto==0.24.0 # via cryptography -async-timeout==3.0.1 # via aiohttp -atomicwrites==1.3.0 # via pytest -atpublic==1.0 # via aiosmtpd -attrs==19.1.0 # via aiohttp, pytest -backcall==0.1.0 # via ipython -bcrypt==3.1.6 # via -r requirements.in -black==20.8b1 # via -r requirements.in -blinker==1.4 # via -r requirements.in, flask-debugtoolbar -boto3==1.9.167 # via -r requirements.in, watchtower -botocore==1.12.167 # via boto3, s3transfer -cachetools==4.0.0 # via google-auth -cbor2==5.1.0 # via webauthn -certifi==2019.11.28 # via aiospamc, requests, sentry-sdk -cffi==1.12.3 # via bcrypt, cryptography -cfgv==3.2.0 # via pre-commit -chardet==3.0.4 # via aiohttp, requests -click==7.1.2 # via black, flask, pip-tools -coloredlogs==10.0 # via -r requirements.in -crontab==0.22.8 # via yacron -cryptography==2.7 # via jwcrypto, pyopenssl, webauthn -decorator==4.4.0 # via ipython, traitlets -distlib==0.3.1 # via virtualenv -dkimpy==1.0.1 # via -r requirements.in -dnspython==1.16.0 # via -r requirements.in, dkimpy -docutils==0.14 # via botocore -facebook-sdk==3.1.0 # via -r requirements.in -filelock==3.0.12 # via virtualenv -flask-admin==1.5.3 # via -r requirements.in -flask-cors==3.0.8 # via -r requirements.in -flask-debugtoolbar==0.10.1 # via -r requirements.in -flask-httpauth==3.3.0 # via flask-profiler -flask-limiter==1.3.1 # via -r requirements.in -flask-login==0.4.1 # via -r requirements.in -flask-migrate==2.5.2 # via -r requirements.in -flask-profiler==1.8.1 # via -r requirements.in -flask-sqlalchemy==2.4.0 # via -r requirements.in, flask-migrate -flask-wtf==0.14.2 # via -r requirements.in -flask==1.0.3 # via -r requirements.in, flask-admin, flask-cors, flask-debugtoolbar, flask-httpauth, flask-limiter, flask-login, flask-migrate, flask-profiler, flask-sqlalchemy, flask-wtf -future==0.18.2 # via webauthn -gevent==20.6.2 # via -r requirements.in -google-api-python-client==1.7.11 # via -r requirements.in -google-auth-httplib2==0.0.3 # via -r requirements.in, google-api-python-client -google-auth==1.11.2 # via google-api-python-client, google-auth-httplib2 -greenlet==0.4.16 # via gevent -gunicorn==19.9.0 # via -r requirements.in -httplib2==0.17.0 # via google-api-python-client, google-auth-httplib2 -humanfriendly==4.18 # via coloredlogs -identify==1.5.0 # via pre-commit -idna==2.8 # via requests, yarl -importlib-metadata==0.18 # via pluggy, pytest -ipython-genutils==0.2.0 # via traitlets -ipython==7.5.0 # via -r requirements.in -itsdangerous==1.1.0 # via flask, flask-debugtoolbar -jedi==0.13.3 # via ipython -jinja2==2.10.1 # via flask, yacron -jmespath==0.9.4 # via boto3, botocore -jwcrypto==0.6.0 # via -r requirements.in -limits==1.5.1 # via flask-limiter -mako==1.0.12 # via alembic -markupsafe==1.1.1 # via jinja2, mako -memory-profiler==0.57.0 # via -r requirements.in -more-itertools==7.0.0 # via pytest -multidict==4.5.2 # via aiohttp, yarl -mypy-extensions==0.4.3 # via black -nodeenv==1.5.0 # via pre-commit -oauthlib==3.0.2 # via requests-oauthlib -packaging==19.0 # via pytest -parso==0.4.0 # via jedi -pathspec==0.8.0 # via black -pexpect==4.7.0 # via ipython -phpserialize==1.3 # via -r requirements.in -pickleshare==0.7.5 # via ipython -pip-tools==5.3.1 # via -r requirements.in -pluggy==0.12.0 # via pytest -pre-commit==2.7.1 # via -r requirements.in -prompt-toolkit==2.0.9 # via ipython -psutil==5.7.0 # via memory-profiler -psycopg2-binary==2.8.2 # via -r requirements.in -ptyprocess==0.6.0 # via pexpect -py==1.8.0 # via pytest -pyasn1-modules==0.2.8 # via google-auth -pyasn1==0.4.8 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -pycryptodome==3.9.4 # via -r requirements.in -pygments==2.4.2 # via ipython -pyopenssl==19.0.0 # via -r requirements.in, webauthn -pyotp==2.3.0 # via -r requirements.in -pyparsing==2.4.0 # via packaging -pyspf==2.0.14 # via -r requirements.in -pytest==4.6.3 # via -r requirements.in -python-dateutil==2.8.0 # via alembic, arrow, botocore, strictyaml -python-dotenv==0.10.3 # via -r requirements.in -python-editor==1.0.4 # via alembic -python-gnupg==0.4.5 # via -r requirements.in -pytz==2020.1 # via yacron -pyyaml==5.3.1 # via pre-commit -regex==2020.7.14 # via black -requests-oauthlib==1.2.0 # via -r requirements.in -requests==2.22.0 # via facebook-sdk, requests-oauthlib -rsa==4.0 # via google-auth -ruamel.yaml==0.16.12 # via strictyaml -s3transfer==0.2.1 # via boto3 -sentry-sdk==0.18.0 # via -r requirements.in, yacron -simplejson==3.17.0 # via flask-profiler -six==1.12.0 # via bcrypt, cryptography, flask-cors, flask-limiter, google-api-python-client, google-auth, limits, packaging, pip-tools, prompt-toolkit, pyopenssl, pytest, python-dateutil, sqlalchemy-utils, traitlets, virtualenv, webauthn -sqlalchemy-utils==0.36.1 # via -r requirements.in -sqlalchemy==1.3.19 # via alembic, flask-sqlalchemy, sqlalchemy-utils -strictyaml==1.0.2 # via yacron -toml==0.10.1 # via black, pre-commit -traitlets==4.3.2 # via ipython -typed-ast==1.4.1 # via black -typing-extensions==3.7.4.3 # via black -unidecode==1.0.23 # via -r requirements.in -uritemplate==3.0.1 # via google-api-python-client -urllib3==1.25.3 # via botocore, requests, sentry-sdk -virtualenv==20.0.31 # via pre-commit -watchtower==0.6.0 # via -r requirements.in -wcwidth==0.1.7 # via prompt-toolkit, pytest -webauthn==0.4.7 # via -r requirements.in -werkzeug==0.15.4 # via flask, flask-debugtoolbar -wtforms==2.2.1 # via -r requirements.in, flask-admin, flask-wtf -yacron==0.11.1 # via -r requirements.in -yarl==1.3.0 # via aiohttp -zipp==0.5.1 # via importlib-metadata -zope.event==4.4 # via gevent -zope.interface==5.1.0 # via gevent - -# The following packages are considered to be unsafe in a requirements file: -# pip -# setuptools From 73f56818fb693583b2358639779c9080901b15da Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 1 Oct 2020 12:21:16 +0200 Subject: [PATCH 20/33] fix ProxyFix --- server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.py b/server.py index 96d3f518..4fa94329 100644 --- a/server.py +++ b/server.py @@ -108,7 +108,7 @@ def create_light_app() -> Flask: def create_app() -> Flask: app = Flask(__name__) # SimpleLogin is deployed behind NGINX - app.wsgi_app = ProxyFix(app.wsgi_app, num_proxies=1) + app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_host=1) limiter.init_app(app) app.url_map.strict_slashes = False From 52b5526261112be6612b2922b224518379911738 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 1 Oct 2020 12:21:37 +0200 Subject: [PATCH 21/33] add email_validator as dependency --- poetry.lock | 20 ++++++++++++++++++-- pyproject.toml | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index fe86f16e..7a1c3603 100644 --- a/poetry.lock +++ b/poetry.lock @@ -387,6 +387,18 @@ doh = ["requests", "requests-toolbelt"] idna = ["idna (>=2.1)"] trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"] +[[package]] +category = "main" +description = "A robust email syntax and deliverability validation library for Python 2.x/3.x." +name = "email-validator" +optional = false +python-versions = "*" +version = "1.1.1" + +[package.dependencies] +dnspython = ">=1.15.0" +idna = ">=2.0.0" + [[package]] category = "main" description = "This client library is designed to support the Facebook Graph API and the official Facebook JavaScript SDK, which is the canonical way to implement Facebook authentication." @@ -1684,9 +1696,9 @@ test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] -content-hash = "5303557051852045309a428fbcb5ca0e706aecfacfd5b398dbd7c38b72537efc" +content-hash = "3bbaf7732527d268d88fa45d58bb9ba4026075e1cced5986033259608c3b7114" lock-version = "1.0" -python-versions = "3.7" +python-versions = "^3.7" [metadata.files] aiocontextvars = [ @@ -1887,6 +1899,10 @@ dnspython = [ {file = "dnspython-2.0.0-py3-none-any.whl", hash = "sha256:40bb3c24b9d4ec12500f0124288a65df232a3aa749bb0c39734b782873a2544d"}, {file = "dnspython-2.0.0.zip", hash = "sha256:044af09374469c3a39eeea1a146e8cac27daec951f1f1f157b1962fc7cb9d1b7"}, ] +email-validator = [ + {file = "email_validator-1.1.1-py2.py3-none-any.whl", hash = "sha256:5f246ae8d81ce3000eade06595b7bb55a4cf350d559e890182a1466a21f25067"}, + {file = "email_validator-1.1.1.tar.gz", hash = "sha256:63094045c3e802c3d3d575b18b004a531c36243ca8d1cec785ff6bfcb04185bb"}, +] facebook-sdk = [ {file = "facebook-sdk-3.1.0.tar.gz", hash = "sha256:cabcd2e69ea3d9f042919c99b353df7aa1e2be86d040121f6e9f5e63c1cf0f8d"}, {file = "facebook_sdk-3.1.0-py2.py3-none-any.whl", hash = "sha256:2e987b3e0f466a6f4ee77b935eb023dba1384134f004a2af21f1cfff7fe0806e"}, diff --git a/pyproject.toml b/pyproject.toml index 7d567ea7..a2d856db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,7 @@ memory_profiler = "^0.57.0" gevent = "^20.9.0" aiocontextvars = "^0.2.2" aiospamc = "^0.6.1" +email_validator = "^1.1.1" [tool.poetry.dev-dependencies] pytest = "^6.1.0" From b690e903fa745d4282cddd0c338b2e2f2f35687d Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 1 Oct 2020 12:24:37 +0200 Subject: [PATCH 22/33] small refactoring --- server.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server.py b/server.py index 4fa94329..821c7f5d 100644 --- a/server.py +++ b/server.py @@ -42,7 +42,6 @@ from app.config import ( SENTRY_FRONT_END_DSN, FIRST_ALIAS_DOMAIN, SESSION_COOKIE_NAME, - ADMIN_EMAIL, PLAUSIBLE_HOST, PLAUSIBLE_DOMAIN, GITHUB_CLIENT_ID, @@ -157,7 +156,7 @@ def create_app() -> Flask: flask_profiler.init_app(app) # enable CORS on /api endpoints - cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) + CORS(app, resources={r"/api/*": {"origins": "*"}}) # set session to permanent so user stays signed in after quitting the browser # the cookie is valid for 7 days @@ -312,7 +311,7 @@ def fake_data(): @login_manager.user_loader def load_user(user_id): - user = User.query.get(user_id) + user = User.get(user_id) return user @@ -659,14 +658,14 @@ window.location.href = "/"; """ -if __name__ == "__main__": +def local_main(): app = create_app() # enable flask toolbar app.config["DEBUG_TB_PROFILER_ENABLED"] = True app.config["DEBUG_TB_INTERCEPT_REDIRECTS"] = False app.debug = True - toolbar = DebugToolbarExtension(app) + DebugToolbarExtension(app) # warning: only used in local if RESET_DB: @@ -682,3 +681,7 @@ if __name__ == "__main__": app.run(debug=True, port=7777, ssl_context=context) else: app.run(debug=True, port=7777) + + +if __name__ == "__main__": + local_main() From b99085419eb57446506033f2e664bcd95bbd50fe Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 1 Oct 2020 12:48:08 +0200 Subject: [PATCH 23/33] remove AioHttpIntegration sentry, remove aiocontextvars dependency --- poetry.lock | 14 +------------- pyproject.toml | 1 - server.py | 2 -- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/poetry.lock b/poetry.lock index 7a1c3603..ca4b6f6c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,11 +1,3 @@ -[[package]] -category = "main" -description = "Asyncio support for PEP-567 contextvars backport." -name = "aiocontextvars" -optional = false -python-versions = ">=3.5" -version = "0.2.2" - [[package]] category = "main" description = "Async http client/server framework (asyncio)" @@ -1696,15 +1688,11 @@ test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] -content-hash = "3bbaf7732527d268d88fa45d58bb9ba4026075e1cced5986033259608c3b7114" +content-hash = "0e3ade53af79b805e718ebf5e7a047ff0d067ec4ed32154d77255579b1940e83" lock-version = "1.0" python-versions = "^3.7" [metadata.files] -aiocontextvars = [ - {file = "aiocontextvars-0.2.2-py2.py3-none-any.whl", hash = "sha256:885daf8261818767d8f7cbd79f9d4482d118f024b6586ef6e67980236a27bfa3"}, - {file = "aiocontextvars-0.2.2.tar.gz", hash = "sha256:f027372dc48641f683c559f247bd84962becaacdc9ba711d583c3871fb5652aa"}, -] aiohttp = [ {file = "aiohttp-3.6.2-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e"}, {file = "aiohttp-3.6.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec"}, diff --git a/pyproject.toml b/pyproject.toml index a2d856db..0cfc8c92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,6 @@ pyspf = "^2.0.14" Flask-Limiter = "^1.4" memory_profiler = "^0.57.0" gevent = "^20.9.0" -aiocontextvars = "^0.2.2" aiospamc = "^0.6.1" email_validator = "^1.1.1" diff --git a/server.py b/server.py index 821c7f5d..2069b9dd 100644 --- a/server.py +++ b/server.py @@ -20,7 +20,6 @@ from flask_admin import Admin from flask_cors import cross_origin, CORS from flask_debugtoolbar import DebugToolbarExtension from flask_login import current_user -from sentry_sdk.integrations.aiohttp import AioHttpIntegration from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from werkzeug.middleware.proxy_fix import ProxyFix @@ -86,7 +85,6 @@ if SENTRY_DSN: integrations=[ FlaskIntegration(), SqlalchemyIntegration(), - AioHttpIntegration(), ], ) From d8ed1cbbc3bd29c3878730e5c0676d7dc9d78b37 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Thu, 1 Oct 2020 20:51:56 +0200 Subject: [PATCH 24/33] Dedupe email address --- app/models.py | 23 ++++++++++------------- tests/test_models.py | 11 +++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/models.py b/app/models.py index 56ed883f..779663ea 100644 --- a/app/models.py +++ b/app/models.py @@ -1181,19 +1181,16 @@ class Contact(db.Model, ModelMixin): or user.sender_format == SenderFormatEnum.VIA.value ): new_name = f"{self.website_email} via SimpleLogin" - elif user.sender_format == SenderFormatEnum.AT.value: - name = self.name or "" - new_name = ( - name + (" - " if name else "") + self.website_email.replace("@", " at ") - ).strip() - elif user.sender_format == SenderFormatEnum.A.value: - name = self.name or "" - new_name = ( - name + (" - " if name else "") + self.website_email.replace("@", "(a)") - ).strip() - elif user.sender_format == SenderFormatEnum.FULL.value: - name = self.name or "" - new_name = (name + (" - " if name else "") + self.website_email).strip() + else: + if user.sender_format == SenderFormatEnum.AT.value: + formatted_email = self.website_email.replace("@", " at ").strip() + elif user.sender_format == SenderFormatEnum.A.value: + formatted_email = self.website_email.replace("@", "(a)").strip() + elif user.sender_format == SenderFormatEnum.FULL.value: + formatted_email = self.website_email.strip() + + # Prefix name to formatted email if available + new_name = (self.name + " - " + formatted_email) if self.name and self.name != self.website_email.strip() else formatted_email new_addr = formataddr((new_name, self.reply_email)).strip() return new_addr.strip() diff --git a/tests/test_models.py b/tests/test_models.py index f3e5905f..25932687 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -133,8 +133,19 @@ def test_new_addr(flask_client): ) assert c1.new_addr() == '"abcd@example.com via SimpleLogin" ' + # set sender format = FULL + user.sender_format = SenderFormatEnum.FULL.value + db.session.commit() + assert c1.new_addr() == '"First Last - abcd@example.com" ' + + # Make sure email isn't duplicated if sender name equals email + c1.name = "abcd@example.com" + db.session.commit() + assert c1.new_addr() == '"abcd@example.com" ' + # set sender_format = AT user.sender_format = SenderFormatEnum.AT.value + c1.name = "First Last" db.session.commit() assert c1.new_addr() == '"First Last - abcd at example.com" ' From 7be674c13bc830f6ab3c21eb83f5cdb2cda200df Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Thu, 1 Oct 2020 21:44:44 +0200 Subject: [PATCH 25/33] Reformat with Black --- app/models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models.py b/app/models.py index 779663ea..7e60bb19 100644 --- a/app/models.py +++ b/app/models.py @@ -1190,7 +1190,11 @@ class Contact(db.Model, ModelMixin): formatted_email = self.website_email.strip() # Prefix name to formatted email if available - new_name = (self.name + " - " + formatted_email) if self.name and self.name != self.website_email.strip() else formatted_email + new_name = ( + (self.name + " - " + formatted_email) + if self.name and self.name != self.website_email.strip() + else formatted_email + ) new_addr = formataddr((new_name, self.reply_email)).strip() return new_addr.strip() From b90705f12ec853a78d103cb8f6afb786eb38f13f Mon Sep 17 00:00:00 2001 From: pojhm91c7iwk <68391729+pojhm91c7iwk@users.noreply.github.com> Date: Fri, 2 Oct 2020 11:39:30 -0700 Subject: [PATCH 26/33] Update README.md Added instructions to update 'myuser' and 'mypassword' where appropriate. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4249f128..61694f9b 100644 --- a/README.md +++ b/README.md @@ -394,7 +394,7 @@ smtpd_recipient_restrictions = ``` Create the `/etc/postfix/pgsql-relay-domains.cf` file with the following content. -Make sure that the database config is correctly set and replace `mydomain.com` with your domain. +Make sure that the database config is correctly set, replace `mydomain.com` with your domain, update 'myuser' and 'mypassword' with your postgress credentials. ``` # postgres config @@ -408,7 +408,7 @@ query = SELECT domain FROM custom_domain WHERE domain='%s' AND verified=true ``` Create the `/etc/postfix/pgsql-transport-maps.cf` file with the following content. -Again, make sure that the database config is correctly set and replace `mydomain.com` with your domain. +Again, make sure that the database config is correctly set, replace `mydomain.com` with your domain, update 'myuser' and 'mypassword' with your postgress credentials. ``` # postgres config @@ -432,7 +432,7 @@ sudo systemctl restart postfix To run the server, you need a config file. Please have a look at [config example](example.env) for an example to create one. Some parameters are optional and are commented out by default. Some have "dummy" values, fill them up if you want to enable these features (Paddle, AWS, etc). -Let's put your config file at `~/simplelogin.env`. Below is an example that you can use right away, make sure to replace `mydomain.com` by your domain and set `FLASK_SECRET` to a secret string. +Let's put your config file at `~/simplelogin.env`. Below is an example that you can use right away, make sure to replace `mydomain.com` by your domain, set `FLASK_SECRET` to a secret string, update 'myuser' and 'mypassword' with your postgress credentials. Make sure to update the following variables and replace these values by yours. @@ -537,7 +537,7 @@ sudo docker run -d \ ### Nginx -Install Nginx +Install Nginx and make sure to replace `mydomain.com` by your domain ```bash sudo apt-get install -y nginx From ff0eaa4bbfc50b09d284dd0407ddd0acf7b865a3 Mon Sep 17 00:00:00 2001 From: Sylvia van Os Date: Fri, 2 Oct 2020 20:27:15 +0200 Subject: [PATCH 27/33] Show icon if address was autogenerated --- app/dashboard/templates/dashboard/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/app/dashboard/templates/dashboard/index.html b/app/dashboard/templates/dashboard/index.html index ac4bc87a..77df38f8 100644 --- a/app/dashboard/templates/dashboard/index.html +++ b/app/dashboard/templates/dashboard/index.html @@ -193,6 +193,7 @@ > {{ alias.email }} + {% if alias.automatic_creation %} {% endif %}