├── urls └── .gitignore ├── downloads ├── photos │ └── .gitignore ├── videos │ └── .gitignore └── .gitignore ├── renovate.json ├── mypy.ini ├── .github ├── FUNDING.yml └── snapchat_data_export@2x.png ├── main.py ├── snapchat_memory_fetcher ├── writer.py ├── __init__.py ├── parse.py ├── decoder.py └── fetcher.py ├── pyproject.toml ├── requirements.txt ├── LICENSE ├── config.py ├── log.py ├── Makefile ├── .gitignore ├── README.md └── poetry.lock /urls/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /downloads/photos/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /downloads/videos/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /downloads/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !photos* 3 | !videos* 4 | !.gitignore -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.9 3 | warn_return_any = True 4 | warn_unused_configs = True 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://www.buymeacoffee.com/hackersslackers'] 4 | -------------------------------------------------------------------------------- /.github/snapchat_data_export@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toddbirchard/snapchat-memory-fetcher/HEAD/.github/snapchat_data_export@2x.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """Script entry point.""" 2 | from snapchat_memory_fetcher import init_script 3 | 4 | if __name__ == "__main__": 5 | init_script() 6 | -------------------------------------------------------------------------------- /snapchat_memory_fetcher/writer.py: -------------------------------------------------------------------------------- 1 | """Write decoded media URLs to local JSON.""" 2 | from typing import List 3 | 4 | import simplejson as json 5 | 6 | from config import BASE_DIR 7 | 8 | 9 | def save_decoded_media_urls(decoded_urls: List[dict], media_type: str): 10 | """ 11 | Write decoded URLs to a local JSON file for future use. 12 | 13 | :param decoded_urls: List of decoded URLs. 14 | :type decoded_urls: List[dict] 15 | :param media_type: Type of media that URLs were generated for. 16 | :type media_type: str 17 | """ 18 | filepath = f"{BASE_DIR}/urls/{media_type}.json" 19 | with open(filepath, "w") as f: 20 | json.dump( 21 | decoded_urls, 22 | f, 23 | indent=4, 24 | sort_keys=True, 25 | ) 26 | f.close() 27 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "Snapchat Memory Fetcher" 3 | version = "0.1.0" 4 | description = "Extract Snapchat videos & stories from your user account." 5 | authors = ["Todd Birchard "] 6 | maintainers = ["Todd Birchard "] 7 | license = "MIT" 8 | readme = "README.md" 9 | keywords = [ 10 | "Snapchat", 11 | "Data Export", 12 | "Automation", 13 | "Async", 14 | "Concurrency" 15 | ] 16 | 17 | [tool.poetry.dependencies] 18 | python = "^3.9" 19 | aiohttp = "^3.7.4" 20 | asyncio = "^3.4.3" 21 | simplejson = "^3.17.2" 22 | loguru = "^0.5.3" 23 | aiofiles = "^0.6.0" 24 | 25 | [tool.poetry.dev-dependencies] 26 | pytest = "*" 27 | mock = "*" 28 | mypy = "*" 29 | black = "^20.8b1" 30 | isort = "*" 31 | flake8 = "^4.0.0" 32 | 33 | [tool.poetry.scripts] 34 | run = "main:start" 35 | 36 | [build-system] 37 | requires = ["poetry-core>=1.0.0"] 38 | build-backend = "poetry.core.masonry.api" 39 | 40 | [tool.poetry.urls] 41 | issues = "https://github.com/toddbirchard/snapchat-memory-fetcher/issues" 42 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiofiles==0.6.0 2 | aiohttp==3.7.4.post0; python_version >= "3.6" 3 | async-timeout==3.0.1; python_full_version >= "3.5.3" and python_version >= "3.6" 4 | asyncio==3.4.3 5 | attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" 6 | chardet==4.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" 7 | colorama==0.4.4; python_version >= "3.5" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.5" and python_full_version >= "3.5.0" 8 | idna==3.1; python_version >= "3.6" 9 | loguru==0.5.3; python_version >= "3.5" 10 | multidict==5.1.0; python_version >= "3.6" 11 | simplejson==3.17.2; (python_version >= "2.5" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") 12 | typing-extensions==3.10.0.0; python_version >= "3.6" 13 | win32-setctime==1.0.3; sys_platform == "win32" and python_version >= "3.5" 14 | yarl==1.6.3; python_version >= "3.6" 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Todd Birchard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /snapchat_memory_fetcher/__init__.py: -------------------------------------------------------------------------------- 1 | """Bulk download `Snapchat Memories` from a Snapchat data export.""" 2 | import simplejson as json 3 | 4 | from config import BASE_DIR, SNAPCHAT_MEDIA_URLS 5 | from log import LOGGER 6 | 7 | from .decoder import decode_urls 8 | from .fetcher import download_snapchat_memories 9 | from .parse import parse_urls_from_export 10 | from .writer import save_decoded_media_urls 11 | 12 | 13 | def init_script(): 14 | """Batch download all Snapchat memories.""" 15 | for media_type in SNAPCHAT_MEDIA_URLS.keys(): 16 | if SNAPCHAT_MEDIA_URLS[media_type] is None: 17 | parse_and_decode_urls(media_type) 18 | download_snapchat_memories(SNAPCHAT_MEDIA_URLS[media_type], media_type) 19 | LOGGER.success(f"Completed downloading all Snapchat memories.") 20 | 21 | 22 | def parse_and_decode_urls(media_type: str): 23 | """ 24 | Decode Snapchat memory URLs and save to local JSON file. 25 | 26 | :param media_type: Resource URLs to fetch. 27 | :type media_type: str 28 | :returns: dict 29 | """ 30 | encoded_urls = parse_urls_from_export(media_type) 31 | media_urls = decode_urls(encoded_urls, media_type) 32 | save_decoded_media_urls(media_urls, media_type) 33 | return json.loads(open(f"{BASE_DIR}/urls/{media_type}.json").read()) 34 | -------------------------------------------------------------------------------- /snapchat_memory_fetcher/parse.py: -------------------------------------------------------------------------------- 1 | """Parse `Snapchat Memories` URLs from exported Snapchat data dump.""" 2 | from typing import Dict, List 3 | 4 | from config import SNAPCHAT_MEMORIES_JSON 5 | from log import LOGGER 6 | 7 | 8 | def parse_urls_from_export(media_type: str) -> List[Dict[str, str]]: 9 | """ 10 | Parse `Snapchat memories` URLs into convenient dictionary for a given data type. 11 | 12 | :param media_type: Resource URLs to fetch (either photos or videos). 13 | :type media_type: str 14 | :return: List[Dict[str, str]] 15 | """ 16 | media_type_key = media_type.upper().replace("S", "") 17 | encoded_urls = [ 18 | create_url_pair(m) 19 | for m in SNAPCHAT_MEMORIES_JSON 20 | if m["Media Type"] == media_type_key 21 | ] 22 | LOGGER.success( 23 | f"Found {len(encoded_urls)} {media_type} from Snapchat export export." 24 | ) 25 | return encoded_urls 26 | 27 | 28 | def create_url_pair(memory: Dict[str, str]): 29 | """ 30 | Create dict of a memory's encoded URL and its creation date. 31 | 32 | :param memory: Dictionary of encoded media URLs. 33 | :type memory: Dict[str, str] 34 | :return: Dict[str, str] 35 | """ 36 | memory_date = memory["Date"].replace(" UTC", "").replace(" ", "T").replace(":", ".") 37 | return {"date": memory_date, "url": memory["Download Link"]} 38 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | """Fetcher configuration.""" 2 | from os import path 3 | 4 | import simplejson as json 5 | 6 | from log import LOGGER 7 | 8 | # Set project base path 9 | BASE_DIR = path.abspath(path.dirname(__file__)) 10 | 11 | # Location of Snapchat data dump from https://accounts.snapchat.com/accounts/welcome 12 | SNAPCHAT_DATA_EXPORT = f"{BASE_DIR}/export" 13 | SNAPCHAT_MEMORIES_EXPORT = f"{SNAPCHAT_DATA_EXPORT}/json/memories_history.json" 14 | if path.exists(SNAPCHAT_MEMORIES_EXPORT): 15 | SNAPCHAT_MEMORIES_JSON = json.loads(open(SNAPCHAT_MEMORIES_EXPORT).read())[ 16 | "Saved Media" 17 | ] 18 | else: 19 | LOGGER.error(f"Snapchat data not found in `/export` folder.") 20 | raise Exception(f"Snapchat data not found in `/export` folder.") 21 | 22 | # Check if media URLs have been decoded and saved from a previous run 23 | SNAPCHAT_MEDIA_URLS = { 24 | "videos": None, 25 | "photos": None, 26 | } 27 | 28 | for k, v in SNAPCHAT_MEDIA_URLS.items(): 29 | if path.exists(f"{BASE_DIR}/urls/{k}.json"): 30 | decoded_urls = json.loads(open(f"{BASE_DIR}/urls/{k}.json").read()) 31 | if decoded_urls is not None and len(decoded_urls) > 0: 32 | SNAPCHAT_MEDIA_URLS[k] = decoded_urls 33 | 34 | 35 | # Destination for fetched media files 36 | MEDIA_EXPORT_FILEPATH = f"{BASE_DIR}/downloads" 37 | if path.exists(MEDIA_EXPORT_FILEPATH) is False: 38 | raise Exception(f"Define a valid filepath for exported Snapchat memories.") 39 | -------------------------------------------------------------------------------- /log.py: -------------------------------------------------------------------------------- 1 | """Custom logger configuration.""" 2 | from sys import stdout 3 | 4 | from loguru import logger as custom_logger 5 | 6 | 7 | def formatter(log): 8 | """Format log colors based on level.""" 9 | if log["level"].name == "SUCCESS": 10 | return ( 11 | "{time:MM-DD-YYYY HH:mm:ss} | " 12 | "{level}: " 13 | "{message} \n" 14 | ) 15 | if log["level"].name == "WARNING": 16 | return ( 17 | "{time:MM-DD-YYYY HH:mm:ss} | " 18 | "{level}: " 19 | "{message} \n" 20 | ) 21 | elif log["level"].name == "ERROR": 22 | return ( 23 | "{time:MM-DD-YYYY HH:mm:ss} | " 24 | "{level}: " 25 | "{message} \n" 26 | ) 27 | else: 28 | return ( 29 | "{time:MM-DD-YYYY HH:mm:ss} | " 30 | "{level}: " 31 | "{message} \n" 32 | ) 33 | 34 | 35 | def create_logger(): 36 | """Create custom logger.""" 37 | custom_logger.remove() 38 | custom_logger.add(stdout, colorize=True, format=formatter) 39 | return custom_logger 40 | 41 | 42 | LOGGER = create_logger() 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VIRTUAL_ENVIRONMENT := $(CURDIR)/.venv 2 | LOCAL_PYTHON := $(VIRTUAL_ENVIRONMENT)/bin/python3 3 | 4 | define HELP 5 | Manage Snapchat Memory Fetcher. Usage: 6 | 7 | make run - Run $(PROJECTNAME). 8 | make install - Pull latest build and deploy to production. 9 | make update - Update pip dependencies via Python Poetry. 10 | make format - Format code with Python's `Black` library. 11 | make lint - Check code formatting with flake8 12 | make clean - Remove cached files and lock files. 13 | endef 14 | export HELP 15 | 16 | 17 | .PHONY: run restart deploy update format lint clean help 18 | 19 | requirements: .requirements.txt 20 | env: ./.venv/bin/activate 21 | 22 | 23 | .requirements.txt: requirements.txt 24 | $(shell . .venv/bin/activate && pip install -r requirements.txt) 25 | 26 | 27 | all help: 28 | @echo "$$HELP" 29 | 30 | 31 | .PHONY: run 32 | run: env 33 | $(LOCAL_PYTHON) main.py 34 | 35 | 36 | .PHONY: install 37 | install: 38 | make clean 39 | python3 -m venv $(VIRTUAL_ENVIRONMENT) 40 | . $(VIRTUAL_ENVIRONMENT)/bin/activate 41 | $(LOCAL_PYTHON) -m pip install --upgrade pip setuptools wheel 42 | make requirements 43 | 44 | 45 | .PHONY: update 46 | update: env 47 | $(LOCAL_PYTHON) -m pip install --upgrade pip setuptools wheel 48 | poetry update 49 | poetry export -f requirements.txt --output requirements.txt --without-hashes 50 | 51 | 52 | .PHONY: format 53 | format: env 54 | isort --multi-line=3 . 55 | black . 56 | 57 | 58 | .PHONY: lint 59 | lint: 60 | flake8 . --count \ 61 | --select=E9,F63,F7,F82 \ 62 | --exclude .git,.github,__pycache__,.pytest_cache,.venv,logs,creds,.venv,docs,logs \ 63 | --show-source \ 64 | --statistics 65 | 66 | 67 | .PHONY: clean 68 | clean: 69 | find . -name '*.pyc' -delete 70 | find . -name '__pycache__' -delete 71 | find . -name 'poetry.lock' -delete 72 | find . -name '*.log' -delete 73 | find . -wholename 'logs/*.json' -delete 74 | find . -wholename '.pytest_cache' -delete 75 | find . -wholename '**/.pytest_cache' -delete 76 | find . -wholename '*/logs/*.json' -delete 77 | find . -wholename '.webassets-cache/*' -delete 78 | find . -wholename '.urls/*json' -delete 79 | find . -wholename '**/logs' -delete -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | eggs/ 15 | .eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | pip-wheel-metadata/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | db.sqlite3-journal 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # IPython 80 | profile_default/ 81 | ipython_config.py 82 | 83 | # pyenv 84 | .python-version 85 | 86 | # pipenv 87 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 88 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 89 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 90 | # install all needed dependencies. 91 | #Pipfile.lock 92 | 93 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 94 | __pypackages__/ 95 | 96 | # Celery stuff 97 | celerybeat-schedule 98 | celerybeat.pid 99 | 100 | # SageMath parsed files 101 | *.sage.py 102 | 103 | # Environments 104 | .env 105 | .venv 106 | env/ 107 | venv/ 108 | ENV/ 109 | env.bak/ 110 | venv.bak/ 111 | 112 | # Spyder project settings 113 | .spyderproject 114 | .spyproject 115 | 116 | # Rope project settings 117 | .ropeproject 118 | 119 | # mkdocs documentation 120 | /site 121 | 122 | # mypy 123 | .mypy_cache/ 124 | .dmypy.json 125 | dmypy.json 126 | 127 | # Pyre type checker 128 | .pyre/ 129 | 130 | # Personal export 131 | data/ 132 | memories/ 133 | export 134 | 135 | # MacOS 136 | .DS_Store 137 | 138 | # IntelliJ 139 | .idea/ -------------------------------------------------------------------------------- /snapchat_memory_fetcher/decoder.py: -------------------------------------------------------------------------------- 1 | """Decode hidden URLs which host Snapchat memories & save URLs to local JSON.""" 2 | import asyncio 3 | from typing import Dict, List 4 | 5 | from aiohttp import ClientError, ClientSession, InvalidURL 6 | 7 | from log import LOGGER 8 | 9 | 10 | def decode_urls( 11 | encoded_urls: List[Dict[str, str]], media_type: str 12 | ) -> List[Dict[str, str]]: 13 | """ 14 | Decode video and photo URLs. 15 | 16 | :param encoded_urls: Resource URLs to fetch. 17 | :type encoded_urls: List[Dict[str, str]] 18 | :param media_type: Resource URLs to fetch. 19 | :type media_type: str 20 | :returns: List[Dict[str, str]] 21 | """ 22 | loop = asyncio.get_event_loop() 23 | urls = loop.run_until_complete(run(encoded_urls, media_type)) 24 | LOGGER.success(f"Decoded {len(urls)} {media_type} URLs.") 25 | return urls 26 | 27 | 28 | async def run(encoded_urls: List[Dict[str, str]], media_type) -> List[dict]: 29 | """ 30 | Create async HTTP session and decode all URLs. 31 | 32 | :param encoded_urls: Resource URLs to decode. 33 | :type encoded_urls: Dict[str, List[Dict[str, str]]] 34 | :param media_type: Type of media urls to generate. 35 | :type media_type: str 36 | """ 37 | headers = { 38 | "authority": "app.snapchat.com", 39 | "content-type": "application/x-www-form-urlencoded", 40 | "accept": "*/*", 41 | } 42 | async with ClientSession(headers=headers) as session: 43 | decoded_urls = await fetch_all(session, encoded_urls, media_type) 44 | return decoded_urls 45 | 46 | 47 | async def fetch_all( 48 | session: ClientSession, media_urls: List[Dict[str, str]], media_type: str 49 | ) -> List[Dict[str, str]]: 50 | """ 51 | Asynchronously decode all URLs. 52 | 53 | :param session: Async HTTP requests session. 54 | :type session: ClientSession 55 | :param media_urls: Resource URLs to fetch. 56 | :type media_urls: List[Dict[str, str]] 57 | :param media_type: Type of media urls to generate. 58 | :type media_type: str 59 | :returns: List[Dict[str, str]] 60 | """ 61 | tasks = [] 62 | for i, media_source in enumerate(media_urls): 63 | task = asyncio.create_task( 64 | fetch_decoded_url(session, media_source, i, len(media_urls), media_type) 65 | ) 66 | tasks.append(task) 67 | results = await asyncio.gather(*tasks) 68 | return results 69 | 70 | 71 | async def fetch_decoded_url( 72 | session, media_source: Dict[str, str], count: int, total_count: int, media_type: str 73 | ) -> Dict[str, str]: 74 | """ 75 | Fetch URL and create a dictionary representing the resource. 76 | 77 | :param session: Async HTTP requests session. 78 | :type session: ClientSession 79 | :param media_source: Single resource to fetch. 80 | :type media_source: Dict[str, str] 81 | :param count: Current URL count. 82 | :type count: int 83 | :param total_count: Total URL count. 84 | :type total_count: int 85 | :param media_type: Type of media urls to generate. 86 | :type media_type: str 87 | :returns: Dict[str, str] 88 | """ 89 | try: 90 | async with session.post(media_source["url"]) as response: 91 | decoded_url = await response.text() 92 | if decoded_url == "": 93 | raise Exception( 94 | f"Decoded URL returned empty string; export may have expired." 95 | ) 96 | resource = {"url": decoded_url, "date": media_source["date"]} 97 | LOGGER.info(f"Decoded {count} of {total_count} {media_type}: {decoded_url}") 98 | return resource 99 | except InvalidURL as e: 100 | LOGGER.error(f"Unable to decode invalid URL `{media_source['date']}`: {e}") 101 | raise e 102 | except ClientError as e: 103 | LOGGER.error(f"Error while decoding URL `{media_source['date']}`: {e}") 104 | raise e 105 | except Exception as e: 106 | LOGGER.error(f"Unexpected error: {e}") 107 | raise e 108 | -------------------------------------------------------------------------------- /snapchat_memory_fetcher/fetcher.py: -------------------------------------------------------------------------------- 1 | """Fetch `Snapchat Memories` media files and save to local drive.""" 2 | import asyncio 3 | from os import mkdir, path 4 | from typing import Dict, List 5 | 6 | import aiofiles 7 | from aiohttp import ClientError, ClientSession, InvalidURL 8 | 9 | from config import MEDIA_EXPORT_FILEPATH 10 | from log import LOGGER 11 | 12 | 13 | def download_snapchat_memories(decoded_memories: List[Dict[str, str]], media_type: str): 14 | """ 15 | Fetch media files and save to local drive. 16 | 17 | :param decoded_memories: Resource URLs to fetch. 18 | :type decoded_memories: List[Dict[str, str]] 19 | :param media_type: Type of media to fetch (photo or video). 20 | :type media_type: str 21 | """ 22 | saved_media_destination = f"{MEDIA_EXPORT_FILEPATH}/{media_type}" 23 | if path.exists(saved_media_destination) is False: 24 | mkdir(saved_media_destination) 25 | loop = asyncio.get_event_loop() 26 | loop.run_until_complete(run(decoded_memories, media_type)) 27 | LOGGER.success(f"Completed downloading {len(decoded_memories)} {media_type}.") 28 | 29 | 30 | async def run(decoded_memory_urls: List[Dict[str, str]], media_type: str): 31 | """ 32 | Create async HTTP session and fetch media from URLs. 33 | 34 | :param decoded_memory_urls: Resource URLs to fetch. 35 | :type decoded_memory_urls: List[Dict[str, str]] 36 | :param media_type: Type of media to fetch (photo or video). 37 | :type media_type: str 38 | """ 39 | headers = { 40 | "connection": "keep-alive", 41 | "host": "sc-prod-memories-dmd-us-east-2.s3.us-east-2.amazonaws.com", 42 | "accept": "*/*", 43 | } 44 | decoded_memories = decoded_memory_urls 45 | async with ClientSession(headers=headers) as session: 46 | await download_all(session, decoded_memories, media_type) 47 | 48 | 49 | async def download_all( 50 | session: ClientSession, decoded_memory_urls: List[Dict[str, str]], media_type: str 51 | ): 52 | """ 53 | Concurrently download all photos/videos. 54 | 55 | :param session: Async HTTP requests session. 56 | :type session: ClientSession 57 | :param decoded_memory_urls: Resource URLs to fetch. 58 | :type decoded_memory_urls: List[Dict[str, str]] 59 | :param media_type: Type of media to fetch (photo or video). 60 | :type media_type: str 61 | """ 62 | tasks = [] 63 | for i, memory in enumerate(decoded_memory_urls): 64 | task = asyncio.create_task( 65 | fetch_snapchat_memory( 66 | session, memory, i, len(decoded_memory_urls), media_type 67 | ) 68 | ) 69 | tasks.append(task) 70 | result = await asyncio.gather(*tasks) 71 | return result 72 | 73 | 74 | async def fetch_snapchat_memory( 75 | session: ClientSession, 76 | memory: Dict[str, str], 77 | count: int, 78 | total_count: int, 79 | media_type: str, 80 | ): 81 | """ 82 | Download single media file and write to local directory. 83 | 84 | :param session: Async HTTP requests session. 85 | :type session: ClientSession 86 | :param memory: Resource URL to fetch with date as filename. 87 | :type memory: Dict[str, str] 88 | :param count: Current URL count. 89 | :type count: int 90 | :param total_count: Total URL count. 91 | :type total_count: int 92 | :param media_type: Type of media to fetch (photo or video). 93 | :type media_type: str 94 | """ 95 | filepath = f"{MEDIA_EXPORT_FILEPATH}/{media_type}/{memory['date']}{'.jpg' if media_type == 'photos' else '.mp4'}" 96 | try: 97 | async with session.get(memory["url"]) as response: 98 | if response.status == 200: 99 | data = await response.read() 100 | async with aiofiles.open(filepath, mode="wb+") as f: 101 | await f.write(data) 102 | LOGGER.info( 103 | f"Fetched {media_type} {count} of {total_count}: {memory['date']}" 104 | ) 105 | await f.close() 106 | except InvalidURL as e: 107 | LOGGER.error(f"Unable to decode invalid URL `{memory['url']}`: {e}") 108 | except ClientError as e: 109 | LOGGER.error(f"Error while decoding URL `{memory['url']}`: {e}") 110 | except Exception as e: 111 | LOGGER.error(f"Unexpected error while decoding URL `{memory['url']}`: {e}") 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snapchat "Memories" Fetcher 2 | 3 | ![Python](https://img.shields.io/badge/Python-~3.9-blue.svg?logo=python&longCache=true&logoColor=white&colorB=5e81ac&style=flat-square&colorA=4c566a) 4 | ![AsyncIO](https://img.shields.io/badge/asyncio-^v3.4.3-blue.svg?longCache=true&logo=python&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a) 5 | ![AioFiles](https://img.shields.io/badge/aiofiles-^v3.5.0-blue.svg?longCache=true&logo=python&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a) 6 | ![AioHTTP](https://img.shields.io/badge/aiohttp-^v3.7.4-blue.svg?longCache=true&logo=python&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a) 7 | ![AioFiles](https://img.shields.io/badge/aiofiles-^v3.5.0-blue.svg?longCache=true&logo=python&style=flat-square&logoColor=white&colorB=5e81ac&colorA=4c566a) 8 | ![GitHub Last Commit](https://img.shields.io/github/last-commit/google/skia.svg?style=flat-square&colorA=4c566a&logo=GitHub&colorB=a3be8c) 9 | [![GitHub Issues](https://img.shields.io/github/issues/toddbirchard/snapchat-memory-fetcher.svg?style=flat-square&colorA=4c566a&logo=GitHub&colorB=ebcb8b)](https://github.com/toddbirchard/snapchat-memory-fetcher/issues) 10 | [![GitHub Stars](https://img.shields.io/github/stars/toddbirchard/snapchat-memory-fetcher.svg?style=flat-square&colorA=4c566a&logo=GitHub&colorB=ebcb8b)](https://github.com/toddbirchard/snapchat-memory-fetcher/stargazers) 11 | [![GitHub Forks](https://img.shields.io/github/forks/toddbirchard/snapchat-memory-fetcher.svg?style=flat-square&colorA=4c566a&logo=GitHub&colorB=ebcb8b)](https://github.com/toddbirchard/snapchat-memory-fetcher/network) 12 | 13 | In compliance with the [California Consumer Privacy Act of 2018 (“CCPA”)](https://oag.ca.gov/privacy/ccpa), businesses which collect and store user data must allow customers the ability to request data the company has collected about the requesting user: 14 | 15 | > "... you may ask businesses to disclose what personal information they have about you and what they do with that information, to delete your personal information and not to sell your personal information. You also have the right to be notified, before or at the point businesses collect your personal information, of the types of personal information they are collecting and what they may do with that information." 16 | 17 | Many businesses (such as Snapchat) begrudgingly comply by "technically" allowing users to do this, but in such as way that is impossible for a human to parse. While Snapchat _technically_ allows users to request photos and videos Snapchat has stored (dubbed "Memories"), they do so in a way which is unusable by human beings. Sparing the technical details, exporting your account's memories is hidden behind a convoluted process of clicking individual URLs, which serve as proxies to reveal _actual_ URLs containing your data. 18 | 19 | The workflow Snapchat is intending to implement is unreasonable. To claim a single media file _which belongs to you_, users are expected to individually click URLs one-by-one. In reality, these URLs simply link to other URls, which will eventually fetch a single media file of the user. For context, by account has over 3500 media files. 20 | 21 | Snapchat's practice of intentionally complicating this process is of questionable legality, and surely an act of defiance. This script automates the process of max-exporting media from Snapchat data exports to resolve this issue in the meantime while the questionable legality of Snapchat's practices are persued. 22 | 23 | ## Getting Started 24 | 25 | ### Requesting your data 26 | 27 | 1. User data can be requested from Snapchat [here](https://accounts.snapchat.com/accounts/welcome). 28 | 2. Under **Manage My Account**, select *My Data*. 29 | 3. Your data export will be emailed to you as a **.zip** file containing the following: 30 | 31 | ```shell 32 | /mydata_1618592678039 33 | ├── index.html 34 | ├── /html 35 | │ └── *.html 36 | └── /json 37 | └── *.json 38 | ``` 39 | 40 | You can explore your exported data via the `index.html` page: 41 | 42 | ![index.html](https://github.com/toddbirchard/snapchat-memory-fetcher/blob/master/.github/snapchat_data_export@2x.png?raw=true) 43 | 44 | ### Running this script 45 | 46 | 1. Clone this repo (`git clone https://github.com/toddbirchard/snapchat-memory-fetcher.git`). 47 | 2. Drop the contents of your export to the `/export` directory of this repo. 48 | 3. Initialize project with requirements via `make install`. 49 | 4. Run script via `make run`. 50 | 51 | ### Result 52 | 53 | * The first time this script is run, it will export decoded URLs extracted from your data into JSON files as `/urls/photos.json` and `/urls/videos.json`. Urls only need to be decoded once and can be reused for future fetching. 54 | * The script will then save all photos and videos associated with your account to `/downloads/photos` and `/downloads/videos`, respectively. 55 | 56 | This script utilizes Python concurrency in both decoding URLS and fetching media, and therefore should run quickly. Note your hard drive space available as you may be downloading thousands of uncompressed videos. 57 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "aiofiles" 3 | version = "0.6.0" 4 | description = "File support for asyncio." 5 | category = "main" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "aiohttp" 11 | version = "3.7.4.post0" 12 | description = "Async http client/server framework (asyncio)" 13 | category = "main" 14 | optional = false 15 | python-versions = ">=3.6" 16 | 17 | [package.dependencies] 18 | async-timeout = ">=3.0,<4.0" 19 | attrs = ">=17.3.0" 20 | chardet = ">=2.0,<5.0" 21 | multidict = ">=4.5,<7.0" 22 | typing-extensions = ">=3.6.5" 23 | yarl = ">=1.0,<2.0" 24 | 25 | [package.extras] 26 | speedups = ["aiodns", "brotlipy", "cchardet"] 27 | 28 | [[package]] 29 | name = "appdirs" 30 | version = "1.4.4" 31 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 32 | category = "dev" 33 | optional = false 34 | python-versions = "*" 35 | 36 | [[package]] 37 | name = "async-timeout" 38 | version = "3.0.1" 39 | description = "Timeout context manager for asyncio programs" 40 | category = "main" 41 | optional = false 42 | python-versions = ">=3.5.3" 43 | 44 | [[package]] 45 | name = "asyncio" 46 | version = "3.4.3" 47 | description = "reference implementation of PEP 3156" 48 | category = "main" 49 | optional = false 50 | python-versions = "*" 51 | 52 | [[package]] 53 | name = "atomicwrites" 54 | version = "1.4.0" 55 | description = "Atomic file writes." 56 | category = "dev" 57 | optional = false 58 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 59 | 60 | [[package]] 61 | name = "attrs" 62 | version = "21.2.0" 63 | description = "Classes Without Boilerplate" 64 | category = "main" 65 | optional = false 66 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 67 | 68 | [package.extras] 69 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] 70 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 71 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] 72 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] 73 | 74 | [[package]] 75 | name = "black" 76 | version = "20.8b1" 77 | description = "The uncompromising code formatter." 78 | category = "dev" 79 | optional = false 80 | python-versions = ">=3.6" 81 | 82 | [package.dependencies] 83 | appdirs = "*" 84 | click = ">=7.1.2" 85 | mypy-extensions = ">=0.4.3" 86 | pathspec = ">=0.6,<1" 87 | regex = ">=2020.1.8" 88 | toml = ">=0.10.1" 89 | typed-ast = ">=1.4.0" 90 | typing-extensions = ">=3.7.4" 91 | 92 | [package.extras] 93 | colorama = ["colorama (>=0.4.3)"] 94 | d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] 95 | 96 | [[package]] 97 | name = "chardet" 98 | version = "4.0.0" 99 | description = "Universal encoding detector for Python 2 and 3" 100 | category = "main" 101 | optional = false 102 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 103 | 104 | [[package]] 105 | name = "click" 106 | version = "8.0.0" 107 | description = "Composable command line interface toolkit" 108 | category = "dev" 109 | optional = false 110 | python-versions = ">=3.6" 111 | 112 | [package.dependencies] 113 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 114 | 115 | [[package]] 116 | name = "colorama" 117 | version = "0.4.4" 118 | description = "Cross-platform colored terminal text." 119 | category = "main" 120 | optional = false 121 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 122 | 123 | [[package]] 124 | name = "flake8" 125 | version = "4.0.1" 126 | description = "the modular source code checker: pep8 pyflakes and co" 127 | category = "dev" 128 | optional = false 129 | python-versions = ">=3.6" 130 | 131 | [package.dependencies] 132 | mccabe = ">=0.6.0,<0.7.0" 133 | pycodestyle = ">=2.8.0,<2.9.0" 134 | pyflakes = ">=2.4.0,<2.5.0" 135 | 136 | [[package]] 137 | name = "idna" 138 | version = "3.1" 139 | description = "Internationalized Domain Names in Applications (IDNA)" 140 | category = "main" 141 | optional = false 142 | python-versions = ">=3.4" 143 | 144 | [[package]] 145 | name = "iniconfig" 146 | version = "1.1.1" 147 | description = "iniconfig: brain-dead simple config-ini parsing" 148 | category = "dev" 149 | optional = false 150 | python-versions = "*" 151 | 152 | [[package]] 153 | name = "isort" 154 | version = "5.8.0" 155 | description = "A Python utility / library to sort Python imports." 156 | category = "dev" 157 | optional = false 158 | python-versions = ">=3.6,<4.0" 159 | 160 | [package.extras] 161 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 162 | requirements_deprecated_finder = ["pipreqs", "pip-api"] 163 | colors = ["colorama (>=0.4.3,<0.5.0)"] 164 | 165 | [[package]] 166 | name = "loguru" 167 | version = "0.5.3" 168 | description = "Python logging made (stupidly) simple" 169 | category = "main" 170 | optional = false 171 | python-versions = ">=3.5" 172 | 173 | [package.dependencies] 174 | colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} 175 | win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} 176 | 177 | [package.extras] 178 | dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.10b0)", "isort (>=5.1.1)"] 179 | 180 | [[package]] 181 | name = "mccabe" 182 | version = "0.6.1" 183 | description = "McCabe checker, plugin for flake8" 184 | category = "dev" 185 | optional = false 186 | python-versions = "*" 187 | 188 | [[package]] 189 | name = "mock" 190 | version = "4.0.3" 191 | description = "Rolling backport of unittest.mock for all Pythons" 192 | category = "dev" 193 | optional = false 194 | python-versions = ">=3.6" 195 | 196 | [package.extras] 197 | build = ["twine", "wheel", "blurb"] 198 | docs = ["sphinx"] 199 | test = ["pytest (<5.4)", "pytest-cov"] 200 | 201 | [[package]] 202 | name = "multidict" 203 | version = "5.1.0" 204 | description = "multidict implementation" 205 | category = "main" 206 | optional = false 207 | python-versions = ">=3.6" 208 | 209 | [[package]] 210 | name = "mypy" 211 | version = "0.812" 212 | description = "Optional static typing for Python" 213 | category = "dev" 214 | optional = false 215 | python-versions = ">=3.5" 216 | 217 | [package.dependencies] 218 | mypy-extensions = ">=0.4.3,<0.5.0" 219 | typed-ast = ">=1.4.0,<1.5.0" 220 | typing-extensions = ">=3.7.4" 221 | 222 | [package.extras] 223 | dmypy = ["psutil (>=4.0)"] 224 | 225 | [[package]] 226 | name = "mypy-extensions" 227 | version = "0.4.3" 228 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 229 | category = "dev" 230 | optional = false 231 | python-versions = "*" 232 | 233 | [[package]] 234 | name = "packaging" 235 | version = "20.9" 236 | description = "Core utilities for Python packages" 237 | category = "dev" 238 | optional = false 239 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 240 | 241 | [package.dependencies] 242 | pyparsing = ">=2.0.2" 243 | 244 | [[package]] 245 | name = "pathspec" 246 | version = "0.8.1" 247 | description = "Utility library for gitignore style pattern matching of file paths." 248 | category = "dev" 249 | optional = false 250 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 251 | 252 | [[package]] 253 | name = "pluggy" 254 | version = "0.13.1" 255 | description = "plugin and hook calling mechanisms for python" 256 | category = "dev" 257 | optional = false 258 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 259 | 260 | [package.extras] 261 | dev = ["pre-commit", "tox"] 262 | 263 | [[package]] 264 | name = "py" 265 | version = "1.10.0" 266 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 267 | category = "dev" 268 | optional = false 269 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 270 | 271 | [[package]] 272 | name = "pycodestyle" 273 | version = "2.8.0" 274 | description = "Python style guide checker" 275 | category = "dev" 276 | optional = false 277 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 278 | 279 | [[package]] 280 | name = "pyflakes" 281 | version = "2.4.0" 282 | description = "passive checker of Python programs" 283 | category = "dev" 284 | optional = false 285 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 286 | 287 | [[package]] 288 | name = "pyparsing" 289 | version = "2.4.7" 290 | description = "Python parsing module" 291 | category = "dev" 292 | optional = false 293 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 294 | 295 | [[package]] 296 | name = "pytest" 297 | version = "6.2.4" 298 | description = "pytest: simple powerful testing with Python" 299 | category = "dev" 300 | optional = false 301 | python-versions = ">=3.6" 302 | 303 | [package.dependencies] 304 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 305 | attrs = ">=19.2.0" 306 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 307 | iniconfig = "*" 308 | packaging = "*" 309 | pluggy = ">=0.12,<1.0.0a1" 310 | py = ">=1.8.2" 311 | toml = "*" 312 | 313 | [package.extras] 314 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 315 | 316 | [[package]] 317 | name = "regex" 318 | version = "2021.4.4" 319 | description = "Alternative regular expression module, to replace re." 320 | category = "dev" 321 | optional = false 322 | python-versions = "*" 323 | 324 | [[package]] 325 | name = "simplejson" 326 | version = "3.17.2" 327 | description = "Simple, fast, extensible JSON encoder/decoder for Python" 328 | category = "main" 329 | optional = false 330 | python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*" 331 | 332 | [[package]] 333 | name = "toml" 334 | version = "0.10.2" 335 | description = "Python Library for Tom's Obvious, Minimal Language" 336 | category = "dev" 337 | optional = false 338 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 339 | 340 | [[package]] 341 | name = "typed-ast" 342 | version = "1.4.3" 343 | description = "a fork of Python 2 and 3 ast modules with type comment support" 344 | category = "dev" 345 | optional = false 346 | python-versions = "*" 347 | 348 | [[package]] 349 | name = "typing-extensions" 350 | version = "3.10.0.0" 351 | description = "Backported and Experimental Type Hints for Python 3.5+" 352 | category = "main" 353 | optional = false 354 | python-versions = "*" 355 | 356 | [[package]] 357 | name = "win32-setctime" 358 | version = "1.0.3" 359 | description = "A small Python utility to set file creation time on Windows" 360 | category = "main" 361 | optional = false 362 | python-versions = ">=3.5" 363 | 364 | [package.extras] 365 | dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] 366 | 367 | [[package]] 368 | name = "yarl" 369 | version = "1.6.3" 370 | description = "Yet another URL library" 371 | category = "main" 372 | optional = false 373 | python-versions = ">=3.6" 374 | 375 | [package.dependencies] 376 | idna = ">=2.0" 377 | multidict = ">=4.0" 378 | 379 | [metadata] 380 | lock-version = "1.1" 381 | python-versions = "^3.9" 382 | content-hash = "5ee3edca4b1c50f1203fcaf772007e734b35e3df0b308a0e90f212902c2ad4aa" 383 | 384 | [metadata.files] 385 | aiofiles = [ 386 | {file = "aiofiles-0.6.0-py3-none-any.whl", hash = "sha256:bd3019af67f83b739f8e4053c6c0512a7f545b9a8d91aaeab55e6e0f9d123c27"}, 387 | {file = "aiofiles-0.6.0.tar.gz", hash = "sha256:e0281b157d3d5d59d803e3f4557dcc9a3dff28a4dd4829a9ff478adae50ca092"}, 388 | ] 389 | aiohttp = [ 390 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3cf75f7cdc2397ed4442594b935a11ed5569961333d49b7539ea741be2cc79d5"}, 391 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4b302b45040890cea949ad092479e01ba25911a15e648429c7c5aae9650c67a8"}, 392 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fe60131d21b31fd1a14bd43e6bb88256f69dfc3188b3a89d736d6c71ed43ec95"}, 393 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:393f389841e8f2dfc86f774ad22f00923fdee66d238af89b70ea314c4aefd290"}, 394 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:c6e9dcb4cb338d91a73f178d866d051efe7c62a7166653a91e7d9fb18274058f"}, 395 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:5df68496d19f849921f05f14f31bd6ef53ad4b00245da3195048c69934521809"}, 396 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0563c1b3826945eecd62186f3f5c7d31abb7391fedc893b7e2b26303b5a9f3fe"}, 397 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-win32.whl", hash = "sha256:3d78619672183be860b96ed96f533046ec97ca067fd46ac1f6a09cd9b7484287"}, 398 | {file = "aiohttp-3.7.4.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:f705e12750171c0ab4ef2a3c76b9a4024a62c4103e3a55dd6f99265b9bc6fcfc"}, 399 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:230a8f7e24298dea47659251abc0fd8b3c4e38a664c59d4b89cca7f6c09c9e87"}, 400 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2e19413bf84934d651344783c9f5e22dee452e251cfd220ebadbed2d9931dbf0"}, 401 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e4b2b334e68b18ac9817d828ba44d8fcb391f6acb398bcc5062b14b2cbeac970"}, 402 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:d012ad7911653a906425d8473a1465caa9f8dea7fcf07b6d870397b774ea7c0f"}, 403 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:40eced07f07a9e60e825554a31f923e8d3997cfc7fb31dbc1328c70826e04cde"}, 404 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:209b4a8ee987eccc91e2bd3ac36adee0e53a5970b8ac52c273f7f8fd4872c94c"}, 405 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:14762875b22d0055f05d12abc7f7d61d5fd4fe4642ce1a249abdf8c700bf1fd8"}, 406 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-win32.whl", hash = "sha256:7615dab56bb07bff74bc865307aeb89a8bfd9941d2ef9d817b9436da3a0ea54f"}, 407 | {file = "aiohttp-3.7.4.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:d9e13b33afd39ddeb377eff2c1c4f00544e191e1d1dee5b6c51ddee8ea6f0cf5"}, 408 | {file = "aiohttp-3.7.4.post0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:547da6cacac20666422d4882cfcd51298d45f7ccb60a04ec27424d2f36ba3eaf"}, 409 | {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af9aa9ef5ba1fd5b8c948bb11f44891968ab30356d65fd0cc6707d989cd521df"}, 410 | {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:64322071e046020e8797117b3658b9c2f80e3267daec409b350b6a7a05041213"}, 411 | {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bb437315738aa441251214dad17428cafda9cdc9729499f1d6001748e1d432f4"}, 412 | {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:e54962802d4b8b18b6207d4a927032826af39395a3bd9196a5af43fc4e60b009"}, 413 | {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:a00bb73540af068ca7390e636c01cbc4f644961896fa9363154ff43fd37af2f5"}, 414 | {file = "aiohttp-3.7.4.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:79ebfc238612123a713a457d92afb4096e2148be17df6c50fb9bf7a81c2f8013"}, 415 | {file = "aiohttp-3.7.4.post0-cp38-cp38-win32.whl", hash = "sha256:515dfef7f869a0feb2afee66b957cc7bbe9ad0cdee45aec7fdc623f4ecd4fb16"}, 416 | {file = "aiohttp-3.7.4.post0-cp38-cp38-win_amd64.whl", hash = "sha256:114b281e4d68302a324dd33abb04778e8557d88947875cbf4e842c2c01a030c5"}, 417 | {file = "aiohttp-3.7.4.post0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:7b18b97cf8ee5452fa5f4e3af95d01d84d86d32c5e2bfa260cf041749d66360b"}, 418 | {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:15492a6368d985b76a2a5fdd2166cddfea5d24e69eefed4630cbaae5c81d89bd"}, 419 | {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bdb230b4943891321e06fc7def63c7aace16095be7d9cf3b1e01be2f10fba439"}, 420 | {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:cffe3ab27871bc3ea47df5d8f7013945712c46a3cc5a95b6bee15887f1675c22"}, 421 | {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:f881853d2643a29e643609da57b96d5f9c9b93f62429dcc1cbb413c7d07f0e1a"}, 422 | {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:a5ca29ee66f8343ed336816c553e82d6cade48a3ad702b9ffa6125d187e2dedb"}, 423 | {file = "aiohttp-3.7.4.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:17c073de315745a1510393a96e680d20af8e67e324f70b42accbd4cb3315c9fb"}, 424 | {file = "aiohttp-3.7.4.post0-cp39-cp39-win32.whl", hash = "sha256:932bb1ea39a54e9ea27fc9232163059a0b8855256f4052e776357ad9add6f1c9"}, 425 | {file = "aiohttp-3.7.4.post0-cp39-cp39-win_amd64.whl", hash = "sha256:02f46fc0e3c5ac58b80d4d56eb0a7c7d97fcef69ace9326289fb9f1955e65cfe"}, 426 | {file = "aiohttp-3.7.4.post0.tar.gz", hash = "sha256:493d3299ebe5f5a7c66b9819eacdcfbbaaf1a8e84911ddffcdc48888497afecf"}, 427 | ] 428 | appdirs = [ 429 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, 430 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, 431 | ] 432 | async-timeout = [ 433 | {file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"}, 434 | {file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"}, 435 | ] 436 | asyncio = [ 437 | {file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"}, 438 | {file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"}, 439 | {file = "asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d"}, 440 | {file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"}, 441 | ] 442 | atomicwrites = [ 443 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 444 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 445 | ] 446 | attrs = [ 447 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, 448 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, 449 | ] 450 | black = [ 451 | {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, 452 | ] 453 | chardet = [ 454 | {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, 455 | {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, 456 | ] 457 | click = [ 458 | {file = "click-8.0.0-py3-none-any.whl", hash = "sha256:e90e62ced43dc8105fb9a26d62f0d9340b5c8db053a814e25d95c19873ae87db"}, 459 | {file = "click-8.0.0.tar.gz", hash = "sha256:7d8c289ee437bcb0316820ccee14aefcb056e58d31830ecab8e47eda6540e136"}, 460 | ] 461 | colorama = [ 462 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 463 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 464 | ] 465 | flake8 = [ 466 | {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, 467 | {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, 468 | ] 469 | idna = [ 470 | {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"}, 471 | {file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"}, 472 | ] 473 | iniconfig = [ 474 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 475 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 476 | ] 477 | isort = [ 478 | {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, 479 | {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, 480 | ] 481 | loguru = [ 482 | {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"}, 483 | {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"}, 484 | ] 485 | mccabe = [ 486 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 487 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 488 | ] 489 | mock = [ 490 | {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, 491 | {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, 492 | ] 493 | multidict = [ 494 | {file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"}, 495 | {file = "multidict-5.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf"}, 496 | {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281"}, 497 | {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d"}, 498 | {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d"}, 499 | {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da"}, 500 | {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224"}, 501 | {file = "multidict-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26"}, 502 | {file = "multidict-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6"}, 503 | {file = "multidict-5.1.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76"}, 504 | {file = "multidict-5.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a"}, 505 | {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f"}, 506 | {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348"}, 507 | {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93"}, 508 | {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9"}, 509 | {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37"}, 510 | {file = "multidict-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5"}, 511 | {file = "multidict-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632"}, 512 | {file = "multidict-5.1.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952"}, 513 | {file = "multidict-5.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79"}, 514 | {file = "multidict-5.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456"}, 515 | {file = "multidict-5.1.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7"}, 516 | {file = "multidict-5.1.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635"}, 517 | {file = "multidict-5.1.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a"}, 518 | {file = "multidict-5.1.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea"}, 519 | {file = "multidict-5.1.0-cp38-cp38-win32.whl", hash = "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656"}, 520 | {file = "multidict-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3"}, 521 | {file = "multidict-5.1.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93"}, 522 | {file = "multidict-5.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647"}, 523 | {file = "multidict-5.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d"}, 524 | {file = "multidict-5.1.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8"}, 525 | {file = "multidict-5.1.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1"}, 526 | {file = "multidict-5.1.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841"}, 527 | {file = "multidict-5.1.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda"}, 528 | {file = "multidict-5.1.0-cp39-cp39-win32.whl", hash = "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"}, 529 | {file = "multidict-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359"}, 530 | {file = "multidict-5.1.0.tar.gz", hash = "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5"}, 531 | ] 532 | mypy = [ 533 | {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, 534 | {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, 535 | {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, 536 | {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, 537 | {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, 538 | {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, 539 | {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, 540 | {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, 541 | {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, 542 | {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, 543 | {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, 544 | {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, 545 | {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, 546 | {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, 547 | {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, 548 | {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, 549 | {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, 550 | {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, 551 | {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, 552 | {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, 553 | {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, 554 | {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, 555 | ] 556 | mypy-extensions = [ 557 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 558 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 559 | ] 560 | packaging = [ 561 | {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, 562 | {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, 563 | ] 564 | pathspec = [ 565 | {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, 566 | {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, 567 | ] 568 | pluggy = [ 569 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 570 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 571 | ] 572 | py = [ 573 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, 574 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, 575 | ] 576 | pycodestyle = [ 577 | {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, 578 | {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, 579 | ] 580 | pyflakes = [ 581 | {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, 582 | {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, 583 | ] 584 | pyparsing = [ 585 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 586 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 587 | ] 588 | pytest = [ 589 | {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, 590 | {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, 591 | ] 592 | regex = [ 593 | {file = "regex-2021.4.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000"}, 594 | {file = "regex-2021.4.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711"}, 595 | {file = "regex-2021.4.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11"}, 596 | {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968"}, 597 | {file = "regex-2021.4.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0"}, 598 | {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4"}, 599 | {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a"}, 600 | {file = "regex-2021.4.4-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7"}, 601 | {file = "regex-2021.4.4-cp36-cp36m-win32.whl", hash = "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29"}, 602 | {file = "regex-2021.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79"}, 603 | {file = "regex-2021.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8"}, 604 | {file = "regex-2021.4.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31"}, 605 | {file = "regex-2021.4.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a"}, 606 | {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5"}, 607 | {file = "regex-2021.4.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82"}, 608 | {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765"}, 609 | {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e"}, 610 | {file = "regex-2021.4.4-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439"}, 611 | {file = "regex-2021.4.4-cp37-cp37m-win32.whl", hash = "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d"}, 612 | {file = "regex-2021.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3"}, 613 | {file = "regex-2021.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500"}, 614 | {file = "regex-2021.4.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14"}, 615 | {file = "regex-2021.4.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480"}, 616 | {file = "regex-2021.4.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc"}, 617 | {file = "regex-2021.4.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093"}, 618 | {file = "regex-2021.4.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10"}, 619 | {file = "regex-2021.4.4-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f"}, 620 | {file = "regex-2021.4.4-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87"}, 621 | {file = "regex-2021.4.4-cp38-cp38-win32.whl", hash = "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac"}, 622 | {file = "regex-2021.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2"}, 623 | {file = "regex-2021.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17"}, 624 | {file = "regex-2021.4.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605"}, 625 | {file = "regex-2021.4.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9"}, 626 | {file = "regex-2021.4.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7"}, 627 | {file = "regex-2021.4.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8"}, 628 | {file = "regex-2021.4.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed"}, 629 | {file = "regex-2021.4.4-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c"}, 630 | {file = "regex-2021.4.4-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042"}, 631 | {file = "regex-2021.4.4-cp39-cp39-win32.whl", hash = "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6"}, 632 | {file = "regex-2021.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07"}, 633 | {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, 634 | ] 635 | simplejson = [ 636 | {file = "simplejson-3.17.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8"}, 637 | {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6"}, 638 | {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d"}, 639 | {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043"}, 640 | {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4"}, 641 | {file = "simplejson-3.17.2-cp27-cp27m-win32.whl", hash = "sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9"}, 642 | {file = "simplejson-3.17.2-cp27-cp27m-win_amd64.whl", hash = "sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667"}, 643 | {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06"}, 644 | {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f"}, 645 | {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b"}, 646 | {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f"}, 647 | {file = "simplejson-3.17.2-cp33-cp33m-win32.whl", hash = "sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746"}, 648 | {file = "simplejson-3.17.2-cp33-cp33m-win_amd64.whl", hash = "sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748"}, 649 | {file = "simplejson-3.17.2-cp34-cp34m-win32.whl", hash = "sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3"}, 650 | {file = "simplejson-3.17.2-cp34-cp34m-win_amd64.whl", hash = "sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b"}, 651 | {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d"}, 652 | {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956"}, 653 | {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d"}, 654 | {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971"}, 655 | {file = "simplejson-3.17.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a"}, 656 | {file = "simplejson-3.17.2-cp35-cp35m-win32.whl", hash = "sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396"}, 657 | {file = "simplejson-3.17.2-cp35-cp35m-win_amd64.whl", hash = "sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a"}, 658 | {file = "simplejson-3.17.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb"}, 659 | {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb"}, 660 | {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f"}, 661 | {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45"}, 662 | {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139"}, 663 | {file = "simplejson-3.17.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46"}, 664 | {file = "simplejson-3.17.2-cp36-cp36m-win32.whl", hash = "sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b"}, 665 | {file = "simplejson-3.17.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625"}, 666 | {file = "simplejson-3.17.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25"}, 667 | {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf"}, 668 | {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0"}, 669 | {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f"}, 670 | {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04"}, 671 | {file = "simplejson-3.17.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278"}, 672 | {file = "simplejson-3.17.2-cp37-cp37m-win32.whl", hash = "sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34"}, 673 | {file = "simplejson-3.17.2-cp37-cp37m-win_amd64.whl", hash = "sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0"}, 674 | {file = "simplejson-3.17.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da"}, 675 | {file = "simplejson-3.17.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a"}, 676 | {file = "simplejson-3.17.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995"}, 677 | {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc"}, 678 | {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94"}, 679 | {file = "simplejson-3.17.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8"}, 680 | {file = "simplejson-3.17.2.tar.gz", hash = "sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841"}, 681 | ] 682 | toml = [ 683 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 684 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 685 | ] 686 | typed-ast = [ 687 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, 688 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, 689 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, 690 | {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, 691 | {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, 692 | {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, 693 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, 694 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, 695 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, 696 | {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, 697 | {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, 698 | {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, 699 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, 700 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, 701 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, 702 | {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, 703 | {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, 704 | {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, 705 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, 706 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, 707 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, 708 | {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, 709 | {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, 710 | {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, 711 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, 712 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, 713 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, 714 | {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, 715 | {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, 716 | {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, 717 | ] 718 | typing-extensions = [ 719 | {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, 720 | {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, 721 | {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, 722 | ] 723 | win32-setctime = [ 724 | {file = "win32_setctime-1.0.3-py3-none-any.whl", hash = "sha256:dc925662de0a6eb987f0b01f599c01a8236cb8c62831c22d9cada09ad958243e"}, 725 | {file = "win32_setctime-1.0.3.tar.gz", hash = "sha256:4e88556c32fdf47f64165a2180ba4552f8bb32c1103a2fafd05723a0bd42bd4b"}, 726 | ] 727 | yarl = [ 728 | {file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"}, 729 | {file = "yarl-1.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478"}, 730 | {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6"}, 731 | {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e"}, 732 | {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406"}, 733 | {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76"}, 734 | {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366"}, 735 | {file = "yarl-1.6.3-cp36-cp36m-win32.whl", hash = "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721"}, 736 | {file = "yarl-1.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643"}, 737 | {file = "yarl-1.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e"}, 738 | {file = "yarl-1.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3"}, 739 | {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8"}, 740 | {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a"}, 741 | {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c"}, 742 | {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f"}, 743 | {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970"}, 744 | {file = "yarl-1.6.3-cp37-cp37m-win32.whl", hash = "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e"}, 745 | {file = "yarl-1.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50"}, 746 | {file = "yarl-1.6.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2"}, 747 | {file = "yarl-1.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec"}, 748 | {file = "yarl-1.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"}, 749 | {file = "yarl-1.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc"}, 750 | {file = "yarl-1.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959"}, 751 | {file = "yarl-1.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2"}, 752 | {file = "yarl-1.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2"}, 753 | {file = "yarl-1.6.3-cp38-cp38-win32.whl", hash = "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896"}, 754 | {file = "yarl-1.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a"}, 755 | {file = "yarl-1.6.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e"}, 756 | {file = "yarl-1.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724"}, 757 | {file = "yarl-1.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c"}, 758 | {file = "yarl-1.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25"}, 759 | {file = "yarl-1.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96"}, 760 | {file = "yarl-1.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0"}, 761 | {file = "yarl-1.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4"}, 762 | {file = "yarl-1.6.3-cp39-cp39-win32.whl", hash = "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424"}, 763 | {file = "yarl-1.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6"}, 764 | {file = "yarl-1.6.3.tar.gz", hash = "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10"}, 765 | ] 766 | --------------------------------------------------------------------------------