From 9fde78ec7e97723404805f03677a744f37e00dff Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Mon, 8 Jun 2020 23:04:05 +0200 Subject: [PATCH 01/75] fix: Link to 2.1 addon folder in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e3e5653..c60e8c6 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ and put it in `~/Anki/addons`. anki.sync.SYNC_BASE = addr anki.sync.SYNC_MEDIA_BASE = addr + "msync/" -[addons21]: https://apps.ankiweb.net/docs/addons.html#_add_on_folders +[addons21]: https://addon-docs.ankiweb.net/#/getting-started?id=add-on-folders ### AnkiDroid From d2968f37b3385f6796b7744bc13184bc2ae2ac22 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Sat, 27 Jun 2020 00:46:47 +0100 Subject: [PATCH 02/75] Add gitignore for python and jupyter notebooks --- .gitignore | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/.gitignore b/.gitignore index ca20904..9061ee6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,222 @@ /ankisyncd/_version.py /collections /venv + +# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,jupyternotebooks +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos,python,jupyternotebooks + +### JupyterNotebooks ### +# gitignore template for Jupyter Notebooks +# website: http://jupyter.org/ + +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# IPython +profile_default/ +ipython_config.py + +# Remove previous ipynb_checkpoints +# git rm -r .ipynb_checkpoints/ + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook + +# IPython + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos,python,jupyternotebooks \ No newline at end of file From b85838d5220777f09d1e967daf3a6d02b717d33f Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Sat, 27 Jun 2020 00:48:50 +0100 Subject: [PATCH 03/75] Add notebook to read collections --- notebooks/read_collections.ipynb | 182 +++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 notebooks/read_collections.ipynb diff --git a/notebooks/read_collections.ipynb b/notebooks/read_collections.ipynb new file mode 100644 index 0000000..865a6c6 --- /dev/null +++ b/notebooks/read_collections.ipynb @@ -0,0 +1,182 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cd .." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Reading Collections\n", + "\n", + "This notebook allows to view your collections. Note currently we are using the anki from the submodule. In the future, we should be able to use the anki installed using `pip install anki` however the current collections do not seem compatibile with the latest library." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Install Anki in venv\n", + "!pip3 install anki" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from anki import Collection\n", + "from anki.utils import intTime\n", + "import time" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Open Database\n", + "\n", + "Make sure you close the database otherwise it will be locked and you will not be able to use your sync server." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "collection_path = \"./collections/anki/collection.anki2\"\n", + "col = Collection(collection_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View Collections" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Collection Name: {col.name()}')\n", + "print(f'Cards in Collection: {col.noteCount()}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View Decks" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print('Decks:')\n", + "for deck in col.decks.all():\n", + " print(f\"{deck['id']}. {deck['name']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View Cards" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "deck_id = None\n", + "print('Cards in deck:')\n", + "i = 0\n", + "for card_id in col.decks.cids(deck_id):\n", + " i+=1\n", + " print(f'{i}. {card_id}')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### View Notes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "card_id = None\n", + "print('Notes in card:')\n", + "note_id = col.getCard(card_id).nid\n", + "print(f\"1. Front: {col.getNote(note_id).fields[0]}\")\n", + "print(f\"2. Back: {col.getNote(note_id).fields[1]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Close Database" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "col.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 09da3d7337c1ac9cf92c37aeb93d25ac1ab0d4f9 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 19:19:45 +0100 Subject: [PATCH 04/75] Move packages into src folder --- {addon => src/addon}/__init__.py | 0 {addon => src/addon}/config.json | 0 ankisyncctl.py => src/ankisyncctl.py | 0 ankisyncd.conf => src/ankisyncd.conf | 0 {ankisyncd => src/ankisyncd}/__init__.py | 0 {ankisyncd => src/ankisyncd}/__main__.py | 0 {ankisyncd => src/ankisyncd}/collection.py | 0 {ankisyncd => src/ankisyncd}/config.py | 0 {ankisyncd => src/ankisyncd}/full_sync.py | 0 {ankisyncd => src/ankisyncd}/media.py | 0 {ankisyncd => src/ankisyncd}/sessions.py | 0 {ankisyncd => src/ankisyncd}/sync_app.py | 0 {ankisyncd => src/ankisyncd}/thread.py | 0 {ankisyncd => src/ankisyncd}/users.py | 0 {utils => src/utils}/migrate_user_tables.py | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename {addon => src/addon}/__init__.py (100%) rename {addon => src/addon}/config.json (100%) rename ankisyncctl.py => src/ankisyncctl.py (100%) rename ankisyncd.conf => src/ankisyncd.conf (100%) rename {ankisyncd => src/ankisyncd}/__init__.py (100%) rename {ankisyncd => src/ankisyncd}/__main__.py (100%) rename {ankisyncd => src/ankisyncd}/collection.py (100%) rename {ankisyncd => src/ankisyncd}/config.py (100%) rename {ankisyncd => src/ankisyncd}/full_sync.py (100%) rename {ankisyncd => src/ankisyncd}/media.py (100%) rename {ankisyncd => src/ankisyncd}/sessions.py (100%) rename {ankisyncd => src/ankisyncd}/sync_app.py (100%) rename {ankisyncd => src/ankisyncd}/thread.py (100%) rename {ankisyncd => src/ankisyncd}/users.py (100%) rename {utils => src/utils}/migrate_user_tables.py (100%) diff --git a/addon/__init__.py b/src/addon/__init__.py similarity index 100% rename from addon/__init__.py rename to src/addon/__init__.py diff --git a/addon/config.json b/src/addon/config.json similarity index 100% rename from addon/config.json rename to src/addon/config.json diff --git a/ankisyncctl.py b/src/ankisyncctl.py similarity index 100% rename from ankisyncctl.py rename to src/ankisyncctl.py diff --git a/ankisyncd.conf b/src/ankisyncd.conf similarity index 100% rename from ankisyncd.conf rename to src/ankisyncd.conf diff --git a/ankisyncd/__init__.py b/src/ankisyncd/__init__.py similarity index 100% rename from ankisyncd/__init__.py rename to src/ankisyncd/__init__.py diff --git a/ankisyncd/__main__.py b/src/ankisyncd/__main__.py similarity index 100% rename from ankisyncd/__main__.py rename to src/ankisyncd/__main__.py diff --git a/ankisyncd/collection.py b/src/ankisyncd/collection.py similarity index 100% rename from ankisyncd/collection.py rename to src/ankisyncd/collection.py diff --git a/ankisyncd/config.py b/src/ankisyncd/config.py similarity index 100% rename from ankisyncd/config.py rename to src/ankisyncd/config.py diff --git a/ankisyncd/full_sync.py b/src/ankisyncd/full_sync.py similarity index 100% rename from ankisyncd/full_sync.py rename to src/ankisyncd/full_sync.py diff --git a/ankisyncd/media.py b/src/ankisyncd/media.py similarity index 100% rename from ankisyncd/media.py rename to src/ankisyncd/media.py diff --git a/ankisyncd/sessions.py b/src/ankisyncd/sessions.py similarity index 100% rename from ankisyncd/sessions.py rename to src/ankisyncd/sessions.py diff --git a/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py similarity index 100% rename from ankisyncd/sync_app.py rename to src/ankisyncd/sync_app.py diff --git a/ankisyncd/thread.py b/src/ankisyncd/thread.py similarity index 100% rename from ankisyncd/thread.py rename to src/ankisyncd/thread.py diff --git a/ankisyncd/users.py b/src/ankisyncd/users.py similarity index 100% rename from ankisyncd/users.py rename to src/ankisyncd/users.py diff --git a/utils/migrate_user_tables.py b/src/utils/migrate_user_tables.py similarity index 100% rename from utils/migrate_user_tables.py rename to src/utils/migrate_user_tables.py From c4ee2da949008b8a219335643ed64412d69e0268 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 19:30:26 +0100 Subject: [PATCH 05/75] Move anki submodule into src folder --- .gitmodules | 2 +- anki-bundled => src/anki-bundled | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename anki-bundled => src/anki-bundled (100%) diff --git a/.gitmodules b/.gitmodules index cc57eb1..9b79105 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "anki-bundled"] - path = anki-bundled + path = src/anki-bundled url = https://github.com/dae/anki.git diff --git a/anki-bundled b/src/anki-bundled similarity index 100% rename from anki-bundled rename to src/anki-bundled From 088e7ae1ac686a7da55eba22fb7f680f5a12d498 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:00:39 +0100 Subject: [PATCH 06/75] Add config folder to support multiple environments --- config/.env.example | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 config/.env.example diff --git a/config/.env.example b/config/.env.example new file mode 100644 index 0000000..e69de29 From f637e8362765d3c59e4fcf715d2dd36e33a0ead9 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:09:39 +0100 Subject: [PATCH 07/75] Add Makefile to simplify development commands --- Makefile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0404dba --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +#/bin/make + +ANKI_SERVER_NAME ?= "Anki Sync Server" +ANKI_SERVER_VERSION ?= "v0.1.0" +ANKI_SERVER_DESCRIPTION ?= "Self-hosted Anki Sync Server." +ENV ?= local + +-include config/.env.${ENV} +export + +.DEFAULT_GOAL := help +.PHONY: help #: Display list of command and exit. +help: + @awk 'BEGIN {FS = " ?#?: "; print ""${ANKI_SERVER_NAME}" "${ANKI_SERVER_VERSION}"\n"${ANKI_SERVER_DESCRIPTION}"\n\nUsage: make \033[36m\033[0m\n\nCommands:"} /^.PHONY: ?[a-zA-Z_-]/ { printf " \033[36m%-10s\033[0m %s\n", $$2, $$3 }' $(MAKEFILE_LIST) + +%: + @test -f scripts/${*}.sh + @${SHELL} scripts/${*}.sh \ No newline at end of file From 2cdb1ca98f20d11e3dde78fd2dd3feeefd98b08d Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:11:45 +0100 Subject: [PATCH 08/75] Add pyproject.toml --- pyproject.toml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9f1ed38 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[tool.poetry] +name = "anki-sync-server" +version = "0.1.0" +description = "Self-hosted Anki Sync Server." +authors = ["Vikash Kothary "] + +[tool.poetry.dependencies] +python = "^3.7" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" From e5695dd229c6e263cd4de7f2a899bd3d62a181cd Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:19:26 +0100 Subject: [PATCH 09/75] Add print-env command to print env variable --- scripts/print-env.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 scripts/print-env.sh diff --git a/scripts/print-env.sh b/scripts/print-env.sh new file mode 100644 index 0000000..e755e2e --- /dev/null +++ b/scripts/print-env.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# file: print-env.sh +# description: Print env variable. + +echo "${ENV}" \ No newline at end of file From cfeb466e13c40590f25dac00d0b05eda29dbf981 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:14:25 +0100 Subject: [PATCH 10/75] Add mkdocs dependency to build documentation --- poetry.lock | 345 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 346 insertions(+) create mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..836e1c1 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,345 @@ +[[package]] +category = "dev" +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 = "dev" +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 = "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 = "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 = "dev" +description = "Lightweight pipelining: using Python functions as pipeline jobs." +marker = "python_version > \"2.7\"" +name = "joblib" +optional = false +python-versions = ">=3.6" +version = "0.16.0" + +[[package]] +category = "dev" +description = "Python LiveReload is an awesome tool for web developers" +name = "livereload" +optional = false +python-versions = "*" +version = "2.6.2" + +[package.dependencies] +six = "*" + +[package.dependencies.tornado] +python = ">=2.8" +version = "*" + +[[package]] +category = "dev" +description = "A Python implementation of Lunr.js" +name = "lunr" +optional = false +python-versions = "*" +version = "0.5.8" + +[package.dependencies] +future = ">=0.16.0" +six = ">=1.11.0" + +[package.dependencies.nltk] +optional = true +python = ">=2.8" +version = ">=3.2.5" + +[package.extras] +languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] + +[[package]] +category = "dev" +description = "Python implementation of Markdown." +name = "markdown" +optional = false +python-versions = ">=3.5" +version = "3.2.2" + +[package.dependencies] +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +category = "dev" +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 = "dev" +description = "Project documentation with Markdown." +name = "mkdocs" +optional = false +python-versions = ">=3.5" +version = "1.1.2" + +[package.dependencies] +Jinja2 = ">=2.10.1" +Markdown = ">=3.2.1" +PyYAML = ">=3.10" +click = ">=3.3" +livereload = ">=2.5.1" +tornado = ">=5.0" + +[package.dependencies.lunr] +extras = ["languages"] +version = "0.5.8" + +[[package]] +category = "dev" +description = "Natural Language Toolkit" +marker = "python_version > \"2.7\"" +name = "nltk" +optional = false +python-versions = "*" +version = "3.5" + +[package.dependencies] +click = "*" +joblib = "*" +regex = "*" +tqdm = "*" + +[package.extras] +all = ["requests", "numpy", "python-crfsuite", "scikit-learn", "twython", "pyparsing", "scipy", "matplotlib", "gensim"] +corenlp = ["requests"] +machine_learning = ["gensim", "numpy", "python-crfsuite", "scikit-learn", "scipy"] +plot = ["matplotlib"] +tgrep = ["pyparsing"] +twitter = ["twython"] + +[[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." +marker = "python_version > \"2.7\"" +name = "regex" +optional = false +python-versions = "*" +version = "2020.7.14" + +[[package]] +category = "dev" +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 = "dev" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +name = "tornado" +optional = false +python-versions = ">= 3.5" +version = "6.0.4" + +[[package]] +category = "dev" +description = "Fast, Extensible Progress Meter" +marker = "python_version > \"2.7\"" +name = "tqdm" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.48.0" + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] + +[[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.1.0" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["jaraco.itertools", "func-timeout"] + +[metadata] +content-hash = "9a1964dfceeac6efd12397455372dcedea30aaa1a5d417e3eed4eff99258eb2d" +python-versions = "^3.7" + +[metadata.files] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] +future = [ + {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, +] +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"}, +] +jinja2 = [ + {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, + {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, +] +joblib = [ + {file = "joblib-0.16.0-py3-none-any.whl", hash = "sha256:d348c5d4ae31496b2aa060d6d9b787864dd204f9480baaa52d18850cb43e9f49"}, + {file = "joblib-0.16.0.tar.gz", hash = "sha256:8f52bf24c64b608bf0b2563e0e47d6fcf516abc8cfafe10cfd98ad66d94f92d6"}, +] +livereload = [ + {file = "livereload-2.6.2.tar.gz", hash = "sha256:d1eddcb5c5eb8d2ca1fa1f750e580da624c0f7fcb734aa5780dc81b7dcbd89be"}, +] +lunr = [ + {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, + {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, +] +markdown = [ + {file = "Markdown-3.2.2-py3-none-any.whl", hash = "sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"}, + {file = "Markdown-3.2.2.tar.gz", hash = "sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17"}, +] +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.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] +mkdocs = [ + {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, + {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, +] +nltk = [ + {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, +] +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.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, + {file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, + {file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"}, + {file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"}, + {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"}, + {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"}, + {file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"}, + {file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"}, + {file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"}, + {file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"}, + {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"}, + {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"}, + {file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"}, + {file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"}, + {file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, + {file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, + {file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, + {file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, + {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, + {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +tornado = [ + {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, + {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, + {file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"}, + {file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"}, + {file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"}, + {file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"}, + {file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"}, + {file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"}, + {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, +] +tqdm = [ + {file = "tqdm-4.48.0-py2.py3-none-any.whl", hash = "sha256:fcb7cb5b729b60a27f300b15c1ffd4744f080fb483b88f31dc8654b082cc8ea5"}, + {file = "tqdm-4.48.0.tar.gz", hash = "sha256:6baa75a88582b1db6d34ce4690da5501d2a1cb65c34664840a456b2c9f794d29"}, +] +zipp = [ + {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, + {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, +] diff --git a/pyproject.toml b/pyproject.toml index 9f1ed38..c381da8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ authors = ["Vikash Kothary "] python = "^3.7" [tool.poetry.dev-dependencies] +mkdocs = "^1.1.2" [build-system] requires = ["poetry>=0.12"] From afedcf719e3bf778c0b57de131129d78e67f5e36 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:15:49 +0100 Subject: [PATCH 11/75] Generate mkdocs markdown documentation --- docs/mkdocs.yml | 9 +++++++++ docs/src/index.md | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 docs/mkdocs.yml create mode 100644 docs/src/index.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 0000000..3737456 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,9 @@ +strict: true +theme: readthedocs +site_name: Anki Sync Server +site_description: Self-hosted Anki Sync Server. +site_author: Anki Community +site_url: https://ankicommunity.github.io/anki-sync-server +repo_url: https://github.com/ankicommunity/anki-sync-server +docs_dir: src +site_dir: build \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..03a9c49 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,19 @@ +# Welcome to MkDocs + +Welcome to the anki-sync-server wiki! + +For full documentation visit [mkdocs.org](https://www.mkdocs.org). + +## Commands + +* `mkdocs new [dir-name]` - Create a new project. +* `mkdocs serve` - Start the live-reloading docs server. +* `mkdocs build` - Build the documentation site. +* `mkdocs -h` - Print help message and exit. + +## Project layout + + mkdocs.yml # The configuration file. + src/ + index.md # The documentation homepage. + ... # Other markdown pages, images and other files. From b8c27ef1e6e5eba49b7ee8258701917b41830cef Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:21:33 +0100 Subject: [PATCH 12/75] Add docs command to build and serve documentation --- Makefile | 4 ++++ config/.env.example | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/Makefile b/Makefile index 0404dba..5f20ee0 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,10 @@ export help: @awk 'BEGIN {FS = " ?#?: "; print ""${ANKI_SERVER_NAME}" "${ANKI_SERVER_VERSION}"\n"${ANKI_SERVER_DESCRIPTION}"\n\nUsage: make \033[36m\033[0m\n\nCommands:"} /^.PHONY: ?[a-zA-Z_-]/ { printf " \033[36m%-10s\033[0m %s\n", $$2, $$3 }' $(MAKEFILE_LIST) +.PHONY: docs #: Build and serve documentation. +docs: print-env + @${MKDOCS} ${MKDOCS_OPTION} -f docs/mkdocs.yml + %: @test -f scripts/${*}.sh @${SHELL} scripts/${*}.sh \ No newline at end of file diff --git a/config/.env.example b/config/.env.example index e69de29..13e35b2 100644 --- a/config/.env.example +++ b/config/.env.example @@ -0,0 +1,10 @@ +# .env.example (anki-sync-server) + +## Make +MKDOCS=mkdocs + +## Mkdocs +MKDOCS_OPTION=serve + +## Path +PATH:=.venv/bin/path:${PATH} \ No newline at end of file From 241f5c0eef7aa798e9f818c398de32c20fb8275b Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 20:50:50 +0100 Subject: [PATCH 13/75] Add .readthedocs.yml to specify mkdocs.yml path --- .readthedocs.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..0b89db9 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,13 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +# Build documentation with MkDocs +mkdocs: + configuration: docs/mkdocs.yml + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3.7 \ No newline at end of file From 1331903c9f2a9e1247b2a7b0e18cbbefe2cfac1d Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 21:34:24 +0100 Subject: [PATCH 14/75] Add readthedocs markdown badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c60e8c6..491fabf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ankisyncd ========= +[![Documentation Status](https://readthedocs.org/projects/anki-sync-server/badge/?version=latest)](https://anki-sync-server.readthedocs.io/?badge=latest) + [Anki][] is a powerful open source flashcard application, which helps you quickly and easily memorize facts over the long term utilizing a spaced repetition algorithm. Anki's main form is a desktop application (for Windows, From a0eed872dee99ee987f644055e0b5146b7ac8abf Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 21:34:37 +0100 Subject: [PATCH 15/75] Add gitterchat markdown badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 491fabf..0714b57 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ankisyncd ========= [![Documentation Status](https://readthedocs.org/projects/anki-sync-server/badge/?version=latest)](https://anki-sync-server.readthedocs.io/?badge=latest) +[![Gitter](https://badges.gitter.im/ankicommunity/community.svg)](https://gitter.im/ankicommunity/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [Anki][] is a powerful open source flashcard application, which helps you quickly and easily memorize facts over the long term utilizing a spaced From b31784aa6be80998ea33f94e1e92f9612b3dc7ce Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 21:47:36 +0100 Subject: [PATCH 16/75] Add jupyter lab and notebook as a dependency --- Makefile | 4 + config/.env.example | 4 + poetry.lock | 950 +++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + 4 files changed, 959 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5f20ee0..40533d6 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,10 @@ help: docs: print-env @${MKDOCS} ${MKDOCS_OPTION} -f docs/mkdocs.yml +.PHONY: notebooks #: Run jupyter notebooks. +notebooks: + @${JUPYTER} ${JUPYTER_OPTION} + %: @test -f scripts/${*}.sh @${SHELL} scripts/${*}.sh \ No newline at end of file diff --git a/config/.env.example b/config/.env.example index 13e35b2..ce327ab 100644 --- a/config/.env.example +++ b/config/.env.example @@ -2,9 +2,13 @@ ## Make MKDOCS=mkdocs +JUPYTER=jupyter ## Mkdocs MKDOCS_OPTION=serve +## Jupyter +JUPYTER_OPTION=lab + ## Path PATH:=.venv/bin/path:${PATH} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 836e1c1..143d752 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,63 @@ +[[package]] +category = "dev" +description = "Disable App Nap on OS X 10.9" +marker = "sys_platform == \"darwin\" or platform_system == \"Darwin\" or python_version >= \"3.3\" and sys_platform == \"darwin\"" +name = "appnope" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "dev" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "19.3.0" + +[package.extras] +azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] +dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] +docs = ["sphinx", "zope.interface"] +tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] + +[[package]] +category = "dev" +description = "Specifications for callback functions passed in to an API" +name = "backcall" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "dev" +description = "An easy safelist-based HTML-sanitizing tool." +name = "bleach" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "3.1.5" + +[package.dependencies] +packaging = "*" +six = ">=1.9.0" +webencodings = "*" + +[[package]] +category = "dev" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2020.6.20" + +[[package]] +category = "dev" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + [[package]] category = "dev" description = "Composable command line interface toolkit" @@ -6,6 +66,39 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "7.1.2" +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "python_version >= \"3.3\" and sys_platform == \"win32\" or 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 = "dev" +description = "Decorators for Humans" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.4.2" + +[[package]] +category = "dev" +description = "XML bomb protection for Python stdlib modules" +name = "defusedxml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.6.0" + +[[package]] +category = "dev" +description = "Discover and load entry points from installed packages." +name = "entrypoints" +optional = false +python-versions = ">=2.7" +version = "0.3" + [[package]] category = "dev" description = "Clean single-source support for Python 3 and 2" @@ -14,6 +107,14 @@ optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" version = "0.18.2" +[[package]] +category = "dev" +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" @@ -30,6 +131,100 @@ zipp = ">=0.5" docs = ["sphinx", "rst.linker"] testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] +[[package]] +category = "dev" +description = "IPython Kernel for Jupyter" +name = "ipykernel" +optional = false +python-versions = ">=3.5" +version = "5.3.4" + +[package.dependencies] +appnope = "*" +ipython = ">=5.0.0" +jupyter-client = "*" +tornado = ">=4.2" +traitlets = ">=4.1.0" + +[package.extras] +test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose"] + +[[package]] +category = "dev" +description = "IPython: Productive Interactive Computing" +name = "ipython" +optional = false +python-versions = ">=3.6" +version = "7.16.1" + +[package.dependencies] +appnope = "*" +backcall = "*" +colorama = "*" +decorator = "*" +jedi = ">=0.10" +pexpect = "*" +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 = "dev" +description = "Vestigial utilities from IPython" +name = "ipython-genutils" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "dev" +description = "IPython HTML widgets for Jupyter" +name = "ipywidgets" +optional = false +python-versions = "*" +version = "7.5.1" + +[package.dependencies] +ipykernel = ">=4.5.1" +nbformat = ">=4.2.0" +traitlets = ">=4.3.1" +widgetsnbextension = ">=3.5.0,<3.6.0" + +[package.dependencies.ipython] +python = ">=3.3" +version = ">=4.0.0" + +[package.extras] +test = ["pytest (>=3.6.0)", "pytest-cov", "mock"] + +[[package]] +category = "dev" +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 = "dev" description = "A very fast and expressive template engine." @@ -53,6 +248,139 @@ optional = false python-versions = ">=3.6" version = "0.16.0" +[[package]] +category = "dev" +description = "A Python implementation of the JSON5 data format." +name = "json5" +optional = false +python-versions = "*" +version = "0.9.5" + +[package.extras] +dev = ["hypothesis"] + +[[package]] +category = "dev" +description = "An implementation of JSON Schema validation for Python" +name = "jsonschema" +optional = false +python-versions = "*" +version = "3.2.0" + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0" +setuptools = "*" +six = ">=1.11.0" + +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" + +[package.extras] +format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] +format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] + +[[package]] +category = "dev" +description = "Jupyter metapackage. Install all the Jupyter components in one go." +name = "jupyter" +optional = false +python-versions = "*" +version = "1.0.0" + +[package.dependencies] +ipykernel = "*" +ipywidgets = "*" +jupyter-console = "*" +nbconvert = "*" +notebook = "*" +qtconsole = "*" + +[[package]] +category = "dev" +description = "Jupyter protocol implementation and client libraries" +name = "jupyter-client" +optional = false +python-versions = ">=3.5" +version = "6.1.6" + +[package.dependencies] +jupyter-core = ">=4.6.0" +python-dateutil = ">=2.1" +pyzmq = ">=13" +tornado = ">=4.1" +traitlets = "*" + +[package.extras] +test = ["async-generator", "ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "pytest-timeout"] + +[[package]] +category = "dev" +description = "Jupyter terminal console" +name = "jupyter-console" +optional = false +python-versions = ">=3.5" +version = "6.1.0" + +[package.dependencies] +ipykernel = "*" +ipython = "*" +jupyter-client = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" + +[package.extras] +test = ["pexpect"] + +[[package]] +category = "dev" +description = "Jupyter core package. A base package on which Jupyter projects rely." +name = "jupyter-core" +optional = false +python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" +version = "4.6.3" + +[package.dependencies] +pywin32 = ">=1.0" +traitlets = "*" + +[[package]] +category = "dev" +description = "The JupyterLab notebook server extension." +name = "jupyterlab" +optional = false +python-versions = ">=3.5" +version = "2.2.2" + +[package.dependencies] +jinja2 = ">=2.10" +jupyterlab-server = ">=1.1.5,<2.0" +notebook = ">=4.3.1" +tornado = "<6.0.0 || >6.0.0,<6.0.1 || >6.0.1,<6.0.2 || >6.0.2" + +[package.extras] +docs = ["jsx-lexer", "recommonmark", "sphinx", "sphinx-rtd-theme", "sphinx-copybutton"] +test = ["pytest", "pytest-check-links", "requests", "wheel", "virtualenv"] + +[[package]] +category = "dev" +description = "JupyterLab Server" +name = "jupyterlab-server" +optional = false +python-versions = ">=3.5" +version = "1.2.0" + +[package.dependencies] +jinja2 = ">=2.10" +json5 = "*" +jsonschema = ">=3.0.1" +notebook = ">=4.2.0" +requests = "*" + +[package.extras] +test = ["pytest", "requests"] + [[package]] category = "dev" description = "Python LiveReload is an awesome tool for web developers" @@ -112,6 +440,14 @@ optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" version = "1.1.1" +[[package]] +category = "dev" +description = "The fastest markdown parser in pure Python" +name = "mistune" +optional = false +python-versions = "*" +version = "0.8.4" + [[package]] category = "dev" description = "Project documentation with Markdown." @@ -132,6 +468,51 @@ tornado = ">=5.0" extras = ["languages"] version = "0.5.8" +[[package]] +category = "dev" +description = "Converting Jupyter Notebooks" +name = "nbconvert" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "5.6.1" + +[package.dependencies] +bleach = "*" +defusedxml = "*" +entrypoints = ">=0.2.2" +jinja2 = ">=2.4" +jupyter-core = "*" +mistune = ">=0.8.1,<2" +nbformat = ">=4.4" +pandocfilters = ">=1.4.1" +pygments = "*" +testpath = "*" +traitlets = ">=4.2" + +[package.extras] +all = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "mock"] +docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "jupyter-client (>=5.3.1)"] +execute = ["jupyter-client (>=5.3.1)"] +serve = ["tornado (>=4.0)"] +test = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "mock"] + +[[package]] +category = "dev" +description = "The Jupyter Notebook format" +name = "nbformat" +optional = false +python-versions = ">=3.5" +version = "5.0.7" + +[package.dependencies] +ipython-genutils = "*" +jsonschema = ">=2.4,<2.5.0 || >2.5.0" +jupyter-core = "*" +traitlets = ">=4.1" + +[package.extras] +test = ["pytest", "pytest-cov", "testpath"] + [[package]] category = "dev" description = "Natural Language Toolkit" @@ -155,6 +536,170 @@ plot = ["matplotlib"] tgrep = ["pyparsing"] twitter = ["twython"] +[[package]] +category = "dev" +description = "A web-based notebook environment for interactive computing" +name = "notebook" +optional = false +python-versions = ">=3.5" +version = "6.0.3" + +[package.dependencies] +Send2Trash = "*" +ipykernel = "*" +ipython-genutils = "*" +jinja2 = "*" +jupyter-client = ">=5.3.4" +jupyter-core = ">=4.6.1" +nbconvert = "*" +nbformat = "*" +prometheus-client = "*" +pyzmq = ">=17" +terminado = ">=0.8.1" +tornado = ">=5.0" +traitlets = ">=4.2.1" + +[package.extras] +test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "nose-exclude"] + +[[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 = "dev" +description = "Utilities for writing pandoc filters in python" +name = "pandocfilters" +optional = false +python-versions = "*" +version = "1.4.2" + +[[package]] +category = "dev" +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 = "Pexpect allows easy control of interactive console applications." +marker = "python_version >= \"3.3\" and sys_platform != \"win32\" or sys_platform != \"win32\"" +name = "pexpect" +optional = false +python-versions = "*" +version = "4.8.0" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +category = "dev" +description = "Tiny 'shelve'-like database with concurrency support" +name = "pickleshare" +optional = false +python-versions = "*" +version = "0.7.5" + +[[package]] +category = "dev" +description = "Python client for the Prometheus monitoring system." +name = "prometheus-client" +optional = false +python-versions = "*" +version = "0.8.0" + +[package.extras] +twisted = ["twisted"] + +[[package]] +category = "dev" +description = "Library for building powerful interactive command lines in Python" +name = "prompt-toolkit" +optional = false +python-versions = ">=3.6.1" +version = "3.0.5" + +[package.dependencies] +wcwidth = "*" + +[[package]] +category = "dev" +description = "Run a subprocess in a pseudo terminal" +marker = "sys_platform != \"win32\" or os_name != \"nt\" or python_version >= \"3.3\" and sys_platform != \"win32\"" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + +[[package]] +category = "dev" +description = "Pygments is a syntax highlighting package written in Python." +name = "pygments" +optional = false +python-versions = ">=3.5" +version = "2.6.1" + +[[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 = "dev" +description = "Persistent/Functional/Immutable data structures" +name = "pyrsistent" +optional = false +python-versions = "*" +version = "0.16.0" + +[package.dependencies] +six = "*" + +[[package]] +category = "dev" +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 = "dev" +description = "Python for Window Extensions" +marker = "sys_platform == \"win32\"" +name = "pywin32" +optional = false +python-versions = "*" +version = "228" + +[[package]] +category = "dev" +description = "Python bindings for the winpty library" +marker = "os_name == \"nt\"" +name = "pywinpty" +optional = false +python-versions = "*" +version = "0.5.7" + [[package]] category = "dev" description = "YAML parser and emitter for Python" @@ -163,6 +708,44 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "5.3.1" +[[package]] +category = "dev" +description = "Python bindings for 0MQ" +name = "pyzmq" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" +version = "19.0.1" + +[[package]] +category = "dev" +description = "Jupyter Qt console" +name = "qtconsole" +optional = false +python-versions = "*" +version = "4.7.5" + +[package.dependencies] +ipykernel = ">=4.1" +ipython-genutils = "*" +jupyter-client = ">=4.1" +jupyter-core = "*" +pygments = "*" +pyzmq = ">=17.1" +qtpy = "*" +traitlets = "*" + +[package.extras] +doc = ["Sphinx (>=1.3)"] +test = ["pytest", "mock"] + +[[package]] +category = "dev" +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5, PyQt4 and PySide) and additional custom QWidgets." +name = "qtpy" +optional = false +python-versions = "*" +version = "1.9.0" + [[package]] category = "dev" description = "Alternative regular expression module, to replace re." @@ -172,6 +755,32 @@ optional = false python-versions = "*" version = "2020.7.14" +[[package]] +category = "dev" +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 = "dev" +description = "Send file to trash natively under Mac OS X, Windows and Linux." +name = "send2trash" +optional = false +python-versions = "*" +version = "1.5.0" + [[package]] category = "dev" description = "Python 2 and 3 compatibility utilities" @@ -180,6 +789,30 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" version = "1.15.0" +[[package]] +category = "dev" +description = "Terminals served to xterm.js using Tornado websockets" +name = "terminado" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.8.3" + +[package.dependencies] +ptyprocess = "*" +pywinpty = ">=0.5" +tornado = ">=4" + +[[package]] +category = "dev" +description = "Test utilities for code working with files and commands" +name = "testpath" +optional = false +python-versions = "*" +version = "0.4.4" + +[package.extras] +test = ["pathlib2"] + [[package]] category = "dev" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." @@ -200,6 +833,62 @@ version = "4.48.0" [package.extras] dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] +[[package]] +category = "dev" +description = "Traitlets Python config system" +name = "traitlets" +optional = false +python-versions = "*" +version = "4.3.3" + +[package.dependencies] +decorator = "*" +ipython-genutils = "*" +six = "*" + +[package.extras] +test = ["pytest", "mock"] + +[[package]] +category = "dev" +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 = "Measures the displayed width of unicode strings in a terminal" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.2.5" + +[[package]] +category = "dev" +description = "Character encoding aliases for legacy web content" +name = "webencodings" +optional = false +python-versions = "*" +version = "0.5.1" + +[[package]] +category = "dev" +description = "IPython HTML widgets for Jupyter" +name = "widgetsnbextension" +optional = false +python-versions = "*" +version = "3.5.1" + +[package.dependencies] +notebook = ">=4.4.1" + [[package]] category = "dev" description = "Backport of pathlib-compatible object wrapper for zip files" @@ -214,21 +903,85 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "9a1964dfceeac6efd12397455372dcedea30aaa1a5d417e3eed4eff99258eb2d" +content-hash = "2d0e4f9aea76962d904d1650f74639f0707c8c3e09fb2eba59e9ae20633e7f11" python-versions = "^3.7" [metadata.files] +appnope = [ + {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, + {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, +] +attrs = [ + {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, + {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, +] +backcall = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] +bleach = [ + {file = "bleach-3.1.5-py2.py3-none-any.whl", hash = "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f"}, + {file = "bleach-3.1.5.tar.gz", hash = "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"}, +] +certifi = [ + {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, + {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, +] +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"}, +] +decorator = [ + {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, + {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, +] +defusedxml = [ + {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, + {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, +] +entrypoints = [ + {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, + {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, +] future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] +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"}, ] +ipykernel = [ + {file = "ipykernel-5.3.4-py3-none-any.whl", hash = "sha256:d6fbba26dba3cebd411382bc484f7bc2caa98427ae0ddb4ab37fe8bfeb5c7dd3"}, + {file = "ipykernel-5.3.4.tar.gz", hash = "sha256:9b2652af1607986a1b231c62302d070bc0534f564c393a5d9d130db9abbbe89d"}, +] +ipython = [ + {file = "ipython-7.16.1-py3-none-any.whl", hash = "sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64"}, + {file = "ipython-7.16.1.tar.gz", hash = "sha256:9f4fcb31d3b2c533333893b9172264e4821c1ac91839500f31bd43f2c59b3ccf"}, +] +ipython-genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] +ipywidgets = [ + {file = "ipywidgets-7.5.1-py2.py3-none-any.whl", hash = "sha256:13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516"}, + {file = "ipywidgets-7.5.1.tar.gz", hash = "sha256:e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"}, +] +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"}, @@ -237,6 +990,39 @@ joblib = [ {file = "joblib-0.16.0-py3-none-any.whl", hash = "sha256:d348c5d4ae31496b2aa060d6d9b787864dd204f9480baaa52d18850cb43e9f49"}, {file = "joblib-0.16.0.tar.gz", hash = "sha256:8f52bf24c64b608bf0b2563e0e47d6fcf516abc8cfafe10cfd98ad66d94f92d6"}, ] +json5 = [ + {file = "json5-0.9.5-py2.py3-none-any.whl", hash = "sha256:af1a1b9a2850c7f62c23fde18be4749b3599fd302f494eebf957e2ada6b9e42c"}, + {file = "json5-0.9.5.tar.gz", hash = "sha256:703cfee540790576b56a92e1c6aaa6c4b0d98971dc358ead83812aa4d06bdb96"}, +] +jsonschema = [ + {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, + {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, +] +jupyter = [ + {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"}, + {file = "jupyter-1.0.0.tar.gz", hash = "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"}, + {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"}, +] +jupyter-client = [ + {file = "jupyter_client-6.1.6-py3-none-any.whl", hash = "sha256:7ad9aa91505786420d77edc5f9fb170d51050c007338ba8d196f603223fd3b3a"}, + {file = "jupyter_client-6.1.6.tar.gz", hash = "sha256:b360f8d4638bc577a4656e93f86298db755f915098dc763f6fc05da0c5d7a595"}, +] +jupyter-console = [ + {file = "jupyter_console-6.1.0-py2.py3-none-any.whl", hash = "sha256:b392155112ec86a329df03b225749a0fa903aa80811e8eda55796a40b5e470d8"}, + {file = "jupyter_console-6.1.0.tar.gz", hash = "sha256:6f6ead433b0534909df789ea64f0a14cdf9b6b2360757756f08182be4b9e431b"}, +] +jupyter-core = [ + {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, + {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, +] +jupyterlab = [ + {file = "jupyterlab-2.2.2-py3-none-any.whl", hash = "sha256:d0d743ea75b8eee20a18b96ccef24f76ee009bafb2617f3f330698fe3a00026e"}, + {file = "jupyterlab-2.2.2.tar.gz", hash = "sha256:8aa9bc4b5020e7b9ec6e006d516d48bddf7d2528680af65840464ee722d59db3"}, +] +jupyterlab-server = [ + {file = "jupyterlab_server-1.2.0-py3-none-any.whl", hash = "sha256:55d256077bf13e5bc9e8fbd5aac51bef82f6315111cec6b712b9a5ededbba924"}, + {file = "jupyterlab_server-1.2.0.tar.gz", hash = "sha256:5431d9dde96659364b7cc877693d5d21e7b80cea7ae3959ecc2b87518e5f5d8c"}, +] livereload = [ {file = "livereload-2.6.2.tar.gz", hash = "sha256:d1eddcb5c5eb8d2ca1fa1f750e580da624c0f7fcb734aa5780dc81b7dcbd89be"}, ] @@ -278,13 +1064,101 @@ markupsafe = [ {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] mkdocs = [ {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] +nbconvert = [ + {file = "nbconvert-5.6.1-py2.py3-none-any.whl", hash = "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee"}, + {file = "nbconvert-5.6.1.tar.gz", hash = "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523"}, +] +nbformat = [ + {file = "nbformat-5.0.7-py3-none-any.whl", hash = "sha256:ea55c9b817855e2dfcd3f66d74857342612a60b1f09653440f4a5845e6e3523f"}, + {file = "nbformat-5.0.7.tar.gz", hash = "sha256:54d4d6354835a936bad7e8182dcd003ca3dc0cedfee5a306090e04854343b340"}, +] nltk = [ {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, ] +notebook = [ + {file = "notebook-6.0.3-py3-none-any.whl", hash = "sha256:3edc616c684214292994a3af05eaea4cc043f6b4247d830f3a2f209fa7639a80"}, + {file = "notebook-6.0.3.tar.gz", hash = "sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48"}, +] +packaging = [ + {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, + {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, +] +pandocfilters = [ + {file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"}, +] +parso = [ + {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, + {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, +] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] +prometheus-client = [ + {file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"}, + {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.5-py3-none-any.whl", hash = "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04"}, + {file = "prompt_toolkit-3.0.5.tar.gz", hash = "sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8"}, +] +ptyprocess = [ + {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, + {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, +] +pygments = [ + {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, + {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pyrsistent = [ + {file = "pyrsistent-0.16.0.tar.gz", hash = "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"}, +] +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"}, +] +pywin32 = [ + {file = "pywin32-228-cp27-cp27m-win32.whl", hash = "sha256:37dc9935f6a383cc744315ae0c2882ba1768d9b06700a70f35dc1ce73cd4ba9c"}, + {file = "pywin32-228-cp27-cp27m-win_amd64.whl", hash = "sha256:11cb6610efc2f078c9e6d8f5d0f957620c333f4b23466931a247fb945ed35e89"}, + {file = "pywin32-228-cp35-cp35m-win32.whl", hash = "sha256:1f45db18af5d36195447b2cffacd182fe2d296849ba0aecdab24d3852fbf3f80"}, + {file = "pywin32-228-cp35-cp35m-win_amd64.whl", hash = "sha256:6e38c44097a834a4707c1b63efa9c2435f5a42afabff634a17f563bc478dfcc8"}, + {file = "pywin32-228-cp36-cp36m-win32.whl", hash = "sha256:ec16d44b49b5f34e99eb97cf270806fdc560dff6f84d281eb2fcb89a014a56a9"}, + {file = "pywin32-228-cp36-cp36m-win_amd64.whl", hash = "sha256:a60d795c6590a5b6baeacd16c583d91cce8038f959bd80c53bd9a68f40130f2d"}, + {file = "pywin32-228-cp37-cp37m-win32.whl", hash = "sha256:af40887b6fc200eafe4d7742c48417529a8702dcc1a60bf89eee152d1d11209f"}, + {file = "pywin32-228-cp37-cp37m-win_amd64.whl", hash = "sha256:00eaf43dbd05ba6a9b0080c77e161e0b7a601f9a3f660727a952e40140537de7"}, + {file = "pywin32-228-cp38-cp38-win32.whl", hash = "sha256:fa6ba028909cfc64ce9e24bcf22f588b14871980d9787f1e2002c99af8f1850c"}, + {file = "pywin32-228-cp38-cp38-win_amd64.whl", hash = "sha256:9b3466083f8271e1a5eb0329f4e0d61925d46b40b195a33413e0905dccb285e8"}, + {file = "pywin32-228-cp39-cp39-win32.whl", hash = "sha256:ed74b72d8059a6606f64842e7917aeee99159ebd6b8d6261c518d002837be298"}, + {file = "pywin32-228-cp39-cp39-win_amd64.whl", hash = "sha256:8319bafdcd90b7202c50d6014efdfe4fde9311b3ff15fd6f893a45c0868de203"}, +] +pywinpty = [ + {file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"}, + {file = "pywinpty-0.5.7-cp27-cp27m-win_amd64.whl", hash = "sha256:1e525a4de05e72016a7af27836d512db67d06a015aeaf2fa0180f8e6a039b3c2"}, + {file = "pywinpty-0.5.7-cp35-cp35m-win32.whl", hash = "sha256:2740eeeb59297593a0d3f762269b01d0285c1b829d6827445fcd348fb47f7e70"}, + {file = "pywinpty-0.5.7-cp35-cp35m-win_amd64.whl", hash = "sha256:33df97f79843b2b8b8bc5c7aaf54adec08cc1bae94ee99dfb1a93c7a67704d95"}, + {file = "pywinpty-0.5.7-cp36-cp36m-win32.whl", hash = "sha256:e854211df55d107f0edfda8a80b39dfc87015bef52a8fe6594eb379240d81df2"}, + {file = "pywinpty-0.5.7-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd838de92de1d4ebf0dce9d4d5e4fc38d0b7b1de837947a18b57a882f219139"}, + {file = "pywinpty-0.5.7-cp37-cp37m-win32.whl", hash = "sha256:5fb2c6c6819491b216f78acc2c521b9df21e0f53b9a399d58a5c151a3c4e2a2d"}, + {file = "pywinpty-0.5.7-cp37-cp37m-win_amd64.whl", hash = "sha256:dd22c8efacf600730abe4a46c1388355ce0d4ab75dc79b15d23a7bd87bf05b48"}, + {file = "pywinpty-0.5.7-cp38-cp38-win_amd64.whl", hash = "sha256:8fc5019ff3efb4f13708bd3b5ad327589c1a554cb516d792527361525a7cb78c"}, + {file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"}, +] 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"}, @@ -298,6 +1172,44 @@ pyyaml = [ {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] +pyzmq = [ + {file = "pyzmq-19.0.1-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:58688a2dfa044fad608a8e70ba8d019d0b872ec2acd75b7b5e37da8905605891"}, + {file = "pyzmq-19.0.1-cp27-cp27m-win32.whl", hash = "sha256:87c78f6936e2654397ca2979c1d323ee4a889eef536cc77a938c6b5be33351a7"}, + {file = "pyzmq-19.0.1-cp27-cp27m-win_amd64.whl", hash = "sha256:97b6255ae77328d0e80593681826a0479cb7bac0ba8251b4dd882f5145a2293a"}, + {file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:15b4cb21118f4589c4db8be4ac12b21c8b4d0d42b3ee435d47f686c32fe2e91f"}, + {file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:931339ac2000d12fe212e64f98ce291e81a7ec6c73b125f17cf08415b753c087"}, + {file = "pyzmq-19.0.1-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:2a88b8fabd9cc35bd59194a7723f3122166811ece8b74018147a4ed8489e6421"}, + {file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:bafd651b557dd81d89bd5f9c678872f3e7b7255c1c751b78d520df2caac80230"}, + {file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8952f6ba6ae598e792703f3134af5a01af8f5c7cf07e9a148f05a12b02412cea"}, + {file = "pyzmq-19.0.1-cp35-cp35m-win32.whl", hash = "sha256:54aa24fd60c4262286fc64ca632f9e747c7cc3a3a1144827490e1dc9b8a3a960"}, + {file = "pyzmq-19.0.1-cp35-cp35m-win_amd64.whl", hash = "sha256:dcbc3f30c11c60d709c30a213dc56e88ac016fe76ac6768e64717bd976072566"}, + {file = "pyzmq-19.0.1-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:6ca519309703e95d55965735a667809bbb65f52beda2fdb6312385d3e7a6d234"}, + {file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4ee0bfd82077a3ff11c985369529b12853a4064320523f8e5079b630f9551448"}, + {file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ba6f24431b569aec674ede49cad197cad59571c12deed6ad8e3c596da8288217"}, + {file = "pyzmq-19.0.1-cp36-cp36m-win32.whl", hash = "sha256:956775444d01331c7eb412c5fb9bb62130dfaac77e09f32764ea1865234e2ca9"}, + {file = "pyzmq-19.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b08780e3a55215873b3b8e6e7ca8987f14c902a24b6ac081b344fd430d6ca7cd"}, + {file = "pyzmq-19.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21f7d91f3536f480cb2c10d0756bfa717927090b7fb863e6323f766e5461ee1c"}, + {file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bfff5ffff051f5aa47ba3b379d87bd051c3196b0c8a603e8b7ed68a6b4f217ec"}, + {file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:07fb8fe6826a229dada876956590135871de60dbc7de5a18c3bcce2ed1f03c98"}, + {file = "pyzmq-19.0.1-cp37-cp37m-win32.whl", hash = "sha256:342fb8a1dddc569bc361387782e8088071593e7eaf3e3ecf7d6bd4976edff112"}, + {file = "pyzmq-19.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:faee2604f279d31312bc455f3d024f160b6168b9c1dde22bf62d8c88a4deca8e"}, + {file = "pyzmq-19.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b9d21fc56c8aacd2e6d14738021a9d64f3f69b30578a99325a728e38a349f85"}, + {file = "pyzmq-19.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af0c02cf49f4f9eedf38edb4f3b6bb621d83026e7e5d76eb5526cc5333782fd6"}, + {file = "pyzmq-19.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5f1f2eb22aab606f808163eb1d537ac9a0ba4283fbeb7a62eb48d9103cf015c2"}, + {file = "pyzmq-19.0.1-cp38-cp38-win32.whl", hash = "sha256:f9d7e742fb0196992477415bb34366c12e9bb9a0699b8b3f221ff93b213d7bec"}, + {file = "pyzmq-19.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:5b99c2ae8089ef50223c28bac57510c163bfdff158c9e90764f812b94e69a0e6"}, + {file = "pyzmq-19.0.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:cf5d689ba9513b9753959164cf500079383bc18859f58bf8ce06d8d4bef2b054"}, + {file = "pyzmq-19.0.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:aaa8b40b676576fd7806839a5de8e6d5d1b74981e6376d862af6c117af2a3c10"}, + {file = "pyzmq-19.0.1.tar.gz", hash = "sha256:13a5638ab24d628a6ade8f794195e1a1acd573496c3b85af2f1183603b7bf5e0"}, +] +qtconsole = [ + {file = "qtconsole-4.7.5-py2.py3-none-any.whl", hash = "sha256:4f43d0b049eacb7d723772847f0c465feccce0ccb398871a6e146001a22bad23"}, + {file = "qtconsole-4.7.5.tar.gz", hash = "sha256:f5cb275d30fc8085e2d1d18bc363e5ba0ce6e559bf37d7d6727b773134298754"}, +] +qtpy = [ + {file = "QtPy-1.9.0-py2.py3-none-any.whl", hash = "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea"}, + {file = "QtPy-1.9.0.tar.gz", hash = "sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d"}, +] regex = [ {file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, {file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, @@ -320,10 +1232,26 @@ regex = [ {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, ] +requests = [ + {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, + {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, +] +send2trash = [ + {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"}, + {file = "Send2Trash-1.5.0.tar.gz", hash = "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2"}, +] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] +terminado = [ + {file = "terminado-0.8.3-py2.py3-none-any.whl", hash = "sha256:a43dcb3e353bc680dd0783b1d9c3fc28d529f190bc54ba9a229f72fe6e7a54d7"}, + {file = "terminado-0.8.3.tar.gz", hash = "sha256:4804a774f802306a7d9af7322193c5390f1da0abb429e082a10ef1d46e6fb2c2"}, +] +testpath = [ + {file = "testpath-0.4.4-py2.py3-none-any.whl", hash = "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4"}, + {file = "testpath-0.4.4.tar.gz", hash = "sha256:60e0a3261c149755f4399a1fff7d37523179a70fdc3abdf78de9fc2604aeec7e"}, +] tornado = [ {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, @@ -339,6 +1267,26 @@ tqdm = [ {file = "tqdm-4.48.0-py2.py3-none-any.whl", hash = "sha256:fcb7cb5b729b60a27f300b15c1ffd4744f080fb483b88f31dc8654b082cc8ea5"}, {file = "tqdm-4.48.0.tar.gz", hash = "sha256:6baa75a88582b1db6d34ce4690da5501d2a1cb65c34664840a456b2c9f794d29"}, ] +traitlets = [ + {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, + {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, +] +urllib3 = [ + {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, + {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +widgetsnbextension = [ + {file = "widgetsnbextension-3.5.1-py2.py3-none-any.whl", hash = "sha256:bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd"}, + {file = "widgetsnbextension-3.5.1.tar.gz", hash = "sha256:079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7"}, +] zipp = [ {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, diff --git a/pyproject.toml b/pyproject.toml index c381da8..167ca5d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,8 @@ python = "^3.7" [tool.poetry.dev-dependencies] mkdocs = "^1.1.2" +jupyter = "^1.0.0" +jupyterlab = "^2.2.2" [build-system] requires = ["poetry>=0.12"] From 7df362bb2c0d86a37ffefc037cfad458ce682cc1 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 21:49:34 +0100 Subject: [PATCH 17/75] Add script to lock and export requirements --- scripts/lock.sh | 9 +++++ src/requirements-dev.txt | 72 ++++++++++++++++++++++++++++++++++++++++ src/requirements.txt | 0 3 files changed, 81 insertions(+) create mode 100644 scripts/lock.sh create mode 100644 src/requirements-dev.txt create mode 100644 src/requirements.txt diff --git a/scripts/lock.sh b/scripts/lock.sh new file mode 100644 index 0000000..cf7dc6d --- /dev/null +++ b/scripts/lock.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# file: lock.sh +# description: Lock dependencies and export requirements. + +poetry lock +poetry export --without-hashes -f requirements.txt > src/requirements.txt +poetry export --dev --without-hashes -f requirements.txt > src/requirements-dev.txt + +echo "-e src/." >> src/requirements-dev.txt \ No newline at end of file diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt new file mode 100644 index 0000000..ec84d16 --- /dev/null +++ b/src/requirements-dev.txt @@ -0,0 +1,72 @@ +appnope==0.1.0; sys_platform == "darwin" or platform_system == "Darwin" or python_version >= "3.3" and sys_platform == "darwin" +attrs==19.3.0 +backcall==0.2.0 +bleach==3.1.5 +certifi==2020.6.20 +chardet==3.0.4 +click==7.1.2 +colorama==0.4.3; python_version >= "3.3" and sys_platform == "win32" or sys_platform == "win32" +decorator==4.4.2 +defusedxml==0.6.0 +entrypoints==0.3 +future==0.18.2 +idna==2.10 +importlib-metadata==1.7.0; python_version < "3.8" +ipykernel==5.3.4 +ipython==7.16.1 +ipython-genutils==0.2.0 +ipywidgets==7.5.1 +jedi==0.17.2 +jinja2==2.11.2 +joblib==0.16.0; python_version > "2.7" +json5==0.9.5 +jsonschema==3.2.0 +jupyter==1.0.0 +jupyter-client==6.1.6 +jupyter-console==6.1.0 +jupyter-core==4.6.3 +jupyterlab==2.2.2 +jupyterlab-server==1.2.0 +livereload==2.6.2 +lunr==0.5.8 +markdown==3.2.2 +markupsafe==1.1.1 +mistune==0.8.4 +mkdocs==1.1.2 +nbconvert==5.6.1 +nbformat==5.0.7 +nltk==3.5; python_version > "2.7" +notebook==6.0.3 +packaging==20.4 +pandocfilters==1.4.2 +parso==0.7.1 +pexpect==4.8.0; python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32" +pickleshare==0.7.5 +prometheus-client==0.8.0 +prompt-toolkit==3.0.5 +ptyprocess==0.6.0; sys_platform != "win32" or os_name != "nt" or python_version >= "3.3" and sys_platform != "win32" +pygments==2.6.1 +pyparsing==2.4.7 +pyrsistent==0.16.0 +python-dateutil==2.8.1 +pywin32==228; sys_platform == "win32" +pywinpty==0.5.7; os_name == "nt" +pyyaml==5.3.1 +pyzmq==19.0.1 +qtconsole==4.7.5 +qtpy==1.9.0 +regex==2020.7.14; python_version > "2.7" +requests==2.24.0 +send2trash==1.5.0 +six==1.15.0 +terminado==0.8.3 +testpath==0.4.4 +tornado==6.0.4 +tqdm==4.48.0; python_version > "2.7" +traitlets==4.3.3 +urllib3==1.25.10 +wcwidth==0.2.5 +webencodings==0.5.1 +widgetsnbextension==3.5.1 +zipp==3.1.0; python_version < "3.8" +-e src/. diff --git a/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000..e69de29 From 5430b729f964126c2b747c79e84d47900e4a5acd Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 22:17:56 +0100 Subject: [PATCH 18/75] Add anki-bundled requirements to pyproject --- poetry.lock | 109 +++++++++++++++++++++++++++++++++++---- pyproject.toml | 8 +++ src/requirements-dev.txt | 5 ++ src/requirements.txt | 15 ++++++ 4 files changed, 126 insertions(+), 11 deletions(-) diff --git a/poetry.lock b/poetry.lock index 143d752..162a636 100644 --- a/poetry.lock +++ b/poetry.lock @@ -29,6 +29,21 @@ optional = false python-versions = "*" version = "0.2.0" +[[package]] +category = "main" +description = "Screen-scraping library" +name = "beautifulsoup4" +optional = false +python-versions = "*" +version = "4.9.1" + +[package.dependencies] +soupsieve = [">1.2", "<2.0"] + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] category = "dev" description = "An easy safelist-based HTML-sanitizing tool." @@ -43,7 +58,7 @@ six = ">=1.9.0" webencodings = "*" [[package]] -category = "dev" +category = "main" description = "Python package for providing Mozilla's CA Bundle." name = "certifi" optional = false @@ -51,7 +66,7 @@ python-versions = "*" version = "2020.6.20" [[package]] -category = "dev" +category = "main" description = "Universal encoding detector for Python 2 and 3" name = "chardet" optional = false @@ -76,7 +91,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.4.3" [[package]] -category = "dev" +category = "main" description = "Decorators for Humans" name = "decorator" optional = false @@ -91,6 +106,14 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.6.0" +[[package]] +category = "main" +description = "Distro - an OS platform information API" +name = "distro" +optional = false +python-versions = "*" +version = "1.5.0" + [[package]] category = "dev" description = "Discover and load entry points from installed packages." @@ -108,7 +131,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" version = "0.18.2" [[package]] -category = "dev" +category = "main" description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" optional = false @@ -116,7 +139,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.10" [[package]] -category = "dev" +category = "main" description = "Read metadata from Python packages" marker = "python_version < \"3.8\"" name = "importlib-metadata" @@ -417,7 +440,7 @@ version = ">=3.2.5" languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] [[package]] -category = "dev" +category = "main" description = "Python implementation of Markdown." name = "markdown" optional = false @@ -635,6 +658,17 @@ version = "3.0.5" [package.dependencies] wcwidth = "*" +[[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 = "dev" description = "Run a subprocess in a pseudo terminal" @@ -644,6 +678,14 @@ optional = false python-versions = "*" version = "0.6.0" +[[package]] +category = "main" +description = "Bindings for PortAudio v19, the cross-platform audio input/output stream library." +name = "pyaudio" +optional = false +python-versions = "*" +version = "0.2.11" + [[package]] category = "dev" description = "Pygments is a syntax highlighting package written in Python." @@ -756,7 +798,7 @@ python-versions = "*" version = "2020.7.14" [[package]] -category = "dev" +category = "main" description = "Python HTTP for Humans." name = "requests" optional = false @@ -774,7 +816,7 @@ security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] -category = "dev" +category = "main" description = "Send file to trash natively under Mac OS X, Windows and Linux." name = "send2trash" optional = false @@ -789,6 +831,14 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" version = "1.15.0" +[[package]] +category = "main" +description = "A modern CSS selector implementation for Beautiful Soup." +name = "soupsieve" +optional = false +python-versions = "*" +version = "1.9.6" + [[package]] category = "dev" description = "Terminals served to xterm.js using Tornado websockets" @@ -850,7 +900,7 @@ six = "*" test = ["pytest", "mock"] [[package]] -category = "dev" +category = "main" description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" optional = false @@ -890,7 +940,7 @@ version = "3.5.1" notebook = ">=4.4.1" [[package]] -category = "dev" +category = "main" description = "Backport of pathlib-compatible object wrapper for zip files" marker = "python_version < \"3.8\"" name = "zipp" @@ -903,7 +953,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "2d0e4f9aea76962d904d1650f74639f0707c8c3e09fb2eba59e9ae20633e7f11" +content-hash = "d69805c25d0383c09b37a94367a0b11cf73b98b0966fd3253af17d3997896703" python-versions = "^3.7" [metadata.files] @@ -919,6 +969,11 @@ backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.9.1-py2-none-any.whl", hash = "sha256:e718f2342e2e099b640a34ab782407b7b676f47ee272d6739e60b8ea23829f2c"}, + {file = "beautifulsoup4-4.9.1-py3-none-any.whl", hash = "sha256:a6237df3c32ccfaee4fd201c8f5f9d9df619b93121d01353a64a73ce8c6ef9a8"}, + {file = "beautifulsoup4-4.9.1.tar.gz", hash = "sha256:73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7"}, +] bleach = [ {file = "bleach-3.1.5-py2.py3-none-any.whl", hash = "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f"}, {file = "bleach-3.1.5.tar.gz", hash = "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"}, @@ -947,6 +1002,10 @@ defusedxml = [ {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, ] +distro = [ + {file = "distro-1.5.0-py2.py3-none-any.whl", hash = "sha256:df74eed763e18d10d0da624258524ae80486432cd17392d9c3d96f5e83cd2799"}, + {file = "distro-1.5.0.tar.gz", hash = "sha256:0e58756ae38fbd8fc3020d54badb8eae17c5b9dcbed388b17bb55b8a5928df92"}, +] entrypoints = [ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, @@ -1114,10 +1173,34 @@ prompt-toolkit = [ {file = "prompt_toolkit-3.0.5-py3-none-any.whl", hash = "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04"}, {file = "prompt_toolkit-3.0.5.tar.gz", hash = "sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8"}, ] +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"}, +] ptyprocess = [ {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, ] +pyaudio = [ + {file = "PyAudio-0.2.11-cp27-cp27m-win32.whl", hash = "sha256:f78d543a98b730e64621ebf7f3e2868a79ade0a373882ef51c0293455ffa8e6e"}, + {file = "PyAudio-0.2.11-cp27-cp27m-win_amd64.whl", hash = "sha256:259bb9c1363be895b4f9a97e320a6017dd06bc540728c1a04eb4a7b6fe75035b"}, + {file = "PyAudio-0.2.11-cp34-cp34m-win32.whl", hash = "sha256:0d92f6a294565260a282f7c9a0b0d309fc8cc988b5ee5b50645634ab9e2da7f7"}, + {file = "PyAudio-0.2.11-cp34-cp34m-win_amd64.whl", hash = "sha256:589bfad2c615dd4b5d3757e763019c42ab82f06fba5cae64ec02fd7f5ae407ed"}, + {file = "PyAudio-0.2.11-cp35-cp35m-win32.whl", hash = "sha256:8f89075b4844ea94dde0c951c2937581c989fabd4df09bfd3f075035f50955df"}, + {file = "PyAudio-0.2.11-cp35-cp35m-win_amd64.whl", hash = "sha256:cf1543ba50bd44ac0d0ab5c035bb9c3127eb76047ff12235149d9adf86f532b6"}, + {file = "PyAudio-0.2.11-cp36-cp36m-win32.whl", hash = "sha256:51b558d1b28c68437b53218279110db44f69f3f5dd3d81859f569a4a96962bdc"}, + {file = "PyAudio-0.2.11-cp36-cp36m-win_amd64.whl", hash = "sha256:2a19bdb8ec1445b4f3e4b7b109e0e4cec1fd1f1ce588592aeb6db0b58d4fb3b0"}, + {file = "PyAudio-0.2.11.tar.gz", hash = "sha256:93bfde30e0b64e63a46f2fd77e85c41fd51182a4a3413d9edfaf9ffaa26efb74"}, +] pygments = [ {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, @@ -1244,6 +1327,10 @@ six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] +soupsieve = [ + {file = "soupsieve-1.9.6-py2.py3-none-any.whl", hash = "sha256:feb1e937fa26a69e08436aad4a9037cd7e1d4c7212909502ba30701247ff8abd"}, + {file = "soupsieve-1.9.6.tar.gz", hash = "sha256:7985bacc98c34923a439967c1a602dc4f1e15f923b6fcf02344184f86cc7efaa"}, +] terminado = [ {file = "terminado-0.8.3-py2.py3-none-any.whl", hash = "sha256:a43dcb3e353bc680dd0783b1d9c3fc28d529f190bc54ba9a229f72fe6e7a54d7"}, {file = "terminado-0.8.3.tar.gz", hash = "sha256:4804a774f802306a7d9af7322193c5390f1da0abb429e082a10ef1d46e6fb2c2"}, diff --git a/pyproject.toml b/pyproject.toml index 167ca5d..ac46806 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,14 @@ authors = ["Vikash Kothary "] [tool.poetry.dependencies] python = "^3.7" +beautifulsoup4 = "^4.9.1" +requests = "^2.24.0" +markdown = "^3.2.2" +send2trash = "^1.5.0" +pyaudio = "^0.2.11" +decorator = "^4.4.2" +psutil = "^5.7.2" +distro = "^1.5.0" [tool.poetry.dev-dependencies] mkdocs = "^1.1.2" diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index ec84d16..31b510d 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -1,6 +1,7 @@ appnope==0.1.0; sys_platform == "darwin" or platform_system == "Darwin" or python_version >= "3.3" and sys_platform == "darwin" attrs==19.3.0 backcall==0.2.0 +beautifulsoup4==4.9.1 bleach==3.1.5 certifi==2020.6.20 chardet==3.0.4 @@ -8,6 +9,7 @@ click==7.1.2 colorama==0.4.3; python_version >= "3.3" and sys_platform == "win32" or sys_platform == "win32" decorator==4.4.2 defusedxml==0.6.0 +distro==1.5.0 entrypoints==0.3 future==0.18.2 idna==2.10 @@ -44,7 +46,9 @@ pexpect==4.8.0; python_version >= "3.3" and sys_platform != "win32" or sys_platf pickleshare==0.7.5 prometheus-client==0.8.0 prompt-toolkit==3.0.5 +psutil==5.7.2 ptyprocess==0.6.0; sys_platform != "win32" or os_name != "nt" or python_version >= "3.3" and sys_platform != "win32" +pyaudio==0.2.11 pygments==2.6.1 pyparsing==2.4.7 pyrsistent==0.16.0 @@ -59,6 +63,7 @@ regex==2020.7.14; python_version > "2.7" requests==2.24.0 send2trash==1.5.0 six==1.15.0 +soupsieve==1.9.6 terminado==0.8.3 testpath==0.4.4 tornado==6.0.4 diff --git a/src/requirements.txt b/src/requirements.txt index e69de29..76746e8 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -0,0 +1,15 @@ +beautifulsoup4==4.9.1 +certifi==2020.6.20 +chardet==3.0.4 +decorator==4.4.2 +distro==1.5.0 +idna==2.10 +importlib-metadata==1.7.0; python_version < "3.8" +markdown==3.2.2 +psutil==5.7.2 +pyaudio==0.2.11 +requests==2.24.0 +send2trash==1.5.0 +soupsieve==1.9.6 +urllib3==1.25.10 +zipp==3.1.0; python_version < "3.8" From 58441cffbde3fb5c65be72298c20dfa38ea50d1c Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 22:18:52 +0100 Subject: [PATCH 19/75] Add webob as a dependency to pyproject --- poetry.lock | 18 +++++++++++++++++- pyproject.toml | 1 + src/requirements-dev.txt | 1 + src/requirements.txt | 1 + 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 162a636..5c9f4dc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -928,6 +928,18 @@ optional = false python-versions = "*" version = "0.5.1" +[[package]] +category = "main" +description = "WSGI request and response object" +name = "webob" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" +version = "1.8.6" + +[package.extras] +docs = ["Sphinx (>=1.7.5)", "pylons-sphinx-themes"] +testing = ["pytest (>=3.1.0)", "coverage", "pytest-cov", "pytest-xdist"] + [[package]] category = "dev" description = "IPython HTML widgets for Jupyter" @@ -953,7 +965,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "d69805c25d0383c09b37a94367a0b11cf73b98b0966fd3253af17d3997896703" +content-hash = "256b39b0726f0028059bd4d3a895cfe5a0676284c57a7615e6178734caa70227" python-versions = "^3.7" [metadata.files] @@ -1370,6 +1382,10 @@ webencodings = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] +webob = [ + {file = "WebOb-1.8.6-py2.py3-none-any.whl", hash = "sha256:a3c89a8e9ba0aeb17382836cdb73c516d0ecf6630ec40ec28288f3ed459ce87b"}, + {file = "WebOb-1.8.6.tar.gz", hash = "sha256:aa3a917ed752ba3e0b242234b2a373f9c4e2a75d35291dcbe977649bd21fd108"}, +] widgetsnbextension = [ {file = "widgetsnbextension-3.5.1-py2.py3-none-any.whl", hash = "sha256:bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd"}, {file = "widgetsnbextension-3.5.1.tar.gz", hash = "sha256:079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7"}, diff --git a/pyproject.toml b/pyproject.toml index ac46806..a5ecd48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ pyaudio = "^0.2.11" decorator = "^4.4.2" psutil = "^5.7.2" distro = "^1.5.0" +webob = "^1.8.6" [tool.poetry.dev-dependencies] mkdocs = "^1.1.2" diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 31b510d..44b3e2b 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -72,6 +72,7 @@ traitlets==4.3.3 urllib3==1.25.10 wcwidth==0.2.5 webencodings==0.5.1 +webob==1.8.6 widgetsnbextension==3.5.1 zipp==3.1.0; python_version < "3.8" -e src/. diff --git a/src/requirements.txt b/src/requirements.txt index 76746e8..f66853b 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -12,4 +12,5 @@ requests==2.24.0 send2trash==1.5.0 soupsieve==1.9.6 urllib3==1.25.10 +webob==1.8.6 zipp==3.1.0; python_version < "3.8" From 0c6a86d72b14453edc3bb78c5495404988986a75 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Thu, 30 Jul 2020 23:06:50 +0100 Subject: [PATCH 20/75] Add ankisyncd environment variables to .env.example --- config/.env.example | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/config/.env.example b/config/.env.example index ce327ab..65979ac 100644 --- a/config/.env.example +++ b/config/.env.example @@ -4,6 +4,20 @@ MKDOCS=mkdocs JUPYTER=jupyter +## Ankisyncd +ANKISYNCD_HOST=0.0.0.0 +ANKISYNCD_PORT=27701 +ANKISYNCD_DATA_ROOT=./collections +ANKISYNCD_BASE_URL=/sync/ +ANKISYNCD_BASE_MEDIA_URL=/msync/ +ANKISYNCD_AUTH_DB_PATH=./auth.db +ANKISYNCD_SESSION_DB_PATH=./session.db + +ANKISYNCD_FULL_SYNC_MANAGER +ANKISYNCD_SESSION_MANAGER +ANKISYNCD_USER_MANAGER +ANKISYNCD_COLLECTION_WRAPPER + ## Mkdocs MKDOCS_OPTION=serve From f7ae69f4e66602d9a2a06d684ca981a6f6799bf4 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 26 Aug 2020 16:22:47 +0200 Subject: [PATCH 21/75] Removed embedded anki submodule --- .gitmodules | 3 --- src/anki-bundled | 1 - src/ankisyncd/__init__.py | 3 --- 3 files changed, 7 deletions(-) delete mode 100644 .gitmodules delete mode 160000 src/anki-bundled diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9b79105..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "anki-bundled"] - path = src/anki-bundled - url = https://github.com/dae/anki.git diff --git a/src/anki-bundled b/src/anki-bundled deleted file mode 160000 index cca3fcb..0000000 --- a/src/anki-bundled +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cca3fcb2418880d0430a5c5c2e6b81ba260065b7 diff --git a/src/ankisyncd/__init__.py b/src/ankisyncd/__init__.py index 8ad83df..e691cc3 100644 --- a/src/ankisyncd/__init__.py +++ b/src/ankisyncd/__init__.py @@ -1,9 +1,6 @@ import os import sys -sys.path.insert(0, "/usr/share/anki") -sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(__file__)), "anki-bundled")) - _homepage = "https://github.com/tsudoko/anki-sync-server" _unknown_version = "[unknown version]" From 26d16b698a5e74d01f19167f17c18d0ee49c421e Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 26 Aug 2020 16:25:03 +0200 Subject: [PATCH 22/75] Removed unused import --- src/ankisyncd/collection.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ankisyncd/collection.py b/src/ankisyncd/collection.py index e32dbfe..5127f2c 100644 --- a/src/ankisyncd/collection.py +++ b/src/ankisyncd/collection.py @@ -1,4 +1,3 @@ -import anki import anki.storage import ankisyncd.media From d0f7d05b446789bcd11542d6472ae4f54fe23d3e Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 26 Aug 2020 16:44:05 +0200 Subject: [PATCH 23/75] Ported Python Syncer from Anki source code --- src/ankisyncd/sync.py | 869 ++++++++++++++++++++++++++++++++++++++ src/ankisyncd/sync_app.py | 10 +- 2 files changed, 874 insertions(+), 5 deletions(-) create mode 100644 src/ankisyncd/sync.py diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py new file mode 100644 index 0000000..2d46950 --- /dev/null +++ b/src/ankisyncd/sync.py @@ -0,0 +1,869 @@ +# -*- coding: utf-8 -*- +# Copyright: Ankitects Pty Ltd and contributors +# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html + +# Taken from https://github.com/ankitects/anki/blob/cca3fcb2418880d0430a5c5c2e6b81ba260065b7/anki/sync.py + +import io +import gzip +import random +import requests +import json +import os + +from anki.db import DB, DBError +from anki.utils import ids2str, intTime, platDesc, checksum, devMode +from anki.consts import * +from anki.utils import versionWithBuild +from anki.hooks import runHook +import anki +from anki.lang import ngettext + + +# https://github.com/ankitects/anki/blob/04b1ca75599f18eb783a8bf0bdeeeb32362f4da0/rslib/src/sync/http_client.rs#L11 +SYNC_VER = 10 +# https://github.com/ankitects/anki/blob/cca3fcb2418880d0430a5c5c2e6b81ba260065b7/anki/consts.py#L50 +SYNC_ZIP_SIZE = int(2.5*1024*1024) +# https://github.com/ankitects/anki/blob/cca3fcb2418880d0430a5c5c2e6b81ba260065b7/anki/consts.py#L51 +SYNC_ZIP_COUNT = 25 + +# syncing vars +HTTP_TIMEOUT = 90 +HTTP_PROXY = None +HTTP_BUF_SIZE = 64*1024 + +# Incremental syncing +########################################################################## + +class Syncer(object): + def __init__(self, col, server=None): + self.col = col + self.server = server + + def sync(self): + "Returns 'noChanges', 'fullSync', 'success', etc" + self.syncMsg = "" + self.uname = "" + # if the deck has any pending changes, flush them first and bump mod + # time + self.col.save() + + # step 1: login & metadata + runHook("sync", "login") + meta = self.server.meta() + self.col.log("rmeta", meta) + if not meta: + return "badAuth" + # server requested abort? + self.syncMsg = meta['msg'] + if not meta['cont']: + return "serverAbort" + else: + # don't abort, but if 'msg' is not blank, gui should show 'msg' + # after sync finishes and wait for confirmation before hiding + pass + rscm = meta['scm'] + rts = meta['ts'] + self.rmod = meta['mod'] + self.maxUsn = meta['usn'] + self.uname = meta.get("uname", "") + self.hostNum = meta.get("hostNum") + meta = self.meta() + self.col.log("lmeta", meta) + self.lmod = meta['mod'] + self.minUsn = meta['usn'] + lscm = meta['scm'] + lts = meta['ts'] + if abs(rts - lts) > 300: + self.col.log("clock off") + return "clockOff" + if self.lmod == self.rmod: + self.col.log("no changes") + return "noChanges" + elif lscm != rscm: + self.col.log("schema diff") + return "fullSync" + self.lnewer = self.lmod > self.rmod + # step 1.5: check collection is valid + if not self.col.basicCheck(): + self.col.log("basic check") + return "basicCheckFailed" + # step 2: startup and deletions + runHook("sync", "meta") + rrem = self.server.start(minUsn=self.minUsn, lnewer=self.lnewer) + + # apply deletions to server + lgraves = self.removed() + while lgraves: + gchunk, lgraves = self._gravesChunk(lgraves) + self.server.applyGraves(chunk=gchunk) + + # then apply server deletions here + self.remove(rrem) + + # ...and small objects + lchg = self.changes() + rchg = self.server.applyChanges(changes=lchg) + self.mergeChanges(lchg, rchg) + # step 3: stream large tables from server + runHook("sync", "server") + while 1: + runHook("sync", "stream") + chunk = self.server.chunk() + self.col.log("server chunk", chunk) + self.applyChunk(chunk=chunk) + if chunk['done']: + break + # step 4: stream to server + runHook("sync", "client") + while 1: + runHook("sync", "stream") + chunk = self.chunk() + self.col.log("client chunk", chunk) + self.server.applyChunk(chunk=chunk) + if chunk['done']: + break + # step 5: sanity check + runHook("sync", "sanity") + c = self.sanityCheck() + ret = self.server.sanityCheck2(client=c) + if ret['status'] != "ok": + # roll back and force full sync + self.col.rollback() + self.col.modSchema(False) + self.col.save() + return "sanityCheckFailed" + # finalize + runHook("sync", "finalize") + mod = self.server.finish() + self.finish(mod) + return "success" + + def _gravesChunk(self, graves): + lim = 250 + chunk = dict(notes=[], cards=[], decks=[]) + for cat in "notes", "cards", "decks": + if lim and graves[cat]: + chunk[cat] = graves[cat][:lim] + graves[cat] = graves[cat][lim:] + lim -= len(chunk[cat]) + + # anything remaining? + if graves['notes'] or graves['cards'] or graves['decks']: + return chunk, graves + return chunk, None + + def meta(self): + return dict( + mod=self.col.mod, + scm=self.col.scm, + usn=self.col._usn, + ts=intTime(), + musn=0, + msg="", + cont=True + ) + + def changes(self): + "Bundle up small objects." + d = dict(models=self.getModels(), + decks=self.getDecks(), + tags=self.getTags()) + if self.lnewer: + #d['conf'] = self.getConf() + d['crt'] = self.col.crt + return d + + def mergeChanges(self, lchg, rchg): + # then the other objects + self.mergeModels(rchg['models']) + self.mergeDecks(rchg['decks']) + self.mergeTags(rchg['tags']) + if 'conf' in rchg: + self.mergeConf(rchg['conf']) + # this was left out of earlier betas + if 'crt' in rchg: + self.col.crt = rchg['crt'] + self.prepareToChunk() + + def sanityCheck(self): + if not self.col.basicCheck(): + return "failed basic check" + for t in "cards", "notes", "revlog", "graves": + if self.col.db.scalar( + "select count() from %s where usn = -1" % t): + return "%s had usn = -1" % t + for g in self.col.decks.all(): + if g['usn'] == -1: + return "deck had usn = -1" + for t, usn in self.col.tags.allItems(): + if usn == -1: + return "tag had usn = -1" + found = False + for m in self.col.models.all(): + if m['usn'] == -1: + return "model had usn = -1" + if found: + self.col.models.save() +# self.col.sched.reset() + # check for missing parent decks + #self.col.sched.deckDueList() + # return summary of deck + return [ + list(self.col.sched.counts()), + self.col.db.scalar("select count() from cards"), + self.col.db.scalar("select count() from notes"), + self.col.db.scalar("select count() from revlog"), + self.col.db.scalar("select count() from graves"), + len(self.col.models.all()), + len(self.col.decks.all()), + len(self.col.decks.allConf()), + ] + + def usnLim(self): + return "usn = -1" + + def finish(self, mod=None): + self.col.ls = mod + self.col._usn = self.maxUsn + 1 + # ensure we save the mod time even if no changes made + self.col.db.mod = True + self.col.save(mod=mod) + return mod + + # Chunked syncing + ########################################################################## + + def prepareToChunk(self): + self.tablesLeft = ["revlog", "cards", "notes"] + self.cursor = None + + def cursorForTable(self, table): + lim = self.usnLim() + with open("/dev/stdout", "w") as f: + f.write(str(type(self.col.db))) + x = self.col.db.execute + d = (self.maxUsn, lim) + if table == "revlog": + return x(""" +select id, cid, %d, ease, ivl, lastIvl, factor, time, type +from revlog where %s""" % d) + elif table == "cards": + return x(""" +select id, nid, did, ord, mod, %d, type, queue, due, ivl, factor, reps, +lapses, left, odue, odid, flags, data from cards where %s""" % d) + else: + return x(""" +select id, guid, mid, mod, %d, tags, flds, '', '', flags, data +from notes where %s""" % d) + + def chunk(self): + buf = dict(done=False) + while self.tablesLeft: + curTable = self.tablesLeft.pop() + if not self.cursor: + self.cursor = self.cursorForTable(curTable) + buf[curTable] = self.cursor + if not self.tablesLeft: + buf['done'] = True + return buf + + def applyChunk(self, chunk): + if "revlog" in chunk: + self.mergeRevlog(chunk['revlog']) + if "cards" in chunk: + self.mergeCards(chunk['cards']) + if "notes" in chunk: + self.mergeNotes(chunk['notes']) + + # Deletions + ########################################################################## + + def removed(self): + cards = [] + notes = [] + decks = [] + + curs = self.col.db.execute( + "select oid, type from graves where usn = -1") + + for oid, type in curs: + if type == REM_CARD: + cards.append(oid) + elif type == REM_NOTE: + notes.append(oid) + else: + decks.append(oid) + + self.col.db.execute("update graves set usn=? where usn=-1", + self.maxUsn) + + return dict(cards=cards, notes=notes, decks=decks) + + def remove(self, graves): + # pretend to be the server so we don't set usn = -1 + self.col.server = True + + # notes first, so we don't end up with duplicate graves + self.col._remNotes(graves['notes']) + # then cards + self.col.remCards(graves['cards'], notes=False) + # and decks + for oid in graves['decks']: + self.col.decks.rem(oid, childrenToo=False) + + self.col.server = False + + # Models + ########################################################################## + + def getModels(self): + mods = [m for m in self.col.models.all() if m['usn'] == -1] + for m in mods: + m['usn'] = self.maxUsn + self.col.models.save() + return mods + + def mergeModels(self, rchg): + for r in rchg: + l = self.col.models.get(r['id']) + # if missing locally or server is newer, update + if not l or r['mod'] > l['mod']: + self.col.models.update(r) + + # Decks + ########################################################################## + + def getDecks(self): + decks = [g for g in self.col.decks.all() if g['usn'] == -1] + for g in decks: + g['usn'] = self.maxUsn + dconf = [g for g in self.col.decks.allConf() if g['usn'] == -1] + for g in dconf: + g['usn'] = self.maxUsn + self.col.decks.save() + return [decks, dconf] + + def mergeDecks(self, rchg): + for r in rchg[0]: + l = self.col.decks.get(r['id'], False) + # work around mod time being stored as string + if l and not isinstance(l['mod'], int): + l['mod'] = int(l['mod']) + + # if missing locally or server is newer, update + if not l or r['mod'] > l['mod']: + self.col.decks.update(r) + for r in rchg[1]: + try: + l = self.col.decks.getConf(r['id']) + except KeyError: + l = None + # if missing locally or server is newer, update + if not l or r['mod'] > l['mod']: + self.col.decks.updateConf(r) + + # Tags + ########################################################################## + + def getTags(self): + tags = [] + for t, usn in self.col.tags.allItems(): + if usn == -1: + self.col.tags.tags[t] = self.maxUsn + tags.append(t) + self.col.tags.save() + return tags + + def mergeTags(self, tags): + self.col.tags.register(tags, usn=self.maxUsn) + + # Cards/notes/revlog + ########################################################################## + + def mergeRevlog(self, logs): + self.col.db.executemany( + "insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)", + logs) + + def newerRows(self, data, table, modIdx): + ids = (r[0] for r in data) + lmods = {} + for id, mod in self.col.db.execute( + "select id, mod from %s where id in %s and %s" % ( + table, ids2str(ids), self.usnLim())): + lmods[id] = mod + update = [] + for r in data: + if r[0] not in lmods or lmods[r[0]] < r[modIdx]: + update.append(r) + self.col.log(table, data) + return update + + def mergeCards(self, cards): + self.col.db.executemany( + "insert or replace into cards values " + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", + self.newerRows(cards, "cards", 4)) + + def mergeNotes(self, notes): + rows = self.newerRows(notes, "notes", 3) + self.col.db.executemany( + "insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", + rows) + self.col.updateFieldCache([f[0] for f in rows]) + + # Col config + ########################################################################## + + def getConf(self): + return self.col.conf + + def mergeConf(self, conf): + self.col.conf = conf + +# Wrapper for requests that tracks upload/download progress +########################################################################## + +class AnkiRequestsClient(object): + verify = True + timeout = 60 + + def __init__(self): + self.session = requests.Session() + + def post(self, url, data, headers): + data = _MonitoringFile(data) + headers['User-Agent'] = self._agentName() + return self.session.post( + url, data=data, headers=headers, stream=True, timeout=self.timeout, verify=self.verify) + + def get(self, url, headers=None): + if headers is None: + headers = {} + headers['User-Agent'] = self._agentName() + return self.session.get(url, stream=True, headers=headers, timeout=self.timeout, verify=self.verify) + + def streamContent(self, resp): + resp.raise_for_status() + + buf = io.BytesIO() + for chunk in resp.iter_content(chunk_size=HTTP_BUF_SIZE): + runHook("httpRecv", len(chunk)) + buf.write(chunk) + return buf.getvalue() + + def _agentName(self): + from anki import version + return "Anki {}".format(version) + +# allow user to accept invalid certs in work/school settings +if os.environ.get("ANKI_NOVERIFYSSL"): + AnkiRequestsClient.verify = False + + import warnings + warnings.filterwarnings("ignore") + +class _MonitoringFile(io.BufferedReader): + def read(self, size=-1): + data = io.BufferedReader.read(self, HTTP_BUF_SIZE) + runHook("httpSend", len(data)) + return data + +# HTTP syncing tools +########################################################################## + +class HttpSyncer(object): + def __init__(self, hkey=None, client=None, hostNum=None): + self.hkey = hkey + self.skey = checksum(str(random.random()))[:8] + self.client = client or AnkiRequestsClient() + self.postVars = {} + self.hostNum = hostNum + self.prefix = "sync/" + + def syncURL(self): + if devMode: + url = "https://l1sync.ankiweb.net/" + else: + url = SYNC_BASE % (self.hostNum or "") + return url + self.prefix + + def assertOk(self, resp): + # not using raise_for_status() as aqt expects this error msg + if resp.status_code != 200: + raise Exception("Unknown response code: %s" % resp.status_code) + + # Posting data as a file + ###################################################################### + # We don't want to post the payload as a form var, as the percent-encoding is + # costly. We could send it as a raw post, but more HTTP clients seem to + # support file uploading, so this is the more compatible choice. + + def _buildPostData(self, fobj, comp): + BOUNDARY=b"Anki-sync-boundary" + bdry = b"--"+BOUNDARY + buf = io.BytesIO() + # post vars + self.postVars['c'] = 1 if comp else 0 + for (key, value) in list(self.postVars.items()): + buf.write(bdry + b"\r\n") + buf.write( + ('Content-Disposition: form-data; name="%s"\r\n\r\n%s\r\n' % + (key, value)).encode("utf8")) + # payload as raw data or json + rawSize = 0 + if fobj: + # header + buf.write(bdry + b"\r\n") + buf.write(b"""\ +Content-Disposition: form-data; name="data"; filename="data"\r\n\ +Content-Type: application/octet-stream\r\n\r\n""") + # write file into buffer, optionally compressing + if comp: + tgt = gzip.GzipFile(mode="wb", fileobj=buf, compresslevel=comp) + else: + tgt = buf + while 1: + data = fobj.read(65536) + if not data: + if comp: + tgt.close() + break + rawSize += len(data) + tgt.write(data) + buf.write(b"\r\n") + buf.write(bdry + b'--\r\n') + size = buf.tell() + # connection headers + headers = { + 'Content-Type': 'multipart/form-data; boundary=%s' % BOUNDARY.decode("utf8"), + 'Content-Length': str(size), + } + buf.seek(0) + + if size >= 100*1024*1024 or rawSize >= 250*1024*1024: + raise Exception("Collection too large to upload to AnkiWeb.") + + return headers, buf + + def req(self, method, fobj=None, comp=6, badAuthRaises=True): + headers, body = self._buildPostData(fobj, comp) + + r = self.client.post(self.syncURL()+method, data=body, headers=headers) + if not badAuthRaises and r.status_code == 403: + return False + self.assertOk(r) + + buf = self.client.streamContent(r) + return buf + +# Incremental sync over HTTP +###################################################################### + +class RemoteServer(HttpSyncer): + def __init__(self, hkey, hostNum): + super().__init__(self, hkey, hostNum=hostNum) + + def hostKey(self, user, pw): + "Returns hkey or none if user/pw incorrect." + self.postVars = dict() + ret = self.req( + "hostKey", io.BytesIO(json.dumps(dict(u=user, p=pw)).encode("utf8")), + badAuthRaises=False) + if not ret: + # invalid auth + return + self.hkey = json.loads(ret.decode("utf8"))['key'] + return self.hkey + + def meta(self): + self.postVars = dict( + k=self.hkey, + s=self.skey, + ) + ret = self.req( + "meta", io.BytesIO(json.dumps(dict( + v=SYNC_VER, cv="ankidesktop,%s,%s"%(versionWithBuild(), platDesc()))).encode("utf8")), + badAuthRaises=False) + if not ret: + # invalid auth + return + return json.loads(ret.decode("utf8")) + + def applyGraves(self, **kw): + return self._run("applyGraves", kw) + + def applyChanges(self, **kw): + return self._run("applyChanges", kw) + + def start(self, **kw): + return self._run("start", kw) + + def chunk(self, **kw): + return self._run("chunk", kw) + + def applyChunk(self, **kw): + return self._run("applyChunk", kw) + + def sanityCheck2(self, **kw): + return self._run("sanityCheck2", kw) + + def finish(self, **kw): + return self._run("finish", kw) + + def abort(self, **kw): + return self._run("abort", kw) + + def _run(self, cmd, data): + return json.loads( + self.req(cmd, io.BytesIO(json.dumps(data).encode("utf8"))).decode("utf8")) + +# Full syncing +########################################################################## + +class FullSyncer(HttpSyncer): + def __init__(self, col, hkey, client, hostNum): + super().__init__(self, hkey, client, hostNum=hostNum) + self.postVars = dict( + k=self.hkey, + v="ankidesktop,%s,%s"%(anki.version, platDesc()), + ) + self.col = col + + def download(self): + runHook("sync", "download") + localNotEmpty = self.col.db.scalar("select 1 from cards") + self.col.close() + cont = self.req("download") + tpath = self.col.path + ".tmp" + if cont == "upgradeRequired": + runHook("sync", "upgradeRequired") + return + open(tpath, "wb").write(cont) + # check the received file is ok + d = DB(tpath) + assert d.scalar("pragma integrity_check") == "ok" + remoteEmpty = not d.scalar("select 1 from cards") + d.close() + # accidental clobber? + if localNotEmpty and remoteEmpty: + os.unlink(tpath) + return "downloadClobber" + # overwrite existing collection + os.unlink(self.col.path) + os.rename(tpath, self.col.path) + self.col = None + + def upload(self): + "True if upload successful." + runHook("sync", "upload") + # make sure it's ok before we try to upload + if self.col.db.scalar("pragma integrity_check") != "ok": + return False + if not self.col.basicCheck(): + return False + # apply some adjustments, then upload + self.col.beforeUpload() + if self.req("upload", open(self.col.path, "rb")) != b"OK": + return False + return True + +# Media syncing +########################################################################## +# +# About conflicts: +# - to minimize data loss, if both sides are marked for sending and one +# side has been deleted, favour the add +# - if added/changed on both sides, favour the server version on the +# assumption other syncers are in sync with the server +# + +class MediaSyncer(object): + def __init__(self, col, server=None): + self.col = col + self.server = server + + def sync(self): + # check if there have been any changes + runHook("sync", "findMedia") + self.col.log("findChanges") + try: + self.col.media.findChanges() + except DBError: + return "corruptMediaDB" + + # begin session and check if in sync + lastUsn = self.col.media.lastUsn() + ret = self.server.begin() + srvUsn = ret['usn'] + if lastUsn == srvUsn and not self.col.media.haveDirty(): + return "noChanges" + + # loop through and process changes from server + self.col.log("last local usn is %s"%lastUsn) + self.downloadCount = 0 + while True: + data = self.server.mediaChanges(lastUsn=lastUsn) + + self.col.log("mediaChanges resp count %d"%len(data)) + if not data: + break + + need = [] + lastUsn = data[-1][1] + for fname, rusn, rsum in data: + lsum, ldirty = self.col.media.syncInfo(fname) + self.col.log( + "check: lsum=%s rsum=%s ldirty=%d rusn=%d fname=%s"%( + (lsum and lsum[0:4]), + (rsum and rsum[0:4]), + ldirty, + rusn, + fname)) + + if rsum: + # added/changed remotely + if not lsum or lsum != rsum: + self.col.log("will fetch") + need.append(fname) + else: + self.col.log("have same already") + if ldirty: + self.col.media.markClean([fname]) + elif lsum: + # deleted remotely + if not ldirty: + self.col.log("delete local") + self.col.media.syncDelete(fname) + else: + # conflict; local add overrides remote delete + self.col.log("conflict; will send") + else: + # deleted both sides + self.col.log("both sides deleted") + if ldirty: + self.col.media.markClean([fname]) + + self._downloadFiles(need) + + self.col.log("update last usn to %d"%lastUsn) + self.col.media.setLastUsn(lastUsn) # commits + + # at this point we're all up to date with the server's changes, + # and we need to send our own + + updateConflict = False + toSend = self.col.media.dirtyCount() + while True: + zip, fnames = self.col.media.mediaChangesZip() + if not fnames: + break + + runHook("syncMsg", ngettext( + "%d media change to upload", "%d media changes to upload", toSend) + % toSend) + + processedCnt, serverLastUsn = self.server.uploadChanges(zip) + self.col.media.markClean(fnames[0:processedCnt]) + + self.col.log("processed %d, serverUsn %d, clientUsn %d" % ( + processedCnt, serverLastUsn, lastUsn + )) + + if serverLastUsn - processedCnt == lastUsn: + self.col.log("lastUsn in sync, updating local") + lastUsn = serverLastUsn + self.col.media.setLastUsn(serverLastUsn) # commits + else: + self.col.log("concurrent update, skipping usn update") + # commit for markClean + self.col.media.db.commit() + updateConflict = True + + toSend -= processedCnt + + if updateConflict: + self.col.log("restart sync due to concurrent update") + return self.sync() + + lcnt = self.col.media.mediaCount() + ret = self.server.mediaSanity(local=lcnt) + if ret == "OK": + return "OK" + else: + self.col.media.forceResync() + return ret + + def _downloadFiles(self, fnames): + self.col.log("%d files to fetch"%len(fnames)) + while fnames: + top = fnames[0:SYNC_ZIP_COUNT] + self.col.log("fetch %s"%top) + zipData = self.server.downloadFiles(files=top) + cnt = self.col.media.addFilesFromZip(zipData) + self.downloadCount += cnt + self.col.log("received %d files"%cnt) + fnames = fnames[cnt:] + + n = self.downloadCount + runHook("syncMsg", ngettext( + "%d media file downloaded", "%d media files downloaded", n) + % n) + +# Remote media syncing +########################################################################## + +class RemoteMediaServer(HttpSyncer): + def __init__(self, col, hkey, client, hostNum): + self.col = col + super().__init__(self, hkey, client, hostNum=hostNum) + self.prefix = "msync/" + + def begin(self): + self.postVars = dict( + k=self.hkey, + v="ankidesktop,%s,%s"%(anki.version, platDesc()) + ) + ret = self._dataOnly(self.req( + "begin", io.BytesIO(json.dumps(dict()).encode("utf8")))) + self.skey = ret['sk'] + return ret + + # args: lastUsn + def mediaChanges(self, **kw): + self.postVars = dict( + sk=self.skey, + ) + return self._dataOnly( + self.req("mediaChanges", io.BytesIO(json.dumps(kw).encode("utf8")))) + + # args: files + def downloadFiles(self, **kw): + return self.req("downloadFiles", io.BytesIO(json.dumps(kw).encode("utf8"))) + + def uploadChanges(self, zip): + # no compression, as we compress the zip file instead + return self._dataOnly( + self.req("uploadChanges", io.BytesIO(zip), comp=0)) + + # args: local + def mediaSanity(self, **kw): + return self._dataOnly( + self.req("mediaSanity", io.BytesIO(json.dumps(kw).encode("utf8")))) + + def _dataOnly(self, resp): + resp = json.loads(resp.decode("utf8")) + if resp['err']: + self.col.log("error returned:%s"%resp['err']) + raise Exception("SyncError:%s"%resp['err']) + return resp['data'] + + # only for unit tests + def mediatest(self, cmd): + self.postVars = dict( + k=self.hkey, + ) + return self._dataOnly( + self.req("newMediaTest", io.BytesIO( + json.dumps(dict(cmd=cmd)).encode("utf8")))) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 7cce5ae..e452898 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -35,24 +35,24 @@ from webob.dec import wsgify from webob.exc import * import anki.db -import anki.sync import anki.utils -from anki.consts import SYNC_VER, SYNC_ZIP_SIZE, SYNC_ZIP_COUNT from anki.consts import REM_CARD, REM_NOTE from ankisyncd.users import get_user_manager from ankisyncd.sessions import get_session_manager from ankisyncd.full_sync import get_full_sync_manager +from .sync import Syncer, SYNC_VER, SYNC_ZIP_SIZE, SYNC_ZIP_COUNT + logger = logging.getLogger("ankisyncd") -class SyncCollectionHandler(anki.sync.Syncer): +class SyncCollectionHandler(Syncer): operations = ['meta', 'applyChanges', 'start', 'applyGraves', 'chunk', 'applyChunk', 'sanityCheck2', 'finish'] def __init__(self, col): # So that 'server' (the 3rd argument) can't get set - anki.sync.Syncer.__init__(self, col) + super().__init__(self, col) @staticmethod def _old_client(cv): @@ -137,7 +137,7 @@ class SyncCollectionHandler(anki.sync.Syncer): return dict(status="ok") def finish(self, mod=None): - return anki.sync.Syncer.finish(self, anki.utils.intTime(1000)) + return super().finish(self, anki.utils.intTime(1000)) # This function had to be put here in its entirety because Syncer.removed() # doesn't use self.usnLim() (which we override in this class) in queries. From bc889958dc443051a0bc6454a4dd8d8d4e85d1b3 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 26 Aug 2020 16:51:06 +0200 Subject: [PATCH 24/75] Added missing fields to meta endpoint --- src/ankisyncd/sync_app.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index e452898..9a41b0e 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -50,9 +50,10 @@ logger = logging.getLogger("ankisyncd") class SyncCollectionHandler(Syncer): operations = ['meta', 'applyChanges', 'start', 'applyGraves', 'chunk', 'applyChunk', 'sanityCheck2', 'finish'] - def __init__(self, col): + def __init__(self, col, session): # So that 'server' (the 3rd argument) can't get set super().__init__(self, col) + self.session = session @staticmethod def _old_client(cv): @@ -96,13 +97,15 @@ class SyncCollectionHandler(Syncer): self.col.media.connect() return { - 'scm': self.col.scm, - 'ts': anki.utils.intTime(), 'mod': self.col.mod, + 'scm': self.col.scm, 'usn': self.col._usn, + 'ts': anki.utils.intTime(), 'musn': self.col.media.lastUsn(), + 'uname': self.session.name, 'msg': '', 'cont': True, + 'hostNum': 0, } def usnLim(self): @@ -176,8 +179,9 @@ class SyncCollectionHandler(Syncer): class SyncMediaHandler: operations = ['begin', 'mediaChanges', 'mediaSanity', 'uploadChanges', 'downloadFiles'] - def __init__(self, col): + def __init__(self, col, session): self.col = col + self.session = session def begin(self, skey): return { @@ -376,7 +380,7 @@ class SyncUserSession: raise Exception("no handler for {}".format(operation)) if getattr(self, attr) is None: - setattr(self, attr, handler_class(col)) + setattr(self, attr, handler_class(col, self)) handler = getattr(self, attr) # The col object may actually be new now! This happens when we close a collection # for inactivity and then later re-open it (creating a new Collection object). From 3857f15c0614925a32bcfd657977d285b1dd389e Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 26 Aug 2020 16:58:50 +0200 Subject: [PATCH 25/75] Read hostkey from GET or POST This commit applies the fix from https://github.com/tsudoko/anki-sync-server/pull/60/files However it using a shorter version by utilizing the params attribute of the webob request. The params attribute combines the get and post params --- src/ankisyncd/sync_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 9a41b0e..0baa830 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -499,7 +499,7 @@ class SyncApp: def __call__(self, req): # Get and verify the session try: - hkey = req.POST['k'] + hkey = req.params['k'] except KeyError: hkey = None From b566e3259758857e74b137eb7b115af2a2523c4b Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 26 Aug 2020 18:27:05 +0200 Subject: [PATCH 26/75] Removed call to load method of anki.collection.Collection This method was removed in a2b7a3084131f747fb476cc8a24f96a00c654859 --- src/ankisyncd/full_sync.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ankisyncd/full_sync.py b/src/ankisyncd/full_sync.py index 9044abd..5d6a5cc 100644 --- a/src/ankisyncd/full_sync.py +++ b/src/ankisyncd/full_sync.py @@ -2,6 +2,7 @@ import os from sqlite3 import dbapi2 as sqlite +import sys import anki.db @@ -28,7 +29,6 @@ class FullSyncManager: os.replace(temp_db_path, session.get_collection_path()) finally: col.reopen() - col.load() return "OK" @@ -39,7 +39,6 @@ class FullSyncManager: data = open(session.get_collection_path(), 'rb').read() finally: col.reopen() - col.load() return data From 8358b092a3b119585286dfabfcd2ae0195cffa45 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 26 Aug 2020 21:06:57 +0200 Subject: [PATCH 27/75] Hide the media managers db --- src/ankisyncd/full_sync.py | 2 ++ src/ankisyncd/media.py | 36 +++++++++++++++++++++++------------- src/ankisyncd/sessions.py | 2 +- src/ankisyncd/sync_app.py | 10 +++------- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/ankisyncd/full_sync.py b/src/ankisyncd/full_sync.py index 5d6a5cc..dd26d68 100644 --- a/src/ankisyncd/full_sync.py +++ b/src/ankisyncd/full_sync.py @@ -29,6 +29,8 @@ class FullSyncManager: os.replace(temp_db_path, session.get_collection_path()) finally: col.reopen() + # Reopen the media database + col.media.connect() return "OK" diff --git a/src/ankisyncd/media.py b/src/ankisyncd/media.py index 9c68c4c..c6627c5 100644 --- a/src/ankisyncd/media.py +++ b/src/ankisyncd/media.py @@ -11,18 +11,27 @@ import anki.db logger = logging.getLogger("ankisyncd.media") - -class ServerMediaManager: +class ServerMediaManager(object): def __init__(self, col): self._dir = re.sub(r"(?i)\.(anki2)$", ".media", col.path) self.connect() + def addMedia(self, media_to_add): + self._db.executemany( + "INSERT OR REPLACE INTO media VALUES (?,?,?)", + media_to_add + ) + self._db.commit() + + def changes(self, lastUsn): + return self._db.execute("select fname,usn,csum from media order by usn desc limit ?", self.lastUsn() - lastUsn) + def connect(self): path = self.dir() + ".server.db" create = not os.path.exists(path) - self.db = anki.db.DB(path) + self._db = anki.db.DB(path) if create: - self.db.executescript( + self._db.executescript( """CREATE TABLE media ( fname TEXT NOT NULL PRIMARY KEY, usn INT NOT NULL, @@ -33,35 +42,36 @@ class ServerMediaManager: oldpath = self.dir() + ".db2" if os.path.exists(oldpath): logger.info("Found client media database, migrating contents") - self.db.execute("ATTACH ? AS old", oldpath) - self.db.execute( + self._db.execute("ATTACH ? AS old", oldpath) + self._db.execute( "INSERT INTO media SELECT fname, lastUsn, csum FROM old.media, old.meta" ) - self.db.commit() - self.db.execute("DETACH old") + self._db.commit() + self._db.execute("DETACH old") def close(self): - self.db.close() + self._db.close() def dir(self): return self._dir def lastUsn(self): - return self.db.scalar("SELECT max(usn) FROM media") or 0 + return self._db.scalar("SELECT max(usn) FROM media") or 0 def mediaCount(self): - return self.db.scalar("SELECT count() FROM media WHERE csum IS NOT NULL") + return self._db.scalar("SELECT count() FROM media WHERE csum IS NOT NULL") # used only in unit tests def syncInfo(self, fname): - return self.db.first("SELECT csum, 0 FROM media WHERE fname=?", fname) + return self._db.first("SELECT csum, 0 FROM media WHERE fname=?", fname) def syncDelete(self, fname): fpath = os.path.join(self.dir(), fname) if os.path.exists(fpath): os.remove(fpath) - self.db.execute( + self._db.execute( "UPDATE media SET csum = NULL, usn = ? WHERE fname = ?", self.lastUsn() + 1, fname, ) + self._db.commit() diff --git a/src/ankisyncd/sessions.py b/src/ankisyncd/sessions.py index 2e09ab6..7c609db 100644 --- a/src/ankisyncd/sessions.py +++ b/src/ankisyncd/sessions.py @@ -32,7 +32,7 @@ class SqliteSessionManager(SimpleSessionManager): everytime the SyncApp is restarted.""" def __init__(self, session_db_path): - SimpleSessionManager.__init__(self) + super().__init__() self.session_db_path = os.path.realpath(session_db_path) self._ensure_schema_up_to_date() diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 0baa830..fad24e4 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -93,8 +93,7 @@ class SyncCollectionHandler(Syncer): return {"cont": False, "msg": "Your client doesn't support the v{} scheduler.".format(self.col.schedVer())} # Make sure the media database is open! - if self.col.media.db is None: - self.col.media.connect() + self.col.media.connect() return { 'mod': self.col.mod, @@ -267,9 +266,7 @@ class SyncMediaHandler: self._remove_media_files(media_to_remove) if media_to_add: - self.col.media.db.executemany( - "INSERT OR REPLACE INTO media VALUES (?,?,?)", media_to_add) - self.col.media.db.commit() + self.col.media.addMedia(media_to_add) assert self.col.media.lastUsn() == oldUsn + processed_count # TODO: move to some unit test return processed_count @@ -325,10 +322,9 @@ class SyncMediaHandler: def mediaChanges(self, lastUsn): result = [] server_lastUsn = self.col.media.lastUsn() - fname = csum = None if lastUsn < server_lastUsn or lastUsn == 0: - for fname,usn,csum, in self.col.media.db.execute("select fname,usn,csum from media order by usn desc limit ?", server_lastUsn - lastUsn): + for fname,usn,csum, in self.col.media.changes(lastUsn): result.append([fname, usn, csum]) # anki assumes server_lastUsn == result[-1][1] From c97a096e8a36809aa3a7daab33e85d217a33bb29 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 00:01:36 +0200 Subject: [PATCH 28/75] Made sure to use ConfigManager in Syncer --- src/ankisyncd/sync.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 2d46950..900973f 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -14,6 +14,7 @@ import os from anki.db import DB, DBError from anki.utils import ids2str, intTime, platDesc, checksum, devMode from anki.consts import * +from anki.config import ConfigManager from anki.utils import versionWithBuild from anki.hooks import runHook import anki @@ -205,7 +206,7 @@ class Syncer(object): return "model had usn = -1" if found: self.col.models.save() -# self.col.sched.reset() + self.col.sched.reset() # check for missing parent decks #self.col.sched.deckDueList() # return summary of deck @@ -420,7 +421,10 @@ from notes where %s""" % d) return self.col.conf def mergeConf(self, conf): - self.col.conf = conf + newConf = ConfigManager(self.col) + for key, value in conf.items(): + newConf[key] = value + self.col.conf = newConf # Wrapper for requests that tracks upload/download progress ########################################################################## From 93d37d6ab6ee7ebbd98ed665889334dcf317c09d Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 00:02:10 +0200 Subject: [PATCH 29/75] fix chunk in sync --- src/ankisyncd/sync.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 900973f..8cd3c3f 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -239,32 +239,26 @@ class Syncer(object): self.tablesLeft = ["revlog", "cards", "notes"] self.cursor = None - def cursorForTable(self, table): + def queryTable(self, table): lim = self.usnLim() - with open("/dev/stdout", "w") as f: - f.write(str(type(self.col.db))) - x = self.col.db.execute - d = (self.maxUsn, lim) if table == "revlog": - return x(""" -select id, cid, %d, ease, ivl, lastIvl, factor, time, type -from revlog where %s""" % d) + return self.col.db.execute(""" +select id, cid, ?, ease, ivl, lastIvl, factor, time, type +from revlog where ?""", self.maxUsn, lim) elif table == "cards": - return x(""" -select id, nid, did, ord, mod, %d, type, queue, due, ivl, factor, reps, -lapses, left, odue, odid, flags, data from cards where %s""" % d) + return self.col.db.execute(""" +select id, nid, did, ord, mod, ?, type, queue, due, ivl, factor, reps, +lapses, left, odue, odid, flags, data from cards where ?""", self.maxUsn, lim) else: - return x(""" -select id, guid, mid, mod, %d, tags, flds, '', '', flags, data -from notes where %s""" % d) + return self.col.db.execute(""" +select id, guid, mid, mod, ?, tags, flds, '', '', flags, data +from notes where ?""", self.maxUsn, lim) def chunk(self): buf = dict(done=False) while self.tablesLeft: curTable = self.tablesLeft.pop() - if not self.cursor: - self.cursor = self.cursorForTable(curTable) - buf[curTable] = self.cursor + buf[curTable] = self.queryTable(curTable) if not self.tablesLeft: buf['done'] = True return buf From e18e86e80904485738d6296a24ea08d079f3ddcc Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 00:03:11 +0200 Subject: [PATCH 30/75] Log sanity check errors --- src/ankisyncd/sync_app.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index fad24e4..bce786d 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -132,14 +132,18 @@ class SyncCollectionHandler(Syncer): self.mergeChanges(lchg, self.rchg) return lchg - def sanityCheck2(self, client): + def sanityCheck2(self, client, full=None): server = self.sanityCheck() if client != server: + logger.info( + f"sanity check failed with server: {server} client: {client}" + ) + return dict(status="bad", c=client, s=server) return dict(status="ok") def finish(self, mod=None): - return super().finish(self, anki.utils.intTime(1000)) + return super().finish(anki.utils.intTime(1000)) # This function had to be put here in its entirety because Syncer.removed() # doesn't use self.usnLim() (which we override in this class) in queries. @@ -295,7 +299,6 @@ class SyncMediaHandler: for filename in filenames: try: self.col.media.syncDelete(filename) - self.col.media.db.commit() except OSError as err: logger.error("Error when removing file '%s' from media dir: " "%s" % (filename, str(err))) From 7eff3815a48d28b3a2965188e593a40f21db2278 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 00:34:07 +0200 Subject: [PATCH 31/75] Always downgrade the database before sync This prevents the missing collation unicase error on the client --- src/ankisyncd/full_sync.py | 57 ++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/src/ankisyncd/full_sync.py b/src/ankisyncd/full_sync.py index dd26d68..9e7c3cc 100644 --- a/src/ankisyncd/full_sync.py +++ b/src/ankisyncd/full_sync.py @@ -1,13 +1,36 @@ # -*- coding: utf-8 -*- +import logging import os from sqlite3 import dbapi2 as sqlite +import shutil import sys +from webob.exc import HTTPBadRequest -import anki.db +from anki.db import DB +from anki.collection import Collection -class FullSyncManager: - def upload(self, col, data, session): +logger = logging.getLogger("ankisyncd.media") +logger.setLevel(1) + +class FullSyncManager(object): + def test_db(self, db: DB): + """ + :param anki.db.DB db: the database uploaded from the client. + """ + if db.scalar("pragma integrity_check") != "ok": + raise HTTPBadRequest( + "Integrity check failed for uploaded collection database file." + ) + + def upload(self, col: Collection, data: bytes, session) -> str: + """ + Uploads a sqlite database from the client to the sync server. + + :param anki.collection.Collectio col: + :param bytes data: The binary sqlite database from the client. + :param .sync_app.SyncUserSession session: The current session. + """ # Verify integrity of the received database file before replacing our # existing db. temp_db_path = session.get_collection_path() + ".tmp" @@ -15,10 +38,8 @@ class FullSyncManager: f.write(data) try: - with anki.db.DB(temp_db_path) as test_db: - if test_db.scalar("pragma integrity_check") != "ok": - raise HTTPBadRequest("Integrity check failed for uploaded " - "collection database file.") + with DB(temp_db_path) as test_db: + self.test_db(test_db) except sqlite.Error as e: raise HTTPBadRequest("Uploaded collection database file is " "corrupt.") @@ -26,7 +47,7 @@ class FullSyncManager: # Overwrite existing db. col.close() try: - os.replace(temp_db_path, session.get_collection_path()) + shutil.copyfile(temp_db_path, session.get_collection_path()) finally: col.reopen() # Reopen the media database @@ -34,13 +55,27 @@ class FullSyncManager: return "OK" + def download(self, col: Collection, session) -> bytes: + """Download the binary database. - def download(self, col, session): - col.close() + Performs a downgrade to database schema 11 before sending the database + to the client. + + :param anki.collection.Collection col: + :param .sync_app.SyncUserSession session: + + :return bytes: the binary sqlite3 database + """ + col.close(downgrade=True) + db_path = session.get_collection_path() try: - data = open(session.get_collection_path(), 'rb').read() + with open(db_path, 'rb') as tmp: + data = tmp.read() finally: col.reopen() + # Reopen the media database + col.media.connect() + return data From 4c09c1e248ebef36d517adff75bf4cdb4a95047f Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 12:27:15 +0200 Subject: [PATCH 32/75] fix sanity check --- src/ankisyncd/sync.py | 14 ++++++++------ src/ankisyncd/sync_app.py | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 8cd3c3f..f5b97ca 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -187,7 +187,7 @@ class Syncer(object): self.col.crt = rchg['crt'] self.prepareToChunk() - def sanityCheck(self): + def sanityCheck(self, full): if not self.col.basicCheck(): return "failed basic check" for t in "cards", "notes", "revlog", "graves": @@ -244,21 +244,24 @@ class Syncer(object): if table == "revlog": return self.col.db.execute(""" select id, cid, ?, ease, ivl, lastIvl, factor, time, type -from revlog where ?""", self.maxUsn, lim) +from revlog where %s""" % lim, self.maxUsn) elif table == "cards": return self.col.db.execute(""" select id, nid, did, ord, mod, ?, type, queue, due, ivl, factor, reps, -lapses, left, odue, odid, flags, data from cards where ?""", self.maxUsn, lim) +lapses, left, odue, odid, flags, data from cards where %s""" % lim, self.maxUsn) else: return self.col.db.execute(""" select id, guid, mid, mod, ?, tags, flds, '', '', flags, data -from notes where ?""", self.maxUsn, lim) +from notes where %s""" % lim, self.maxUsn) def chunk(self): buf = dict(done=False) while self.tablesLeft: curTable = self.tablesLeft.pop() buf[curTable] = self.queryTable(curTable) + self.col.db.execute( + f"update {curTable} set usn=? where usn=-1", self.maxUsn + ) if not self.tablesLeft: buf['done'] = True return buf @@ -417,8 +420,7 @@ from notes where ?""", self.maxUsn, lim) def mergeConf(self, conf): newConf = ConfigManager(self.col) for key, value in conf.items(): - newConf[key] = value - self.col.conf = newConf + self.col.set_config(key, value) # Wrapper for requests that tracks upload/download progress ########################################################################## diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index bce786d..638e6d6 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -133,7 +133,7 @@ class SyncCollectionHandler(Syncer): return lchg def sanityCheck2(self, client, full=None): - server = self.sanityCheck() + server = self.sanityCheck(full) if client != server: logger.info( f"sanity check failed with server: {server} client: {client}" From 537bbe89c95f94e79b355093f1c40ddd6e874d46 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 12:39:51 +0200 Subject: [PATCH 33/75] updated readme to reflect latest changes --- README.md | 85 ++++++++++++++++++++----------------------------------- 1 file changed, 31 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 0714b57..7aa73fc 100644 --- a/README.md +++ b/README.md @@ -39,22 +39,8 @@ It supports Python 3 and Anki 2.1. Installing ---------- -0. Install Anki. The currently supported version range is 2.1.1〜2.1.11, with the - exception of 2.1.9[1](#readme-fn-01). (Keep in - mind this range only applies to the Anki used by the server, clients can be - as old as 2.0.27 and still work.) Running the server with other versions might - work as long as they're not 2.0.x, but things might break, so do it at your - own risk. If for some reason you can't get the supported Anki version easily - on your system, you can use `anki-bundled` from this repo: - - $ git submodule update --init - $ cd anki-bundled - $ pip install -r requirements.txt - - Keep in mind `pyaudio`, a dependency of Anki, requires development headers for - Python 3 and PortAudio to be present before running `pip`. If you can't or - don't want to install these, you can try [patching Anki](#running-ankisyncd-without-pyaudio). - +0. Install the current version of Anki. + 1. Install the dependencies: $ pip install webob @@ -65,22 +51,28 @@ Installing $ ./ankisyncctl.py adduser -4. Run ankisyncd: +4. Setup a proxy to unchunk the requests. +Webob does not support the header "Transfer-Encoding: chunked" used by Anki and +therefore ankisyncd sees chunked requests as empty. To solve this problem setup +Nginx (or any other webserver of your choice) and configure it to "unchunk" the +requests to ankisyncd: + + server { + listen 27701; + server_name default; + + location / { + proxy_http_version 1.0; + proxy_pass http://ankisyncd:27701/; + } + } + +5. Run ankisyncd: $ python -m ankisyncd --- - -1. 2.1.9 is not supported due to [commit `95ccbfdd3679`][] introducing the - dependency on the `aqt` module, which depends on PyQt5. The server should - still work fine if you have PyQt5 installed. This has been fixed in - [commit `a389b8b4a0e2`][], which is a part of the 2.1.10 release. -[↑](#readme-fn-01b) - -[commit `95ccbfdd3679`]: https://github.com/dae/anki/commit/95ccbfdd3679dd46f22847c539c7fddb8fa904ea -[commit `a389b8b4a0e2`]: https://github.com/dae/anki/commit/a389b8b4a0e209023c4533a7ee335096a704079c - Installing (Docker) ------------------- @@ -89,6 +81,18 @@ Follow [these instructions](https://github.com/kuklinistvan/docker-anki-sync-ser Setting up Anki --------------- +### Anki 2.1.28 and above + +Create a new directory in [the add-ons folder][addons21] (name it something +like ankisyncd), create a file named `__init__.py` containing the code below +and put it in the `ankisyncd` directory. + + import os + + addr = "http://127.0.0.1:27701/" # put your server address here + os.environ["SYNC_ENDPOINT"] = addr + "sync/" + os.environ["SYNC_ENDPOINT_MEDIA"] = addr + "msync/" + ### Anki 2.1 Create a new directory in [the add-ons folder][addons21] (name it something @@ -125,8 +129,6 @@ Unless you have set up a reverse proxy to handle encrypted connections, use whatever you have specified in `ankisyncd.conf` (or, if using a reverse proxy, whatever port you configured to accept the front-end connection). -**Do not use trailing slashes.** - Even though the AnkiDroid interface will request an email address, this is not required; it will simply be the username you configured with `ankisyncctl.py adduser`. @@ -138,31 +140,6 @@ Running `ankisyncd` without `pyaudio` want to install PortAudio, you can edit some files in the `anki-bundled` directory to exclude `pyaudio`: -### Anki ≥2.1.9 - -Just remove "pyaudio" from requirements.txt and you're done. This change has -been introduced in [commit `ca710ab3f1c1`][]. - -[commit `ca710ab3f1c1`]: https://github.com/dae/anki/commit/ca710ab3f1c1174469a3b48f1257c0fc0ce624bf - -### Older versions - -First go to `anki-bundled`, then follow one of the instructions below. They all -do the same thing, you can pick whichever one you're most comfortable with. - -Manual version: remove every line past "# Packaged commands" in anki/sound.py, -remove every line starting with "pyaudio" in requirements.txt - -`ed` version: - - $ echo '/# Packaged commands/,$d;w' | tr ';' '\n' | ed anki/sound.py - $ echo '/^pyaudio/d;w' | tr ';' '\n' | ed requirements.txt - -`sed -i` version: - - $ sed -i '/# Packaged commands/,$d' anki/sound.py - $ sed -i '/^pyaudio/d' requirements.txt - ENVVAR configuration overrides ------------------------------ From 471e3aead4abaa9f5e29b509207def18619dabe8 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 17:05:28 +0200 Subject: [PATCH 34/75] Removed haveDirty check from sync code The dirty field does not exist in the media table anymore. --- src/ankisyncd/sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index f5b97ca..1e8fd06 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -697,7 +697,7 @@ class MediaSyncer(object): lastUsn = self.col.media.lastUsn() ret = self.server.begin() srvUsn = ret['usn'] - if lastUsn == srvUsn and not self.col.media.haveDirty(): + if lastUsn == srvUsn: return "noChanges" # loop through and process changes from server From c6f82e20cd9c9fcebc6f3f2b85cee5f84adb5edc Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 19:58:10 +0200 Subject: [PATCH 35/75] Removed unused class MediaSyncer --- src/ankisyncd/sync.py | 142 ------------------------------------------ 1 file changed, 142 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 1e8fd06..3eda801 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -669,148 +669,6 @@ class FullSyncer(HttpSyncer): return False return True -# Media syncing -########################################################################## -# -# About conflicts: -# - to minimize data loss, if both sides are marked for sending and one -# side has been deleted, favour the add -# - if added/changed on both sides, favour the server version on the -# assumption other syncers are in sync with the server -# - -class MediaSyncer(object): - def __init__(self, col, server=None): - self.col = col - self.server = server - - def sync(self): - # check if there have been any changes - runHook("sync", "findMedia") - self.col.log("findChanges") - try: - self.col.media.findChanges() - except DBError: - return "corruptMediaDB" - - # begin session and check if in sync - lastUsn = self.col.media.lastUsn() - ret = self.server.begin() - srvUsn = ret['usn'] - if lastUsn == srvUsn: - return "noChanges" - - # loop through and process changes from server - self.col.log("last local usn is %s"%lastUsn) - self.downloadCount = 0 - while True: - data = self.server.mediaChanges(lastUsn=lastUsn) - - self.col.log("mediaChanges resp count %d"%len(data)) - if not data: - break - - need = [] - lastUsn = data[-1][1] - for fname, rusn, rsum in data: - lsum, ldirty = self.col.media.syncInfo(fname) - self.col.log( - "check: lsum=%s rsum=%s ldirty=%d rusn=%d fname=%s"%( - (lsum and lsum[0:4]), - (rsum and rsum[0:4]), - ldirty, - rusn, - fname)) - - if rsum: - # added/changed remotely - if not lsum or lsum != rsum: - self.col.log("will fetch") - need.append(fname) - else: - self.col.log("have same already") - if ldirty: - self.col.media.markClean([fname]) - elif lsum: - # deleted remotely - if not ldirty: - self.col.log("delete local") - self.col.media.syncDelete(fname) - else: - # conflict; local add overrides remote delete - self.col.log("conflict; will send") - else: - # deleted both sides - self.col.log("both sides deleted") - if ldirty: - self.col.media.markClean([fname]) - - self._downloadFiles(need) - - self.col.log("update last usn to %d"%lastUsn) - self.col.media.setLastUsn(lastUsn) # commits - - # at this point we're all up to date with the server's changes, - # and we need to send our own - - updateConflict = False - toSend = self.col.media.dirtyCount() - while True: - zip, fnames = self.col.media.mediaChangesZip() - if not fnames: - break - - runHook("syncMsg", ngettext( - "%d media change to upload", "%d media changes to upload", toSend) - % toSend) - - processedCnt, serverLastUsn = self.server.uploadChanges(zip) - self.col.media.markClean(fnames[0:processedCnt]) - - self.col.log("processed %d, serverUsn %d, clientUsn %d" % ( - processedCnt, serverLastUsn, lastUsn - )) - - if serverLastUsn - processedCnt == lastUsn: - self.col.log("lastUsn in sync, updating local") - lastUsn = serverLastUsn - self.col.media.setLastUsn(serverLastUsn) # commits - else: - self.col.log("concurrent update, skipping usn update") - # commit for markClean - self.col.media.db.commit() - updateConflict = True - - toSend -= processedCnt - - if updateConflict: - self.col.log("restart sync due to concurrent update") - return self.sync() - - lcnt = self.col.media.mediaCount() - ret = self.server.mediaSanity(local=lcnt) - if ret == "OK": - return "OK" - else: - self.col.media.forceResync() - return ret - - def _downloadFiles(self, fnames): - self.col.log("%d files to fetch"%len(fnames)) - while fnames: - top = fnames[0:SYNC_ZIP_COUNT] - self.col.log("fetch %s"%top) - zipData = self.server.downloadFiles(files=top) - cnt = self.col.media.addFilesFromZip(zipData) - self.downloadCount += cnt - self.col.log("received %d files"%cnt) - fnames = fnames[cnt:] - - n = self.downloadCount - runHook("syncMsg", ngettext( - "%d media file downloaded", "%d media files downloaded", n) - % n) - # Remote media syncing ########################################################################## From 9d67943c11ca0c46aad5024d9e16313d513c32e0 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:02:18 +0200 Subject: [PATCH 36/75] Marked test to fail because of missing _logChanges Method --- tests/test_media.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_media.py b/tests/test_media.py index fc67dd4..5e9f34f 100644 --- a/tests/test_media.py +++ b/tests/test_media.py @@ -15,6 +15,9 @@ class ServerMediaManagerTest(unittest.TestCase): cls.colutils.clean_up() cls.colutils = None + # This test is currently expected to fail because the _logChanges + # method of the media manager does not exist anymore. + @unittest.expectedFailure def test_upgrade(self): col = self.colutils.create_empty_col() cm = col.media From 94da88a3b2b3c20ea997d440b80eec2206b840ff Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:03:26 +0200 Subject: [PATCH 37/75] Updated media test to work with latest changes --- tests/test_media.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/test_media.py b/tests/test_media.py index 5e9f34f..a7aacd9 100644 --- a/tests/test_media.py +++ b/tests/test_media.py @@ -1,5 +1,6 @@ import os.path import unittest +from unittest.mock import MagicMock import ankisyncd.media import helpers.collection_utils @@ -44,19 +45,26 @@ class ServerMediaManagerTest(unittest.TestCase): list(cm.db.execute("SELECT fname, csum FROM media")), ) self.assertEqual(cm.lastUsn(), sm.lastUsn()) - self.assertEqual(list(sm.db.execute("SELECT usn FROM media")), [(161,), (161,)]) + self.assertEqual( + list(sm.db.execute("SELECT usn FROM media")), + [(161,), (161,)] + ) def test_mediaChanges_lastUsn_order(self): col = self.colutils.create_empty_col() col.media = ankisyncd.media.ServerMediaManager(col) - mh = ankisyncd.sync_app.SyncMediaHandler(col) - mh.col.media.db.execute(""" - INSERT INTO media (fname, usn, csum) - VALUES + session = MagicMock() + session.name = 'test' + mh = ankisyncd.sync_app.SyncMediaHandler(col, session) + mh.col.media.addMedia( + ( ('fileA', 101, '53059abba1a72c7aff34a3eaf7fef10ed65541ce'), - ('fileB', 100, 'a5ae546046d09559399c80fa7076fb10f1ce4bcd') - """) - + ('fileB', 100, 'a5ae546046d09559399c80fa7076fb10f1ce4bcd'), + ) + ) # anki assumes mh.col.media.lastUsn() == mh.mediaChanges()['data'][-1][1] # ref: anki/sync.py:720 (commit cca3fcb2418880d0430a5c5c2e6b81ba260065b7) - self.assertEqual(mh.mediaChanges(lastUsn=99)['data'][-1][1], mh.col.media.lastUsn()) + self.assertEqual( + mh.mediaChanges(lastUsn=99)['data'][-1][1], + mh.col.media.lastUsn() + ) From 89dcfd6ecd3625039d96c0bcf26a5d67d862caa7 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:04:18 +0200 Subject: [PATCH 38/75] ServerMediaManager extends MediaManager --- src/ankisyncd/media.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ankisyncd/media.py b/src/ankisyncd/media.py index c6627c5..47341f4 100644 --- a/src/ankisyncd/media.py +++ b/src/ankisyncd/media.py @@ -8,11 +8,13 @@ import os import os.path import anki.db +from anki.media import MediaManager logger = logging.getLogger("ankisyncd.media") -class ServerMediaManager(object): - def __init__(self, col): +class ServerMediaManager(MediaManager): + def __init__(self, col, server=True): + super().__init__(col, server) self._dir = re.sub(r"(?i)\.(anki2)$", ".media", col.path) self.connect() From 75c9267ecc6889708dbf3721abf8726a825363b4 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:04:57 +0200 Subject: [PATCH 39/75] Fix parent initialization of SyncCollectionHandler --- src/ankisyncd/sync_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 638e6d6..3cbe3aa 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -52,7 +52,7 @@ class SyncCollectionHandler(Syncer): def __init__(self, col, session): # So that 'server' (the 3rd argument) can't get set - super().__init__(self, col) + super().__init__(col) self.session = session @staticmethod From 2c1e5936b3920118973ddb766beb78c933e6e7d2 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:05:32 +0200 Subject: [PATCH 40/75] Removed test because of deprecated MediaSyncer --- tests/test_web_media.py | 435 ---------------------------------------- 1 file changed, 435 deletions(-) delete mode 100644 tests/test_web_media.py diff --git a/tests/test_web_media.py b/tests/test_web_media.py deleted file mode 100644 index 0e2c787..0000000 --- a/tests/test_web_media.py +++ /dev/null @@ -1,435 +0,0 @@ -# -*- coding: utf-8 -*- -import tempfile -import filecmp -import sqlite3 -import os -import shutil - -import helpers.file_utils -import helpers.server_utils -import helpers.db_utils -import anki.utils -from anki.sync import MediaSyncer -from helpers.mock_servers import MockRemoteMediaServer -from helpers.monkey_patches import monkeypatch_mediamanager, unpatch_mediamanager -from sync_app_functional_test_base import SyncAppFunctionalTestBase - - -class SyncAppFunctionalMediaTest(SyncAppFunctionalTestBase): - def setUp(self): - SyncAppFunctionalTestBase.setUp(self) - - monkeypatch_mediamanager() - self.tempdir = tempfile.mkdtemp(prefix=self.__class__.__name__) - self.hkey = self.mock_remote_server.hostKey("testuser", "testpassword") - client_collection = self.colutils.create_empty_col() - self.client_syncer = self.create_client_syncer(client_collection, - self.hkey, - self.server_test_app) - - def tearDown(self): - self.hkey = None - self.client_syncer = None - unpatch_mediamanager() - SyncAppFunctionalTestBase.tearDown(self) - - @staticmethod - def create_client_syncer(collection, hkey, server_test_app): - mock_remote_server = MockRemoteMediaServer(col=collection, - hkey=hkey, - server_test_app=server_test_app) - media_syncer = MediaSyncer(col=collection, - server=mock_remote_server) - return media_syncer - - @staticmethod - def file_checksum(fname): - with open(fname, "rb") as f: - return anki.utils.checksum(f.read()) - - def media_dbs_differ(self, left_db_path, right_db_path, compare_timestamps=False): - """ - Compares two media sqlite database files for equality. mtime and dirMod - timestamps are not considered when comparing. - - :param left_db_path: path to the left db file - :param right_db_path: path to the right db file - :param compare_timestamps: flag determining if timestamp values - (media.mtime and meta.dirMod) are included - in the comparison - :return: True if the specified databases differ, False else - """ - - if not os.path.isfile(right_db_path): - raise IOError("file '" + left_db_path + "' does not exist") - elif not os.path.isfile(right_db_path): - raise IOError("file '" + right_db_path + "' does not exist") - - # Create temporary copies of the files to act on. - newleft = os.path.join(self.tempdir, left_db_path) + ".tmp" - shutil.copyfile(left_db_path, newleft) - left_db_path = newleft - - newright = os.path.join(self.tempdir, left_db_path) + ".tmp" - shutil.copyfile(right_db_path, newright) - right_db_path = newright - - if not compare_timestamps: - # Set all timestamps that are not NULL to 0. - for dbPath in [left_db_path, right_db_path]: - connection = sqlite3.connect(dbPath) - - connection.execute("""UPDATE media SET mtime=0 - WHERE mtime IS NOT NULL""") - - connection.execute("""UPDATE meta SET dirMod=0 - WHERE rowid=1""") - connection.commit() - connection.close() - - return helpers.db_utils.diff(left_db_path, right_db_path) - - def test_sync_empty_media_dbs(self): - # With both the client and the server having no media to sync, - # syncing should change nothing. - self.assertEqual('noChanges', self.client_syncer.sync()) - self.assertEqual('noChanges', self.client_syncer.sync()) - - def test_sync_file_from_server(self): - """ - Adds a file on the server. After syncing, client and server should have - the identical file in their media directories and media databases. - """ - client = self.client_syncer - server = helpers.server_utils.get_syncer_for_hkey(self.server_app, - self.hkey, - 'media') - - # Create a test file. - temp_file_path = helpers.file_utils.create_named_file("foo.jpg", "hello") - - # Add the test file to the server's collection. - helpers.server_utils.add_files_to_server_mediadb(server.col.media, [temp_file_path]) - - # Syncing should work. - self.assertEqual(client.sync(), 'OK') - - # The test file should be present in the server's and in the client's - # media directory. - self.assertTrue( - filecmp.cmp(os.path.join(client.col.media.dir(), "foo.jpg"), - os.path.join(server.col.media.dir(), "foo.jpg"))) - - # Further syncing should do nothing. - self.assertEqual(client.sync(), 'noChanges') - - def test_sync_file_from_client(self): - """ - Adds a file on the client. After syncing, client and server should have - the identical file in their media directories and media databases. - """ - join = os.path.join - client = self.client_syncer - server = helpers.server_utils.get_syncer_for_hkey(self.server_app, - self.hkey, - 'media') - - # Create a test file. - temp_file_path = helpers.file_utils.create_named_file("foo.jpg", "hello") - - # Add the test file to the client's media collection. - helpers.server_utils.add_files_to_client_mediadb(client.col.media, - [temp_file_path], - update_db=True) - - # Syncing should work. - self.assertEqual(client.sync(), 'OK') - - # The same file should be present in both the client's and the server's - # media directory. - self.assertTrue(filecmp.cmp(join(client.col.media.dir(), "foo.jpg"), - join(server.col.media.dir(), "foo.jpg"))) - - # Further syncing should do nothing. - self.assertEqual(client.sync(), 'noChanges') - - # The media data of client and server should be identical. - self.assertEqual( - list(client.col.media.db.execute("SELECT fname, csum FROM media")), - list(server.col.media.db.execute("SELECT fname, csum FROM media")) - ) - self.assertEqual(client.col.media.lastUsn(), server.col.media.lastUsn()) - - def test_sync_different_files(self): - """ - Adds a file on the client and a file with different name and content on - the server. After syncing, both client and server should have both - files in their media directories and databases. - """ - join = os.path.join - isfile = os.path.isfile - client = self.client_syncer - server = helpers.server_utils.get_syncer_for_hkey(self.server_app, - self.hkey, - 'media') - - # Create two files and add one to the server and one to the client. - file_for_client = helpers.file_utils.create_named_file("foo.jpg", "hello") - file_for_server = helpers.file_utils.create_named_file("bar.jpg", "goodbye") - - helpers.server_utils.add_files_to_client_mediadb(client.col.media, - [file_for_client], - update_db=True) - helpers.server_utils.add_files_to_server_mediadb(server.col.media, [file_for_server]) - - # Syncing should work. - self.assertEqual(client.sync(), 'OK') - - # Both files should be present in the client's and in the server's - # media directories. - self.assertTrue(isfile(join(client.col.media.dir(), "foo.jpg"))) - self.assertTrue(isfile(join(server.col.media.dir(), "foo.jpg"))) - self.assertTrue(filecmp.cmp( - join(client.col.media.dir(), "foo.jpg"), - join(server.col.media.dir(), "foo.jpg")) - ) - self.assertTrue(isfile(join(client.col.media.dir(), "bar.jpg"))) - self.assertTrue(isfile(join(server.col.media.dir(), "bar.jpg"))) - self.assertTrue(filecmp.cmp( - join(client.col.media.dir(), "bar.jpg"), - join(server.col.media.dir(), "bar.jpg")) - ) - - # Further syncing should change nothing. - self.assertEqual(client.sync(), 'noChanges') - - def test_sync_different_contents(self): - """ - Adds a file to the client and a file with identical name but different - contents to the server. After syncing, both client and server should - have the server's version of the file in their media directories and - databases. - """ - join = os.path.join - isfile = os.path.isfile - client = self.client_syncer - server = helpers.server_utils.get_syncer_for_hkey(self.server_app, - self.hkey, - 'media') - - # Create two files with identical names but different contents and - # checksums. Add one to the server and one to the client. - file_for_client = helpers.file_utils.create_named_file("foo.jpg", "hello") - file_for_server = helpers.file_utils.create_named_file("foo.jpg", "goodbye") - - helpers.server_utils.add_files_to_client_mediadb(client.col.media, - [file_for_client], - update_db=True) - helpers.server_utils.add_files_to_server_mediadb(server.col.media, [file_for_server]) - - # Syncing should work. - self.assertEqual(client.sync(), 'OK') - - # A version of the file should be present in both the client's and the - # server's media directory. - self.assertTrue(isfile(join(client.col.media.dir(), "foo.jpg"))) - self.assertEqual(os.listdir(client.col.media.dir()), ['foo.jpg']) - self.assertTrue(isfile(join(server.col.media.dir(), "foo.jpg"))) - self.assertEqual(os.listdir(server.col.media.dir()), ['foo.jpg']) - self.assertEqual(client.sync(), 'noChanges') - - # Both files should have the contents of the server's version. - _checksum = client.col.media._checksum - self.assertEqual(_checksum(join(client.col.media.dir(), "foo.jpg")), - _checksum(file_for_server)) - self.assertEqual(_checksum(join(server.col.media.dir(), "foo.jpg")), - _checksum(file_for_server)) - - def test_sync_add_and_delete_on_client(self): - """ - Adds a file on the client. After syncing, the client and server should - both have the file. Then removes the file from the client's directory - and marks it as deleted in its database. After syncing again, the - server should have removed its version of the file from its media dir - and marked it as deleted in its db. - """ - join = os.path.join - isfile = os.path.isfile - client = self.client_syncer - server = helpers.server_utils.get_syncer_for_hkey(self.server_app, - self.hkey, - 'media') - - # Create a test file. - temp_file_path = helpers.file_utils.create_named_file("foo.jpg", "hello") - - # Add the test file to client's media collection. - helpers.server_utils.add_files_to_client_mediadb(client.col.media, - [temp_file_path], - update_db=True) - - # Syncing client should work. - self.assertEqual(client.sync(), 'OK') - - # The same file should be present in both client's and the server's - # media directory. - self.assertTrue(filecmp.cmp(join(client.col.media.dir(), "foo.jpg"), - join(server.col.media.dir(), "foo.jpg"))) - - # Syncing client again should do nothing. - self.assertEqual(client.sync(), 'noChanges') - - # Remove files from client's media dir and write changes to its db. - os.remove(join(client.col.media.dir(), "foo.jpg")) - - # TODO: client.col.media.findChanges() doesn't work here - why? - client.col.media._logChanges() - self.assertEqual(client.col.media.syncInfo("foo.jpg"), (None, 1)) - self.assertFalse(isfile(join(client.col.media.dir(), "foo.jpg"))) - - # Syncing client again should work. - self.assertEqual(client.sync(), 'OK') - - # server should have picked up the removal from client. - self.assertEqual(server.col.media.syncInfo("foo.jpg"), (None, 0)) - self.assertFalse(isfile(join(server.col.media.dir(), "foo.jpg"))) - - # Syncing client again should do nothing. - self.assertEqual(client.sync(), 'noChanges') - - def test_sync_compare_database_to_expected(self): - """ - Adds a test image file to the client's media directory. After syncing, - the server's database should, except for timestamps, be identical to a - database containing the expected data. - """ - client = self.client_syncer - - # Add a test image file to the client's media collection but don't - # update its media db since the desktop client updates that, using - # findChanges(), only during syncs. - support_file = helpers.file_utils.get_asset_path('blue.jpg') - self.assertTrue(os.path.isfile(support_file)) - helpers.server_utils.add_files_to_client_mediadb(client.col.media, - [support_file], - update_db=False) - - # Syncing should work. - self.assertEqual(client.sync(), "OK") - - # Create temporary db file with expected results. - chksum = client.col.media._checksum(support_file) - sql = (""" - CREATE TABLE meta (dirMod int, lastUsn int); - - INSERT INTO `meta` (dirMod, lastUsn) VALUES (123456789,1); - - CREATE TABLE media ( - fname text not null primary key, - csum text, - mtime int not null, - dirty int not null - ); - - INSERT INTO `media` (fname, csum, mtime, dirty) VALUES ( - 'blue.jpg', - '%s', - 1441483037, - 0 - ); - - CREATE INDEX idx_media_dirty on media (dirty); - """ % chksum) - - _, dbpath = tempfile.mkstemp(suffix=".anki2") - helpers.db_utils.from_sql(dbpath, sql) - - # Except for timestamps, the client's db after sync should be identical - # to the expected data. - self.assertFalse(self.media_dbs_differ( - client.col.media.db._path, - dbpath - )) - os.unlink(dbpath) - - def test_sync_mediaChanges(self): - client = self.client_syncer - client2 = self.create_client_syncer(self.colutils.create_empty_col(), self.hkey, self.server_test_app) - server = helpers.server_utils.get_syncer_for_hkey(self.server_app, self.hkey, 'media') - self.assertEqual(server.mediaChanges(lastUsn=client.col.media.lastUsn())['data'], []) - - helpers.server_utils.add_files_to_client_mediadb(client.col.media, [ - helpers.file_utils.create_named_file("a", "lastUsn a"), - helpers.file_utils.create_named_file("b", "lastUsn b"), - helpers.file_utils.create_named_file("c", "lastUsn c"), - ], update_db=True) - self.assertEqual(client.sync(), "OK") - self.assertEqual(server.mediaChanges(lastUsn=client.col.media.lastUsn())['data'], []) - - self.assertEqual(client2.sync(), "OK") - os.remove(os.path.join(client2.col.media.dir(), "c")) - client2.col.media._logChanges() - self.assertEqual(client2.sync(), "OK") - self.assertEqual(server.mediaChanges(lastUsn=client.col.media.lastUsn())['data'], [['c', 4, None]]) - self.assertEqual(client.sync(), "OK") - self.assertEqual(server.mediaChanges(lastUsn=client.col.media.lastUsn())['data'], []) - - helpers.server_utils.add_files_to_client_mediadb(client.col.media, [ - helpers.file_utils.create_named_file("d", "lastUsn d"), - ], update_db=True) - client.col.media._logChanges() - self.assertEqual(client.sync(), "OK") - - self.assertEqual(server.mediaChanges(lastUsn=client2.col.media.lastUsn())['data'], [['d', 5, self.file_checksum(os.path.join(server.col.media.dir(), "d"))]]) - - self.assertEqual(client2.sync(), "OK") - self.assertEqual(server.mediaChanges(lastUsn=client2.col.media.lastUsn())['data'], []) - - dpath = os.path.join(client.col.media.dir(), "d") - with open(dpath, "a") as f: - f.write("\nsome change") - # files with the same mtime and name are considered equivalent by anki.media.MediaManager._changes - os.utime(dpath, (315529200, 315529200)) - client.col.media._logChanges() - self.assertEqual(client.sync(), "OK") - self.assertEqual(server.mediaChanges(lastUsn=client2.col.media.lastUsn())['data'], [['d', 6, self.file_checksum(os.path.join(server.col.media.dir(), "d"))]]) - self.assertEqual(client2.sync(), "OK") - self.assertEqual(server.mediaChanges(lastUsn=client2.col.media.lastUsn())['data'], []) - - def test_sync_rename(self): - """ - Adds 3 media files to the client's media directory, syncs and then - renames them and syncs again. After syncing, both the client and the - server should only have the renamed files. - """ - client = self.client_syncer - client2 = self.create_client_syncer(self.colutils.create_empty_col(), self.hkey, self.server_test_app) - server = helpers.server_utils.get_syncer_for_hkey(self.server_app, self.hkey, 'media') - self.assertEqual(server.mediaChanges(lastUsn=client.col.media.lastUsn())['data'], []) - - helpers.server_utils.add_files_to_client_mediadb(client.col.media, [ - helpers.file_utils.create_named_file("a.wav", "lastUsn a"), - helpers.file_utils.create_named_file("b.wav", "lastUsn b"), - helpers.file_utils.create_named_file("c.wav", "lastUsn c"), - ], update_db=True) - self.assertEqual(client.sync(), "OK") - - for fname in os.listdir(client.col.media.dir()): - os.rename( - os.path.join(client.col.media.dir(), fname), - os.path.join(client.col.media.dir(), fname[:1] + ".mp3") - ) - client.col.media._logChanges() - self.assertEqual(client.sync(), "OK") - self.assertEqual( - set(os.listdir(server.col.media.dir())), - {"a.mp3", "b.mp3", "c.mp3"}, - ) - self.assertEqual( - set(os.listdir(client.col.media.dir())), - set(os.listdir(server.col.media.dir())), - ) - self.assertEqual( - list(client.col.media.db.execute("SELECT fname, csum FROM media ORDER BY fname")), - list(server.col.media.db.execute("SELECT fname, csum FROM media ORDER BY fname")), - ) From c5bce6282fc8f3477edc6e4309e506cb456a3c07 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:06:15 +0200 Subject: [PATCH 41/75] Adapted sync app test to latest changes --- tests/test_sync_app.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/test_sync_app.py b/tests/test_sync_app.py index 8e3ff89..d49adec 100644 --- a/tests/test_sync_app.py +++ b/tests/test_sync_app.py @@ -3,9 +3,9 @@ import os import sqlite3 import tempfile import unittest +from unittest.mock import MagicMock, Mock -from anki.consts import SYNC_VER - +from ankisyncd.sync import SYNC_VER from ankisyncd.sync_app import SyncCollectionHandler from ankisyncd.sync_app import SyncUserSession @@ -14,8 +14,13 @@ from collection_test_base import CollectionTestBase class SyncCollectionHandlerTest(CollectionTestBase): def setUp(self): - CollectionTestBase.setUp(self) - self.syncCollectionHandler = SyncCollectionHandler(self.collection) + super().setUp() + self.session = MagicMock() + self.session.name = 'test' + self.syncCollectionHandler = SyncCollectionHandler( + self.collection, + self.session + ) def tearDown(self): CollectionTestBase.tearDown(self) @@ -60,6 +65,7 @@ class SyncCollectionHandlerTest(CollectionTestBase): self.assertTrue((type(meta['ts']) == int) and meta['ts'] > 0) self.assertEqual(meta['mod'], self.collection.mod) self.assertEqual(meta['usn'], self.collection._usn) + self.assertEqual(meta['uname'], self.session.name) self.assertEqual(meta['musn'], self.collection.media.lastUsn()) self.assertEqual(meta['msg'], '') self.assertEqual(meta['cont'], True) From 5f17eb7db9c798922f864d6b80a1bc7feb168ebb Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:07:22 +0200 Subject: [PATCH 42/75] server_utils test helper works with non-expose media db --- tests/helpers/server_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/helpers/server_utils.py b/tests/helpers/server_utils.py index fed41ac..45e6b76 100644 --- a/tests/helpers/server_utils.py +++ b/tests/helpers/server_utils.py @@ -86,5 +86,6 @@ def add_files_to_server_mediadb(media, filepaths): with open(os.path.join(media.dir(), fname), 'wb') as f: f.write(data) - media.db.execute("INSERT INTO media VALUES (?, ?, ?)", fname, media.lastUsn() + 1, csum) - media.db.commit() + media.addMedia( + ((fname, media.lastUsn() + 1, csum),) + ) From e2e756dcda2a08442a9dd37d71336ae04e99968c Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:08:23 +0200 Subject: [PATCH 43/75] Removed references to unused methods --- tests/helpers/monkey_patches.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/tests/helpers/monkey_patches.py b/tests/helpers/monkey_patches.py index 6a9792e..e65fe80 100644 --- a/tests/helpers/monkey_patches.py +++ b/tests/helpers/monkey_patches.py @@ -2,7 +2,7 @@ import os import sqlite3 as sqlite from anki.media import MediaManager -from anki.storage import DB +from anki.db import DB mediamanager_orig_funcs = { "findChanges": None, @@ -26,10 +26,6 @@ def monkeypatch_mediamanager(): def make_cwd_safe(original_func): mediamanager_orig_funcs["findChanges"] = MediaManager.findChanges - mediamanager_orig_funcs["mediaChangesZip"] = MediaManager.mediaChangesZip - mediamanager_orig_funcs["addFilesFromZip"] = MediaManager.addFilesFromZip - mediamanager_orig_funcs["syncDelete"] = MediaManager.syncDelete - mediamanager_orig_funcs["_logChanges"] = MediaManager._logChanges def wrapper(instance, *args): old_cwd = os.getcwd() @@ -42,27 +38,14 @@ def monkeypatch_mediamanager(): return wrapper MediaManager.findChanges = make_cwd_safe(MediaManager.findChanges) - MediaManager.mediaChangesZip = make_cwd_safe(MediaManager.mediaChangesZip) - MediaManager.addFilesFromZip = make_cwd_safe(MediaManager.addFilesFromZip) - MediaManager.syncDelete = make_cwd_safe(MediaManager.syncDelete) - MediaManager._logChanges = make_cwd_safe(MediaManager._logChanges) def unpatch_mediamanager(): """Undoes monkey patches to Anki's MediaManager.""" MediaManager.findChanges = mediamanager_orig_funcs["findChanges"] - MediaManager.mediaChangesZip = mediamanager_orig_funcs["mediaChangesZip"] - MediaManager.addFilesFromZip = mediamanager_orig_funcs["addFilesFromZip"] - MediaManager.syncDelete = mediamanager_orig_funcs["syncDelete"] - MediaManager._logChanges = mediamanager_orig_funcs["_logChanges"] mediamanager_orig_funcs["findChanges"] = None - mediamanager_orig_funcs["mediaChangesZip"] = None - mediamanager_orig_funcs["mediaChangesZip"] = None - mediamanager_orig_funcs["mediaChangesZip"] = None - mediamanager_orig_funcs["_logChanges"] = None - def monkeypatch_db(): """ From 0ef99f3524b5fd389a4b588b6ddf8fe657e95cd0 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:08:54 +0200 Subject: [PATCH 44/75] mock_servers test helper works with new syncer code --- tests/helpers/mock_servers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers/mock_servers.py b/tests/helpers/mock_servers.py index 38442d3..fdd3a18 100644 --- a/tests/helpers/mock_servers.py +++ b/tests/helpers/mock_servers.py @@ -3,7 +3,7 @@ import io import logging import types -from anki.sync import HttpSyncer, RemoteServer, RemoteMediaServer +from ankisyncd.sync import HttpSyncer, RemoteServer, RemoteMediaServer class MockServerConnection: From 60f12cf0a04128a5f9099056c22db49c0b9ce491 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:09:14 +0200 Subject: [PATCH 45/75] file_utils test helper works with new syncer code --- tests/helpers/file_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers/file_utils.py b/tests/helpers/file_utils.py index ad99b5d..28dece3 100644 --- a/tests/helpers/file_utils.py +++ b/tests/helpers/file_utils.py @@ -10,7 +10,7 @@ import tempfile import unicodedata import zipfile -from anki.consts import SYNC_ZIP_SIZE +from ankisyncd.sync import SYNC_ZIP_SIZE def create_named_file(filename, file_contents=None): From 9831159653fb4c5784bb9db27db595f78e0530a1 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:09:59 +0200 Subject: [PATCH 46/75] Close whole collection instead of just the database --- tests/helpers/collection_utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/helpers/collection_utils.py b/tests/helpers/collection_utils.py index 10fcaf5..b8e1231 100644 --- a/tests/helpers/collection_utils.py +++ b/tests/helpers/collection_utils.py @@ -5,7 +5,6 @@ import tempfile from anki import Collection - class CollectionUtils: """ Provides utility methods for creating, inspecting and manipulating anki @@ -26,7 +25,7 @@ class CollectionUtils: file_path = os.path.join(self.tempdir, "collection.anki2") master_col = Collection(file_path) - master_col.db.close() + master_col.close() self.master_db_path = file_path def __enter__(self): From b0d57d3a02f91ffa40d31747caeb7dce27ce29cc Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:10:26 +0200 Subject: [PATCH 47/75] Use wrapped collection in tests That way we make sure, our ServerMediaManager is used instead of the MediaManager --- tests/collection_test_base.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/collection_test_base.py b/tests/collection_test_base.py index 03d05b0..b1da4fd 100644 --- a/tests/collection_test_base.py +++ b/tests/collection_test_base.py @@ -8,6 +8,8 @@ import shutil import anki import anki.storage +from ankisyncd.collection import CollectionManager + class CollectionTestBase(unittest.TestCase): """Parent class for tests that need a collection set up and torn down.""" @@ -15,7 +17,9 @@ class CollectionTestBase(unittest.TestCase): def setUp(self): self.temp_dir = tempfile.mkdtemp() self.collection_path = os.path.join(self.temp_dir, 'collection.anki2'); - self.collection = anki.storage.Collection(self.collection_path) + cm = CollectionManager({}) + collectionWrapper = cm.get_collection(self.collection_path) + self.collection = collectionWrapper._get_collection() self.mock_app = MagicMock() def tearDown(self): From 75c1ea09947dffab4788d0198520d4925d5ee2dd Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:33:08 +0200 Subject: [PATCH 48/75] Added emacs temporary files to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 9061ee6..c3241a4 100644 --- a/.gitignore +++ b/.gitignore @@ -95,6 +95,10 @@ share/python-wheels/ *.egg MANIFEST +# Emacs temporary files +*#*# +*.#* + # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. From 9da0eb77735c9efc81ae5d3955eecb8c25068f0d Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Fri, 28 Aug 2020 20:34:22 +0200 Subject: [PATCH 49/75] Added anki to dependencies --- poetry.lock | 327 +++++++++++++++++++++++++++++++-------- pyproject.toml | 1 + src/requirements-dev.txt | 27 ++-- src/requirements.txt | 5 + 4 files changed, 285 insertions(+), 75 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5c9f4dc..3810d88 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,32 @@ +[[package]] +category = "main" +description = "Anki's library code" +name = "anki" +optional = false +python-versions = ">=3.7" +version = "2.1.32" + +[package.dependencies] +ankirspy = "2.1.32" +beautifulsoup4 = "*" +decorator = "*" +distro = "*" +orjson = "*" +protobuf = "*" +psutil = "*" + +[package.dependencies.requests] +extras = ["socks"] +version = "*" + +[[package]] +category = "main" +description = "Anki's Rust library code Python bindings" +name = "ankirspy" +optional = false +python-versions = "*" +version = "2.1.32" + [[package]] category = "dev" description = "Disable App Nap on OS X 10.9" @@ -7,19 +36,35 @@ optional = false python-versions = "*" version = "0.1.0" +[[package]] +category = "dev" +description = "The secure Argon2 password hashing algorithm." +name = "argon2-cffi" +optional = false +python-versions = "*" +version = "20.1.0" + +[package.dependencies] +cffi = ">=1.0.0" +six = "*" + +[package.extras] +dev = ["coverage (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] +docs = ["sphinx"] +tests = ["coverage (>=5.0.2)", "hypothesis", "pytest"] + [[package]] category = "dev" description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.3.0" +version = "20.1.0" [package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +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"] [[package]] category = "dev" @@ -65,6 +110,17 @@ optional = false python-versions = "*" version = "2020.6.20" +[[package]] +category = "dev" +description = "Foreign Function Interface for Python calling C code." +name = "cffi" +optional = false +python-versions = "*" +version = "1.14.2" + +[package.dependencies] +pycparser = "*" + [[package]] category = "main" description = "Universal encoding detector for Python 2 and 3" @@ -177,8 +233,8 @@ category = "dev" description = "IPython: Productive Interactive Computing" name = "ipython" optional = false -python-versions = ">=3.6" -version = "7.16.1" +python-versions = ">=3.7" +version = "7.17.0" [package.dependencies] appnope = "*" @@ -326,7 +382,7 @@ description = "Jupyter protocol implementation and client libraries" name = "jupyter-client" optional = false python-versions = ">=3.5" -version = "6.1.6" +version = "6.1.7" [package.dependencies] jupyter-core = ">=4.6.0" @@ -336,7 +392,7 @@ tornado = ">=4.1" traitlets = "*" [package.extras] -test = ["async-generator", "ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "pytest-timeout"] +test = ["ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "async-generator", "pytest-timeout"] [[package]] category = "dev" @@ -374,7 +430,7 @@ description = "The JupyterLab notebook server extension." name = "jupyterlab" optional = false python-versions = ">=3.5" -version = "2.2.2" +version = "2.2.6" [package.dependencies] jinja2 = ">=2.10" @@ -410,7 +466,7 @@ description = "Python LiveReload is an awesome tool for web developers" name = "livereload" optional = false python-versions = "*" -version = "2.6.2" +version = "2.6.3" [package.dependencies] six = "*" @@ -565,10 +621,11 @@ description = "A web-based notebook environment for interactive computing" name = "notebook" optional = false python-versions = ">=3.5" -version = "6.0.3" +version = "6.1.3" [package.dependencies] Send2Trash = "*" +argon2-cffi = "*" ipykernel = "*" ipython-genutils = "*" jinja2 = "*" @@ -578,12 +635,22 @@ nbconvert = "*" nbformat = "*" prometheus-client = "*" pyzmq = ">=17" -terminado = ">=0.8.1" +terminado = ">=0.8.3" tornado = ">=5.0" traitlets = ">=4.2.1" [package.extras] -test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "nose-exclude"] +docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt"] +test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "requests-unixsocket"] + +[[package]] +category = "main" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +marker = "platform_machine == \"x86_64\"" +name = "orjson" +optional = false +python-versions = ">=3.6" +version = "3.3.1" [[package]] category = "dev" @@ -653,11 +720,23 @@ description = "Library for building powerful interactive command lines in Python name = "prompt-toolkit" optional = false python-versions = ">=3.6.1" -version = "3.0.5" +version = "3.0.6" [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." @@ -686,6 +765,14 @@ optional = false python-versions = "*" version = "0.2.11" +[[package]] +category = "dev" +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 = "dev" description = "Pygments is a syntax highlighting package written in Python." @@ -756,7 +843,7 @@ description = "Python bindings for 0MQ" name = "pyzmq" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -version = "19.0.1" +version = "19.0.2" [[package]] category = "dev" @@ -764,7 +851,7 @@ description = "Jupyter Qt console" name = "qtconsole" optional = false python-versions = "*" -version = "4.7.5" +version = "4.7.6" [package.dependencies] ipykernel = ">=4.1" @@ -824,7 +911,7 @@ python-versions = "*" version = "1.5.0" [[package]] -category = "dev" +category = "main" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false @@ -878,7 +965,7 @@ marker = "python_version > \"2.7\"" name = "tqdm" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.48.0" +version = "4.48.2" [package.extras] dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] @@ -965,17 +1052,47 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "256b39b0726f0028059bd4d3a895cfe5a0676284c57a7615e6178734caa70227" +content-hash = "85d03342e458196cc35e890733a1dd3c48a504cda333b46114dd57c58b42c9b6" +lock-version = "1.0" python-versions = "^3.7" [metadata.files] +anki = [ + {file = "anki-2.1.32-py3-none-any.whl", hash = "sha256:97cfc292876196572b3d037ab218e3c9014ec7b31744c82e9847a45e796e3fdd"}, +] +ankirspy = [ + {file = "ankirspy-2.1.32-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:6cd446155ee56f2557ecee6cfa42857ef44f4e5322a9fd5a06ff25a3bffc6980"}, + {file = "ankirspy-2.1.32-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:59cf16a23f7afabfe011302ae47833c13d57fcbfd7bbf9e2ff78c52cbffea106"}, + {file = "ankirspy-2.1.32-cp37-none-win_amd64.whl", hash = "sha256:e5d133cda5a849a5734cd12d3e7d29f34907116e97712d70c895232cbba9a802"}, + {file = "ankirspy-2.1.32-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:8358846c61b575b163fb12bfcb28ba12d44611606f04eef7230f374f9c31c2a4"}, + {file = "ankirspy-2.1.32-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ce71ae0e9695246cc58bd6c51b3ca5d8958a32fa3cee77843eb1ed95a35739ff"}, + {file = "ankirspy-2.1.32-cp38-none-win_amd64.whl", hash = "sha256:1962126aaf72b678bde10bebb5108f988d6888be35870c46ec2e14af7fedee1e"}, +] appnope = [ {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, ] +argon2-cffi = [ + {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05a8ac07c7026542377e38389638a8a1e9b78f1cd8439cd7493b39f08dd75fbf"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-win32.whl", hash = "sha256:0bf066bc049332489bb2d75f69216416329d9dc65deee127152caeb16e5ce7d5"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:57358570592c46c420300ec94f2ff3b32cbccd10d38bdc12dc6979c4a8484fbc"}, + {file = "argon2_cffi-20.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7d455c802727710e9dfa69b74ccaab04568386ca17b0ad36350b622cd34606fe"}, + {file = "argon2_cffi-20.1.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b160416adc0f012fb1f12588a5e6954889510f82f698e23ed4f4fa57f12a0647"}, + {file = "argon2_cffi-20.1.0-cp35-cp35m-win32.whl", hash = "sha256:9bee3212ba4f560af397b6d7146848c32a800652301843df06b9e8f68f0f7361"}, + {file = "argon2_cffi-20.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:392c3c2ef91d12da510cfb6f9bae52512a4552573a9e27600bdb800e05905d2b"}, + {file = "argon2_cffi-20.1.0-cp36-cp36m-win32.whl", hash = "sha256:ba7209b608945b889457f949cc04c8e762bed4fe3fec88ae9a6b7765ae82e496"}, + {file = "argon2_cffi-20.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:da7f0445b71db6d3a72462e04f36544b0de871289b0bc8a7cc87c0f5ec7079fa"}, + {file = "argon2_cffi-20.1.0-cp37-abi3-macosx_10_6_intel.whl", hash = "sha256:cc0e028b209a5483b6846053d5fd7165f460a1f14774d79e632e75e7ae64b82b"}, + {file = "argon2_cffi-20.1.0-cp37-cp37m-win32.whl", hash = "sha256:18dee20e25e4be86680b178b35ccfc5d495ebd5792cd00781548d50880fee5c5"}, + {file = "argon2_cffi-20.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6678bb047373f52bcff02db8afab0d2a77d83bde61cfecea7c5c62e2335cb203"}, + {file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"}, + {file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"}, +] attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, + {file = "attrs-20.1.0-py2.py3-none-any.whl", hash = "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"}, + {file = "attrs-20.1.0.tar.gz", hash = "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a"}, ] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, @@ -994,6 +1111,36 @@ certifi = [ {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, ] +cffi = [ + {file = "cffi-1.14.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82"}, + {file = "cffi-1.14.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4"}, + {file = "cffi-1.14.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e"}, + {file = "cffi-1.14.2-cp27-cp27m-win32.whl", hash = "sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c"}, + {file = "cffi-1.14.2-cp27-cp27m-win_amd64.whl", hash = "sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1"}, + {file = "cffi-1.14.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7"}, + {file = "cffi-1.14.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c"}, + {file = "cffi-1.14.2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731"}, + {file = "cffi-1.14.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0"}, + {file = "cffi-1.14.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e"}, + {file = "cffi-1.14.2-cp35-cp35m-win32.whl", hash = "sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487"}, + {file = "cffi-1.14.2-cp35-cp35m-win_amd64.whl", hash = "sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad"}, + {file = "cffi-1.14.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2"}, + {file = "cffi-1.14.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123"}, + {file = "cffi-1.14.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1"}, + {file = "cffi-1.14.2-cp36-cp36m-win32.whl", hash = "sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281"}, + {file = "cffi-1.14.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4"}, + {file = "cffi-1.14.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798"}, + {file = "cffi-1.14.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4"}, + {file = "cffi-1.14.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f"}, + {file = "cffi-1.14.2-cp37-cp37m-win32.whl", hash = "sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650"}, + {file = "cffi-1.14.2-cp37-cp37m-win_amd64.whl", hash = "sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15"}, + {file = "cffi-1.14.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa"}, + {file = "cffi-1.14.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c"}, + {file = "cffi-1.14.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75"}, + {file = "cffi-1.14.2-cp38-cp38-win32.whl", hash = "sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e"}, + {file = "cffi-1.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c"}, + {file = "cffi-1.14.2.tar.gz", hash = "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b"}, +] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, @@ -1038,8 +1185,8 @@ ipykernel = [ {file = "ipykernel-5.3.4.tar.gz", hash = "sha256:9b2652af1607986a1b231c62302d070bc0534f564c393a5d9d130db9abbbe89d"}, ] ipython = [ - {file = "ipython-7.16.1-py3-none-any.whl", hash = "sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64"}, - {file = "ipython-7.16.1.tar.gz", hash = "sha256:9f4fcb31d3b2c533333893b9172264e4821c1ac91839500f31bd43f2c59b3ccf"}, + {file = "ipython-7.17.0-py3-none-any.whl", hash = "sha256:5a8f159ca8b22b9a0a1f2a28befe5ad2b703339afb58c2ffe0d7c8d7a3af5999"}, + {file = "ipython-7.17.0.tar.gz", hash = "sha256:b70974aaa2674b05eb86a910c02ed09956a33f2dd6c71afc60f0b128a77e7f28"}, ] ipython-genutils = [ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, @@ -1075,8 +1222,8 @@ jupyter = [ {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"}, ] jupyter-client = [ - {file = "jupyter_client-6.1.6-py3-none-any.whl", hash = "sha256:7ad9aa91505786420d77edc5f9fb170d51050c007338ba8d196f603223fd3b3a"}, - {file = "jupyter_client-6.1.6.tar.gz", hash = "sha256:b360f8d4638bc577a4656e93f86298db755f915098dc763f6fc05da0c5d7a595"}, + {file = "jupyter_client-6.1.7-py3-none-any.whl", hash = "sha256:c958d24d6eacb975c1acebb68ac9077da61b5f5c040f22f6849928ad7393b950"}, + {file = "jupyter_client-6.1.7.tar.gz", hash = "sha256:49e390b36fe4b4226724704ea28d9fb903f1a3601b6882ce3105221cd09377a1"}, ] jupyter-console = [ {file = "jupyter_console-6.1.0-py2.py3-none-any.whl", hash = "sha256:b392155112ec86a329df03b225749a0fa903aa80811e8eda55796a40b5e470d8"}, @@ -1087,15 +1234,15 @@ jupyter-core = [ {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, ] jupyterlab = [ - {file = "jupyterlab-2.2.2-py3-none-any.whl", hash = "sha256:d0d743ea75b8eee20a18b96ccef24f76ee009bafb2617f3f330698fe3a00026e"}, - {file = "jupyterlab-2.2.2.tar.gz", hash = "sha256:8aa9bc4b5020e7b9ec6e006d516d48bddf7d2528680af65840464ee722d59db3"}, + {file = "jupyterlab-2.2.6-py3-none-any.whl", hash = "sha256:ae557386633fcb74359f436f2b87788a451260a07f2f14a1880fca8f4a9f64de"}, + {file = "jupyterlab-2.2.6.tar.gz", hash = "sha256:6554b022d2cd120100e165ec537c6511d70de7f89e253b3c667ea28f2a9263ff"}, ] jupyterlab-server = [ {file = "jupyterlab_server-1.2.0-py3-none-any.whl", hash = "sha256:55d256077bf13e5bc9e8fbd5aac51bef82f6315111cec6b712b9a5ededbba924"}, {file = "jupyterlab_server-1.2.0.tar.gz", hash = "sha256:5431d9dde96659364b7cc877693d5d21e7b80cea7ae3959ecc2b87518e5f5d8c"}, ] livereload = [ - {file = "livereload-2.6.2.tar.gz", hash = "sha256:d1eddcb5c5eb8d2ca1fa1f750e580da624c0f7fcb734aa5780dc81b7dcbd89be"}, + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, ] lunr = [ {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, @@ -1133,6 +1280,11 @@ markupsafe = [ {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"}, ] mistune = [ @@ -1155,8 +1307,28 @@ nltk = [ {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, ] notebook = [ - {file = "notebook-6.0.3-py3-none-any.whl", hash = "sha256:3edc616c684214292994a3af05eaea4cc043f6b4247d830f3a2f209fa7639a80"}, - {file = "notebook-6.0.3.tar.gz", hash = "sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48"}, + {file = "notebook-6.1.3-py3-none-any.whl", hash = "sha256:964cc40cff68e473f3778aef9266e867f7703cb4aebdfd250f334efe02f64c86"}, + {file = "notebook-6.1.3.tar.gz", hash = "sha256:9990d51b9931a31e681635899aeb198b4c4b41586a9e87fbfaaed1a71d0a05b6"}, +] +orjson = [ + {file = "orjson-3.3.1-cp36-cp36m-macosx_10_7_x86_64.whl", hash = "sha256:0f33d28083819579976669f54ca79675d8e95fd5d75e7db21b798354ed8dd15b"}, + {file = "orjson-3.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4c290f1c0b6665d60181ee2f0ef631640d04ead2002ca4eadce4991ea5d6a4ed"}, + {file = "orjson-3.3.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf542f372162533550e86003d48664ab5fc1b44fb2b88923b9794cc8db6f0cf0"}, + {file = "orjson-3.3.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:28e6116ebd2082357bb9c66a76a3a1dc6aa4de0754801ac10b9903d31b752a1b"}, + {file = "orjson-3.3.1-cp36-none-win_amd64.whl", hash = "sha256:c4ac5a1d1767733708fd9b45cbbab3f8871af57b54b707a2dc6fddb47e51a81a"}, + {file = "orjson-3.3.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:0f11fd620b74fbdcf29021b3a9c36fb6e13efcdd63cbacc292d0786b54b4b2e8"}, + {file = "orjson-3.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e455c5b42a023f4777526c623d2e9ae415084de5130f93aefe689ea482de5f67"}, + {file = "orjson-3.3.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8c90083c67653d88b132820719e604250f26ba04229efe3149bf82ba2a08f8cf"}, + {file = "orjson-3.3.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:bc23eed41167b4454cddd51f72a7ee4163c33565c509bb9469adf56384b1cce2"}, + {file = "orjson-3.3.1-cp37-none-win_amd64.whl", hash = "sha256:3bff4765281da6fa8ddbbe692e5061f950d11aabdfe64837fb53ead4756e9af6"}, + {file = "orjson-3.3.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:1e19907c1ccf82976c2d111f3914a2c0697720b91908e8ef02405e4dc21c662a"}, + {file = "orjson-3.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:aa8332a3ee0fa03a331bea4f28cdcc4d363b53af2ea41630d7eb580422514a1f"}, + {file = "orjson-3.3.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:4ab9536c3776136303ab9e6432691d970e6aa5d27dbc2b5e0ca0d0db3e12f1c4"}, + {file = "orjson-3.3.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:28dc7e1f89440a68c1ccb937f6f0ae40fa3875de84f747262c00bc18aa25c5ec"}, + {file = "orjson-3.3.1-cp38-none-win_amd64.whl", hash = "sha256:fa4d5d734e76d9f21a94444fbf1de7eea185b355b324d38c8a7456ce63c3bbeb"}, + {file = "orjson-3.3.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b0533d6719b781db7563c478672d91faeac9ea810f30f16ebb5e917c4451b098"}, + {file = "orjson-3.3.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:a7d634eb69083ca5a49baf412625604813f9e3365cb869f445c388d15fe60122"}, + {file = "orjson-3.3.1.tar.gz", hash = "sha256:149d6a2bc71514826979b9d053f3df0c2397a99e2b87213ba71605a1626d662c"}, ] packaging = [ {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, @@ -1182,8 +1354,28 @@ prometheus-client = [ {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.5-py3-none-any.whl", hash = "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04"}, - {file = "prompt_toolkit-3.0.5.tar.gz", hash = "sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8"}, + {file = "prompt_toolkit-3.0.6-py3-none-any.whl", hash = "sha256:683397077a64cd1f750b71c05afcfc6612a7300cb6932666531e5a54f38ea564"}, + {file = "prompt_toolkit-3.0.6.tar.gz", hash = "sha256:7630ab85a23302839a0f26b31cc24f518e6155dea1ed395ea61b42c45941b6a6"}, +] +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"}, @@ -1213,6 +1405,10 @@ pyaudio = [ {file = "PyAudio-0.2.11-cp36-cp36m-win_amd64.whl", hash = "sha256:2a19bdb8ec1445b4f3e4b7b109e0e4cec1fd1f1ce588592aeb6db0b58d4fb3b0"}, {file = "PyAudio-0.2.11.tar.gz", hash = "sha256:93bfde30e0b64e63a46f2fd77e85c41fd51182a4a3413d9edfaf9ffaa26efb74"}, ] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] pygments = [ {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, @@ -1268,38 +1464,38 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] pyzmq = [ - {file = "pyzmq-19.0.1-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:58688a2dfa044fad608a8e70ba8d019d0b872ec2acd75b7b5e37da8905605891"}, - {file = "pyzmq-19.0.1-cp27-cp27m-win32.whl", hash = "sha256:87c78f6936e2654397ca2979c1d323ee4a889eef536cc77a938c6b5be33351a7"}, - {file = "pyzmq-19.0.1-cp27-cp27m-win_amd64.whl", hash = "sha256:97b6255ae77328d0e80593681826a0479cb7bac0ba8251b4dd882f5145a2293a"}, - {file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:15b4cb21118f4589c4db8be4ac12b21c8b4d0d42b3ee435d47f686c32fe2e91f"}, - {file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:931339ac2000d12fe212e64f98ce291e81a7ec6c73b125f17cf08415b753c087"}, - {file = "pyzmq-19.0.1-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:2a88b8fabd9cc35bd59194a7723f3122166811ece8b74018147a4ed8489e6421"}, - {file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:bafd651b557dd81d89bd5f9c678872f3e7b7255c1c751b78d520df2caac80230"}, - {file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8952f6ba6ae598e792703f3134af5a01af8f5c7cf07e9a148f05a12b02412cea"}, - {file = "pyzmq-19.0.1-cp35-cp35m-win32.whl", hash = "sha256:54aa24fd60c4262286fc64ca632f9e747c7cc3a3a1144827490e1dc9b8a3a960"}, - {file = "pyzmq-19.0.1-cp35-cp35m-win_amd64.whl", hash = "sha256:dcbc3f30c11c60d709c30a213dc56e88ac016fe76ac6768e64717bd976072566"}, - {file = "pyzmq-19.0.1-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:6ca519309703e95d55965735a667809bbb65f52beda2fdb6312385d3e7a6d234"}, - {file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4ee0bfd82077a3ff11c985369529b12853a4064320523f8e5079b630f9551448"}, - {file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ba6f24431b569aec674ede49cad197cad59571c12deed6ad8e3c596da8288217"}, - {file = "pyzmq-19.0.1-cp36-cp36m-win32.whl", hash = "sha256:956775444d01331c7eb412c5fb9bb62130dfaac77e09f32764ea1865234e2ca9"}, - {file = "pyzmq-19.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b08780e3a55215873b3b8e6e7ca8987f14c902a24b6ac081b344fd430d6ca7cd"}, - {file = "pyzmq-19.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21f7d91f3536f480cb2c10d0756bfa717927090b7fb863e6323f766e5461ee1c"}, - {file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bfff5ffff051f5aa47ba3b379d87bd051c3196b0c8a603e8b7ed68a6b4f217ec"}, - {file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:07fb8fe6826a229dada876956590135871de60dbc7de5a18c3bcce2ed1f03c98"}, - {file = "pyzmq-19.0.1-cp37-cp37m-win32.whl", hash = "sha256:342fb8a1dddc569bc361387782e8088071593e7eaf3e3ecf7d6bd4976edff112"}, - {file = "pyzmq-19.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:faee2604f279d31312bc455f3d024f160b6168b9c1dde22bf62d8c88a4deca8e"}, - {file = "pyzmq-19.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b9d21fc56c8aacd2e6d14738021a9d64f3f69b30578a99325a728e38a349f85"}, - {file = "pyzmq-19.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af0c02cf49f4f9eedf38edb4f3b6bb621d83026e7e5d76eb5526cc5333782fd6"}, - {file = "pyzmq-19.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5f1f2eb22aab606f808163eb1d537ac9a0ba4283fbeb7a62eb48d9103cf015c2"}, - {file = "pyzmq-19.0.1-cp38-cp38-win32.whl", hash = "sha256:f9d7e742fb0196992477415bb34366c12e9bb9a0699b8b3f221ff93b213d7bec"}, - {file = "pyzmq-19.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:5b99c2ae8089ef50223c28bac57510c163bfdff158c9e90764f812b94e69a0e6"}, - {file = "pyzmq-19.0.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:cf5d689ba9513b9753959164cf500079383bc18859f58bf8ce06d8d4bef2b054"}, - {file = "pyzmq-19.0.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:aaa8b40b676576fd7806839a5de8e6d5d1b74981e6376d862af6c117af2a3c10"}, - {file = "pyzmq-19.0.1.tar.gz", hash = "sha256:13a5638ab24d628a6ade8f794195e1a1acd573496c3b85af2f1183603b7bf5e0"}, + {file = "pyzmq-19.0.2-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:59f1e54627483dcf61c663941d94c4af9bf4163aec334171686cdaee67974fe5"}, + {file = "pyzmq-19.0.2-cp27-cp27m-win32.whl", hash = "sha256:c36ffe1e5aa35a1af6a96640d723d0d211c5f48841735c2aa8d034204e87eb87"}, + {file = "pyzmq-19.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:0a422fc290d03958899743db091f8154958410fc76ce7ee0ceb66150f72c2c97"}, + {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c20dd60b9428f532bc59f2ef6d3b1029a28fc790d408af82f871a7db03e722ff"}, + {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d46fb17f5693244de83e434648b3dbb4f4b0fec88415d6cbab1c1452b6f2ae17"}, + {file = "pyzmq-19.0.2-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:f1a25a61495b6f7bb986accc5b597a3541d9bd3ef0016f50be16dbb32025b302"}, + {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ab0d01148d13854de716786ca73701012e07dff4dfbbd68c4e06d8888743526e"}, + {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:720d2b6083498a9281eaee3f2927486e9fe02cd16d13a844f2e95217f243efea"}, + {file = "pyzmq-19.0.2-cp35-cp35m-win32.whl", hash = "sha256:29d51279060d0a70f551663bc592418bcad7f4be4eea7b324f6dd81de05cb4c1"}, + {file = "pyzmq-19.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:5120c64646e75f6db20cc16b9a94203926ead5d633de9feba4f137004241221d"}, + {file = "pyzmq-19.0.2-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:8a6ada5a3f719bf46a04ba38595073df8d6b067316c011180102ba2a1925f5b5"}, + {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fa411b1d8f371d3a49d31b0789eb6da2537dadbb2aef74a43aa99a78195c3f76"}, + {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:00dca814469436455399660247d74045172955459c0bd49b54a540ce4d652185"}, + {file = "pyzmq-19.0.2-cp36-cp36m-win32.whl", hash = "sha256:046b92e860914e39612e84fa760fc3f16054d268c11e0e25dcb011fb1bc6a075"}, + {file = "pyzmq-19.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99cc0e339a731c6a34109e5c4072aaa06d8e32c0b93dc2c2d90345dd45fa196c"}, + {file = "pyzmq-19.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e36f12f503511d72d9bdfae11cadbadca22ff632ff67c1b5459f69756a029c19"}, + {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c40fbb2b9933369e994b837ee72193d6a4c35dfb9a7c573257ef7ff28961272c"}, + {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5d9fc809aa8d636e757e4ced2302569d6e60e9b9c26114a83f0d9d6519c40493"}, + {file = "pyzmq-19.0.2-cp37-cp37m-win32.whl", hash = "sha256:3fa6debf4bf9412e59353defad1f8035a1e68b66095a94ead8f7a61ae90b2675"}, + {file = "pyzmq-19.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:73483a2caaa0264ac717af33d6fb3f143d8379e60a422730ee8d010526ce1913"}, + {file = "pyzmq-19.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36ab114021c0cab1a423fe6689355e8f813979f2c750968833b318c1fa10a0fd"}, + {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8b66b94fe6243d2d1d89bca336b2424399aac57932858b9a30309803ffc28112"}, + {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:654d3e06a4edc566b416c10293064732516cf8871a4522e0a2ba00cc2a2e600c"}, + {file = "pyzmq-19.0.2-cp38-cp38-win32.whl", hash = "sha256:276ad604bffd70992a386a84bea34883e696a6b22e7378053e5d3227321d9702"}, + {file = "pyzmq-19.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:09d24a80ccb8cbda1af6ed8eb26b005b6743e58e9290566d2a6841f4e31fa8e0"}, + {file = "pyzmq-19.0.2-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:c1a31cd42905b405530e92bdb70a8a56f048c8a371728b8acf9d746ecd4482c0"}, + {file = "pyzmq-19.0.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7e7f930039ee0c4c26e4dfee015f20bd6919cd8b97c9cd7afbde2923a5167b6"}, + {file = "pyzmq-19.0.2.tar.gz", hash = "sha256:296540a065c8c21b26d63e3cea2d1d57902373b16e4256afe46422691903a438"}, ] qtconsole = [ - {file = "qtconsole-4.7.5-py2.py3-none-any.whl", hash = "sha256:4f43d0b049eacb7d723772847f0c465feccce0ccb398871a6e146001a22bad23"}, - {file = "qtconsole-4.7.5.tar.gz", hash = "sha256:f5cb275d30fc8085e2d1d18bc363e5ba0ce6e559bf37d7d6727b773134298754"}, + {file = "qtconsole-4.7.6-py2.py3-none-any.whl", hash = "sha256:570b9e1dd4f9b727699b0ed04c6943d9d32d5a2085aa69d82d814e039bbcf74b"}, + {file = "qtconsole-4.7.6.tar.gz", hash = "sha256:6c24397c19a49a5cf69582c931db4b0f6b00a78530a2bfd122936f2ebfae2fef"}, ] qtpy = [ {file = "QtPy-1.9.0-py2.py3-none-any.whl", hash = "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea"}, @@ -1323,6 +1519,7 @@ regex = [ {file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, {file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, {file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, + {file = "regex-2020.7.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a"}, {file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, @@ -1363,8 +1560,8 @@ tornado = [ {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, ] tqdm = [ - {file = "tqdm-4.48.0-py2.py3-none-any.whl", hash = "sha256:fcb7cb5b729b60a27f300b15c1ffd4744f080fb483b88f31dc8654b082cc8ea5"}, - {file = "tqdm-4.48.0.tar.gz", hash = "sha256:6baa75a88582b1db6d34ce4690da5501d2a1cb65c34664840a456b2c9f794d29"}, + {file = "tqdm-4.48.2-py2.py3-none-any.whl", hash = "sha256:1a336d2b829be50e46b84668691e0a2719f26c97c62846298dd5ae2937e4d5cf"}, + {file = "tqdm-4.48.2.tar.gz", hash = "sha256:564d632ea2b9cb52979f7956e093e831c28d441c11751682f84c86fc46e4fd21"}, ] traitlets = [ {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, diff --git a/pyproject.toml b/pyproject.toml index a5ecd48..f776a08 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ authors = ["Vikash Kothary "] [tool.poetry.dependencies] python = "^3.7" +anki = "^2.1.32" beautifulsoup4 = "^4.9.1" requests = "^2.24.0" markdown = "^3.2.2" diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 44b3e2b..49dca27 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -1,9 +1,13 @@ +anki==2.1.32 +ankirspy==2.1.32 appnope==0.1.0; sys_platform == "darwin" or platform_system == "Darwin" or python_version >= "3.3" and sys_platform == "darwin" -attrs==19.3.0 +argon2-cffi==20.1.0 +attrs==20.1.0 backcall==0.2.0 beautifulsoup4==4.9.1 bleach==3.1.5 certifi==2020.6.20 +cffi==1.14.2 chardet==3.0.4 click==7.1.2 colorama==0.4.3; python_version >= "3.3" and sys_platform == "win32" or sys_platform == "win32" @@ -15,7 +19,7 @@ future==0.18.2 idna==2.10 importlib-metadata==1.7.0; python_version < "3.8" ipykernel==5.3.4 -ipython==7.16.1 +ipython==7.17.0 ipython-genutils==0.2.0 ipywidgets==7.5.1 jedi==0.17.2 @@ -24,12 +28,12 @@ joblib==0.16.0; python_version > "2.7" json5==0.9.5 jsonschema==3.2.0 jupyter==1.0.0 -jupyter-client==6.1.6 +jupyter-client==6.1.7 jupyter-console==6.1.0 jupyter-core==4.6.3 -jupyterlab==2.2.2 +jupyterlab==2.2.6 jupyterlab-server==1.2.0 -livereload==2.6.2 +livereload==2.6.3 lunr==0.5.8 markdown==3.2.2 markupsafe==1.1.1 @@ -38,17 +42,20 @@ mkdocs==1.1.2 nbconvert==5.6.1 nbformat==5.0.7 nltk==3.5; python_version > "2.7" -notebook==6.0.3 +notebook==6.1.3 +orjson==3.3.1; platform_machine == "x86_64" packaging==20.4 pandocfilters==1.4.2 parso==0.7.1 pexpect==4.8.0; python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32" pickleshare==0.7.5 prometheus-client==0.8.0 -prompt-toolkit==3.0.5 +prompt-toolkit==3.0.6 +protobuf==3.13.0 psutil==5.7.2 ptyprocess==0.6.0; sys_platform != "win32" or os_name != "nt" or python_version >= "3.3" and sys_platform != "win32" pyaudio==0.2.11 +pycparser==2.20 pygments==2.6.1 pyparsing==2.4.7 pyrsistent==0.16.0 @@ -56,8 +63,8 @@ python-dateutil==2.8.1 pywin32==228; sys_platform == "win32" pywinpty==0.5.7; os_name == "nt" pyyaml==5.3.1 -pyzmq==19.0.1 -qtconsole==4.7.5 +pyzmq==19.0.2 +qtconsole==4.7.6 qtpy==1.9.0 regex==2020.7.14; python_version > "2.7" requests==2.24.0 @@ -67,7 +74,7 @@ soupsieve==1.9.6 terminado==0.8.3 testpath==0.4.4 tornado==6.0.4 -tqdm==4.48.0; python_version > "2.7" +tqdm==4.48.2; python_version > "2.7" traitlets==4.3.3 urllib3==1.25.10 wcwidth==0.2.5 diff --git a/src/requirements.txt b/src/requirements.txt index f66853b..3a1dc74 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,3 +1,5 @@ +anki==2.1.32 +ankirspy==2.1.32 beautifulsoup4==4.9.1 certifi==2020.6.20 chardet==3.0.4 @@ -6,10 +8,13 @@ distro==1.5.0 idna==2.10 importlib-metadata==1.7.0; python_version < "3.8" markdown==3.2.2 +orjson==3.3.1; platform_machine == "x86_64" +protobuf==3.13.0 psutil==5.7.2 pyaudio==0.2.11 requests==2.24.0 send2trash==1.5.0 +six==1.15.0 soupsieve==1.9.6 urllib3==1.25.10 webob==1.8.6 From 70bfaa171970dffd1d0cfb9ac774fda474785338 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Mon, 31 Aug 2020 16:50:53 +0200 Subject: [PATCH 50/75] Further clarified configuration for Nginx proxy --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7aa73fc..06c8c84 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ankisyncd -========= +11;rgb:2323/2727/2929========= [![Documentation Status](https://readthedocs.org/projects/anki-sync-server/badge/?version=latest)](https://anki-sync-server.readthedocs.io/?badge=latest) [![Gitter](https://badges.gitter.im/ankicommunity/community.svg)](https://gitter.im/ankicommunity/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) @@ -52,20 +52,31 @@ Installing $ ./ankisyncctl.py adduser 4. Setup a proxy to unchunk the requests. -Webob does not support the header "Transfer-Encoding: chunked" used by Anki and -therefore ankisyncd sees chunked requests as empty. To solve this problem setup -Nginx (or any other webserver of your choice) and configure it to "unchunk" the -requests to ankisyncd: + Webob does not support the header "Transfer-Encoding: chunked" used by Anki + and therefore ankisyncd sees chunked requests as empty. To solve this problem + setup Nginx (or any other webserver of your choice) and configure it to + "unchunk" the requests for ankisyncd. + + For example, if you use Nginx on the same machine as ankisyncd, you first + have to change the port in `ankisyncd.conf` to something other than `27701`. + Then configure Nginx to listen on port `27701` and forward the unchunked + requests to ankisyncd. + + An example configuration with ankisyncd running on the same machine as Nginx + and listening on port `27702` may look like: + + ``` server { listen 27701; server_name default; location / { proxy_http_version 1.0; - proxy_pass http://ankisyncd:27701/; + proxy_pass http://localhost:27702/; } } + ``` 5. Run ankisyncd: From 6f29fce600f2dcdc496eab4cfc5f3dc566e0861c Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Tue, 1 Sep 2020 23:53:59 +0200 Subject: [PATCH 51/75] replaced relative with absolute import --- src/ankisyncd/sync_app.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 3cbe3aa..90e70a2 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -38,11 +38,10 @@ import anki.db import anki.utils from anki.consts import REM_CARD, REM_NOTE -from ankisyncd.users import get_user_manager -from ankisyncd.sessions import get_session_manager from ankisyncd.full_sync import get_full_sync_manager - -from .sync import Syncer, SYNC_VER, SYNC_ZIP_SIZE, SYNC_ZIP_COUNT +from ankisyncd.sessions import get_session_manager +from ankisyncd.sync import Syncer, SYNC_VER, SYNC_ZIP_SIZE, SYNC_ZIP_COUNT +from ankisyncd.users import get_user_manager logger = logging.getLogger("ankisyncd") From 3ec37ec80f4a7147b908c3fc104596fcfc19c45c Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Tue, 1 Sep 2020 23:56:44 +0200 Subject: [PATCH 52/75] Removed artifact from README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06c8c84..c33876f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ankisyncd -11;rgb:2323/2727/2929========= +========= [![Documentation Status](https://readthedocs.org/projects/anki-sync-server/badge/?version=latest)](https://anki-sync-server.readthedocs.io/?badge=latest) [![Gitter](https://badges.gitter.im/ankicommunity/community.svg)](https://gitter.im/ankicommunity/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) From 65e9bbf747c3d5d9af428f216b160e492588b607 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 2 Sep 2020 00:03:58 +0200 Subject: [PATCH 53/75] Updated docs about installation of requirements --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index c33876f..1c8e75c 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,10 @@ It supports Python 3 and Anki 2.1. Installing ---------- - -0. Install the current version of Anki. 1. Install the dependencies: - $ pip install webob + $ pip install -r src/requirements.txt 2. Modify ankisyncd.conf according to your needs From 7deda95d77b488b26242b64aafa5ebd709fa1e24 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 2 Sep 2020 00:07:57 +0200 Subject: [PATCH 54/75] Removed whole section about ankisyncd without pyaudio from the README --- README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.md b/README.md index 1c8e75c..61dd905 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,6 @@ It supports Python 3 and Anki 2.1. - [Anki 2.1](#anki-21) - [Anki 2.0](#anki-20) - [AnkiDroid](#ankidroid) - - [Running `ankisyncd` without `pyaudio`](#running-ankisyncd-without-pyaudio) - - [Anki ≥2.1.9](#anki-219) - - [Older versions](#older-versions) - [ENVVAR configuration overrides](#envvar-configuration-overrides) - [Support for other database backends](#support-for-other-database-backends) @@ -142,13 +139,6 @@ Even though the AnkiDroid interface will request an email address, this is not required; it will simply be the username you configured with `ankisyncctl.py adduser`. -Running `ankisyncd` without `pyaudio` -------------------------------------- - -`ankisyncd` doesn't use the audio recording feature of Anki, so if you don't -want to install PortAudio, you can edit some files in the `anki-bundled` -directory to exclude `pyaudio`: - ENVVAR configuration overrides ------------------------------ From 614f209f98d54fa0c1eb6724fa39a4440a54cf16 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 2 Sep 2020 18:30:56 +0200 Subject: [PATCH 55/75] Removed unused post- and prehooks --- src/ankisyncd/sync_app.py | 53 --------------------------------------- 1 file changed, 53 deletions(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 90e70a2..d28bd6a 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -396,9 +396,6 @@ class SyncApp: self.base_media_url = config['base_media_url'] self.setup_new_collection = None - self.prehooks = {} - self.posthooks = {} - self.user_manager = get_user_manager(config) self.session_manager = get_session_manager(config) self.full_sync_manager = get_full_sync_manager(config) @@ -410,39 +407,6 @@ class SyncApp: if not self.base_media_url.endswith('/'): self.base_media_url += '/' - # backwards compat - @property - def hook_pre_sync(self): - return self.prehooks.get("start") - - @hook_pre_sync.setter - def hook_pre_sync(self, value): - self.prehooks['start'] = value - - @property - def hook_post_sync(self): - return self.posthooks.get("finish") - - @hook_post_sync.setter - def hook_post_sync(self, value): - self.posthooks['finish'] = value - - @property - def hook_upload(self): - return self.prehooks.get("upload") - - @hook_upload.setter - def hook_upload(self, value): - self.prehooks['upload'] = value - - @property - def hook_download(self): - return self.posthooks.get("download") - - @hook_download.setter - def hook_download(self, value): - self.posthooks['download'] = value - def generateHostKey(self, username): """Generates a new host key to be used by the given username to identify their session. This values is random.""" @@ -549,39 +513,22 @@ class SyncApp: self.session_manager.save(hkey, session) session = self.session_manager.load(hkey, self.create_session) - thread = session.get_thread() - - if url in self.prehooks: - thread.execute(self.prehooks[url], [session]) - result = self._execute_handler_method_in_thread(url, data, session) - # If it's a complex data type, we convert it to JSON if type(result) not in (str, bytes, Response): result = json.dumps(result) - if url in self.posthooks: - thread.execute(self.posthooks[url], [session]) - return result elif url == 'upload': thread = session.get_thread() - if url in self.prehooks: - thread.execute(self.prehooks[url], [session]) result = thread.execute(self.operation_upload, [data['data'], session]) - if url in self.posthooks: - thread.execute(self.posthooks[url], [session]) return result elif url == 'download': thread = session.get_thread() - if url in self.prehooks: - thread.execute(self.prehooks[url], [session]) result = thread.execute(self.operation_download, [session]) - if url in self.posthooks: - thread.execute(self.posthooks[url], [session]) return result # This was one of our operations but it didn't get handled... Oops! From 0d8f3c6eeaa1296e8bbe3065f62c3279d2570c20 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 2 Sep 2020 18:33:48 +0200 Subject: [PATCH 56/75] Removed runHook calls from sync code These hooks were only used on the client. --- src/ankisyncd/sync.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 3eda801..183bcc4 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -16,7 +16,6 @@ from anki.utils import ids2str, intTime, platDesc, checksum, devMode from anki.consts import * from anki.config import ConfigManager from anki.utils import versionWithBuild -from anki.hooks import runHook import anki from anki.lang import ngettext @@ -50,7 +49,6 @@ class Syncer(object): self.col.save() # step 1: login & metadata - runHook("sync", "login") meta = self.server.meta() self.col.log("rmeta", meta) if not meta: @@ -90,7 +88,6 @@ class Syncer(object): self.col.log("basic check") return "basicCheckFailed" # step 2: startup and deletions - runHook("sync", "meta") rrem = self.server.start(minUsn=self.minUsn, lnewer=self.lnewer) # apply deletions to server @@ -107,25 +104,20 @@ class Syncer(object): rchg = self.server.applyChanges(changes=lchg) self.mergeChanges(lchg, rchg) # step 3: stream large tables from server - runHook("sync", "server") - while 1: - runHook("sync", "stream") + while True: chunk = self.server.chunk() self.col.log("server chunk", chunk) self.applyChunk(chunk=chunk) if chunk['done']: break # step 4: stream to server - runHook("sync", "client") - while 1: - runHook("sync", "stream") + while True: chunk = self.chunk() self.col.log("client chunk", chunk) self.server.applyChunk(chunk=chunk) if chunk['done']: break # step 5: sanity check - runHook("sync", "sanity") c = self.sanityCheck() ret = self.server.sanityCheck2(client=c) if ret['status'] != "ok": @@ -135,7 +127,6 @@ class Syncer(object): self.col.save() return "sanityCheckFailed" # finalize - runHook("sync", "finalize") mod = self.server.finish() self.finish(mod) return "success" @@ -449,7 +440,6 @@ class AnkiRequestsClient(object): buf = io.BytesIO() for chunk in resp.iter_content(chunk_size=HTTP_BUF_SIZE): - runHook("httpRecv", len(chunk)) buf.write(chunk) return buf.getvalue() @@ -467,7 +457,7 @@ if os.environ.get("ANKI_NOVERIFYSSL"): class _MonitoringFile(io.BufferedReader): def read(self, size=-1): data = io.BufferedReader.read(self, HTTP_BUF_SIZE) - runHook("httpSend", len(data)) + return data # HTTP syncing tools @@ -632,13 +622,11 @@ class FullSyncer(HttpSyncer): self.col = col def download(self): - runHook("sync", "download") localNotEmpty = self.col.db.scalar("select 1 from cards") self.col.close() cont = self.req("download") tpath = self.col.path + ".tmp" if cont == "upgradeRequired": - runHook("sync", "upgradeRequired") return open(tpath, "wb").write(cont) # check the received file is ok @@ -657,7 +645,6 @@ class FullSyncer(HttpSyncer): def upload(self): "True if upload successful." - runHook("sync", "upload") # make sure it's ok before we try to upload if self.col.db.scalar("pragma integrity_check") != "ok": return False From e9e06454169c72c82070a1d5032c9149dd3ddce1 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 2 Sep 2020 18:39:50 +0200 Subject: [PATCH 57/75] Removed unused sync method --- src/ankisyncd/sync.py | 105 ------------------------------------------ 1 file changed, 105 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 183bcc4..66be132 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -40,111 +40,6 @@ class Syncer(object): self.col = col self.server = server - def sync(self): - "Returns 'noChanges', 'fullSync', 'success', etc" - self.syncMsg = "" - self.uname = "" - # if the deck has any pending changes, flush them first and bump mod - # time - self.col.save() - - # step 1: login & metadata - meta = self.server.meta() - self.col.log("rmeta", meta) - if not meta: - return "badAuth" - # server requested abort? - self.syncMsg = meta['msg'] - if not meta['cont']: - return "serverAbort" - else: - # don't abort, but if 'msg' is not blank, gui should show 'msg' - # after sync finishes and wait for confirmation before hiding - pass - rscm = meta['scm'] - rts = meta['ts'] - self.rmod = meta['mod'] - self.maxUsn = meta['usn'] - self.uname = meta.get("uname", "") - self.hostNum = meta.get("hostNum") - meta = self.meta() - self.col.log("lmeta", meta) - self.lmod = meta['mod'] - self.minUsn = meta['usn'] - lscm = meta['scm'] - lts = meta['ts'] - if abs(rts - lts) > 300: - self.col.log("clock off") - return "clockOff" - if self.lmod == self.rmod: - self.col.log("no changes") - return "noChanges" - elif lscm != rscm: - self.col.log("schema diff") - return "fullSync" - self.lnewer = self.lmod > self.rmod - # step 1.5: check collection is valid - if not self.col.basicCheck(): - self.col.log("basic check") - return "basicCheckFailed" - # step 2: startup and deletions - rrem = self.server.start(minUsn=self.minUsn, lnewer=self.lnewer) - - # apply deletions to server - lgraves = self.removed() - while lgraves: - gchunk, lgraves = self._gravesChunk(lgraves) - self.server.applyGraves(chunk=gchunk) - - # then apply server deletions here - self.remove(rrem) - - # ...and small objects - lchg = self.changes() - rchg = self.server.applyChanges(changes=lchg) - self.mergeChanges(lchg, rchg) - # step 3: stream large tables from server - while True: - chunk = self.server.chunk() - self.col.log("server chunk", chunk) - self.applyChunk(chunk=chunk) - if chunk['done']: - break - # step 4: stream to server - while True: - chunk = self.chunk() - self.col.log("client chunk", chunk) - self.server.applyChunk(chunk=chunk) - if chunk['done']: - break - # step 5: sanity check - c = self.sanityCheck() - ret = self.server.sanityCheck2(client=c) - if ret['status'] != "ok": - # roll back and force full sync - self.col.rollback() - self.col.modSchema(False) - self.col.save() - return "sanityCheckFailed" - # finalize - mod = self.server.finish() - self.finish(mod) - return "success" - - def _gravesChunk(self, graves): - lim = 250 - chunk = dict(notes=[], cards=[], decks=[]) - for cat in "notes", "cards", "decks": - if lim and graves[cat]: - chunk[cat] = graves[cat][:lim] - graves[cat] = graves[cat][lim:] - lim -= len(chunk[cat]) - - # anything remaining? - if graves['notes'] or graves['cards'] or graves['decks']: - return chunk, graves - return chunk, None - def meta(self): return dict( mod=self.col.mod, From f51005032a24f42c7b3fc138273b0f1b0147dd35 Mon Sep 17 00:00:00 2001 From: Karsten Lehmann Date: Wed, 2 Sep 2020 18:43:15 +0200 Subject: [PATCH 58/75] Removed explicit object parent from FullSyncManager --- src/ankisyncd/full_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/full_sync.py b/src/ankisyncd/full_sync.py index 9e7c3cc..a6c9b9d 100644 --- a/src/ankisyncd/full_sync.py +++ b/src/ankisyncd/full_sync.py @@ -13,7 +13,7 @@ from anki.collection import Collection logger = logging.getLogger("ankisyncd.media") logger.setLevel(1) -class FullSyncManager(object): +class FullSyncManager: def test_db(self, db: DB): """ :param anki.db.DB db: the database uploaded from the client. From 4cf05f71625005cf4ed9590bcee5f293af9358d6 Mon Sep 17 00:00:00 2001 From: Alexander Bocken Date: Sat, 5 Sep 2020 11:42:44 +0200 Subject: [PATCH 59/75] improved explanation for AnkiDroid setup --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 61dd905..7adda91 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ It supports Python 3 and Anki 2.1. Installing ---------- - + 1. Install the dependencies: $ pip install -r src/requirements.txt @@ -135,6 +135,8 @@ Unless you have set up a reverse proxy to handle encrypted connections, use whatever you have specified in `ankisyncd.conf` (or, if using a reverse proxy, whatever port you configured to accept the front-end connection). +Use the same base url for both the `Sync url` and the `Media sync url`. Do **not** append `/sync` and `/msync` as the default setup does. + Even though the AnkiDroid interface will request an email address, this is not required; it will simply be the username you configured with `ankisyncctl.py adduser`. From 0f629738364e63b599a8106494e2bc37670756f4 Mon Sep 17 00:00:00 2001 From: reivilibre <38398653+reivilibre@users.noreply.github.com> Date: Wed, 28 Oct 2020 10:52:10 +0000 Subject: [PATCH 60/75] Fix AnkiDroid documentation wrt. Media sync url It does not work if you do not append `/msync/`. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7adda91..88334b7 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,8 @@ Unless you have set up a reverse proxy to handle encrypted connections, use whatever you have specified in `ankisyncd.conf` (or, if using a reverse proxy, whatever port you configured to accept the front-end connection). -Use the same base url for both the `Sync url` and the `Media sync url`. Do **not** append `/sync` and `/msync` as the default setup does. +Use the same base url for both the `Sync url` and the `Media sync url`, but append `/msync` to +the `Media sync url`. Do **not** append `/sync` to the `Sync url`. Even though the AnkiDroid interface will request an email address, this is not required; it will simply be the username you configured with `ankisyncctl.py From 003e1cb471475f5af5bd0218e8b15e919f993fcd Mon Sep 17 00:00:00 2001 From: cecini Date: Thu, 29 Oct 2020 18:46:05 +0800 Subject: [PATCH 61/75] fix checking media and delete usued media and sync issue#32 --- src/ankisyncd/sync_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index d28bd6a..732dac2 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -236,7 +236,7 @@ class SyncMediaHandler: # Remove media files that were removed on the client. media_to_remove = [] for normname, ordinal in meta: - if ordinal == '': + if not ordinal: media_to_remove.append(self._normalize_filename(normname)) # Add media files that were added on the client. From 1d73376115d61105b61f26e40157342716cb3548 Mon Sep 17 00:00:00 2001 From: cecini Date: Fri, 30 Oct 2020 10:00:00 +0800 Subject: [PATCH 62/75] Fix the remove deck lead server crash issue #28, the change intruduced after 3.1.28 f592672fa --- src/ankisyncd/sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 66be132..4745d73 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -194,7 +194,7 @@ from notes where %s""" % lim, self.maxUsn) self.col.remCards(graves['cards'], notes=False) # and decks for oid in graves['decks']: - self.col.decks.rem(oid, childrenToo=False) + self.col.decks.rem(oid) self.col.server = False From 3d5d27bf6f704c7682454d9101a49bfae61c75ec Mon Sep 17 00:00:00 2001 From: cecini Date: Fri, 30 Oct 2020 10:09:21 +0800 Subject: [PATCH 63/75] Fix: server side graves's usn need not set as -1 once client items in graves have been remove in the server,need update graves usn as maxusn avoid sync serve log: sanity check failed with server: graves had usn = -1 such as issue #28 second comment --- src/ankisyncd/collection.py | 2 +- src/ankisyncd/sync.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ankisyncd/collection.py b/src/ankisyncd/collection.py index 5127f2c..d29e97e 100644 --- a/src/ankisyncd/collection.py +++ b/src/ankisyncd/collection.py @@ -64,7 +64,7 @@ class CollectionWrapper: return col def _get_collection(self): - col = anki.storage.Collection(self.path) + col = anki.storage.Collection(self.path, server=True) # Ugly hack, replace default media manager with our custom one col.media.close() diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 66be132..8e04914 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -185,8 +185,6 @@ from notes where %s""" % lim, self.maxUsn) return dict(cards=cards, notes=notes, decks=decks) def remove(self, graves): - # pretend to be the server so we don't set usn = -1 - self.col.server = True # notes first, so we don't end up with duplicate graves self.col._remNotes(graves['notes']) @@ -196,7 +194,6 @@ from notes where %s""" % lim, self.maxUsn) for oid in graves['decks']: self.col.decks.rem(oid, childrenToo=False) - self.col.server = False # Models ########################################################################## From 7a01c14729d33cb553592cb859f15a13cdf3af6a Mon Sep 17 00:00:00 2001 From: cecini Date: Fri, 30 Oct 2020 11:09:51 +0800 Subject: [PATCH 64/75] Ensure mediadir exsit in the case full sync ok after delete server collection dir --- src/ankisyncd/sync_app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 732dac2..3cffa58 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -243,6 +243,9 @@ class SyncMediaHandler: media_to_add = [] usn = self.col.media.lastUsn() oldUsn = usn + media_dir = self.col.media.dir() + os.makedirs(media_dir, exist_ok=True) + for i in zip_file.infolist(): if i.filename == "_meta": # Ignore previously retrieved metadata. continue @@ -250,7 +253,7 @@ class SyncMediaHandler: file_data = zip_file.read(i) csum = anki.utils.checksum(file_data) filename = self._normalize_filename(meta[int(i.filename)][0]) - file_path = os.path.join(self.col.media.dir(), filename) + file_path = os.path.join(media_dir, filename) # Save file to media directory. with open(file_path, 'wb') as f: From a8f26a5a34d8ce5231283c0359ab454782b7908c Mon Sep 17 00:00:00 2001 From: cecini Date: Tue, 27 Oct 2020 10:27:40 +0800 Subject: [PATCH 65/75] Update ankisyncd _homepage --- src/ankisyncd/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/__init__.py b/src/ankisyncd/__init__.py index e691cc3..322c52a 100644 --- a/src/ankisyncd/__init__.py +++ b/src/ankisyncd/__init__.py @@ -1,7 +1,7 @@ import os import sys -_homepage = "https://github.com/tsudoko/anki-sync-server" +_homepage = "https://github.com/ankicommunity/anki-sync-server.git" _unknown_version = "[unknown version]" From 8448c6586d1acb19d1c7c2ced07332a98a92b416 Mon Sep 17 00:00:00 2001 From: cecini Date: Tue, 27 Oct 2020 11:30:20 +0800 Subject: [PATCH 66/75] Enable V2 scheduler --- src/ankisyncd/sync_app.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 732dac2..4372ac7 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -112,8 +112,10 @@ class SyncCollectionHandler(Syncer): # ankidesktop >=2.1rc2 sends graves in applyGraves, but still expects # server-side deletions to be returned by start def start(self, minUsn, lnewer, graves={"cards": [], "notes": [], "decks": []}, offset=None): - if offset is not None: - raise NotImplementedError('You are using the experimental V2 scheduler, which is not supported by the server.') + # The offset para is passed by client V2 scheduler,which is minutes_west. + # Since now have not thorougly test the V2 scheduler, we leave this comments here, and + # just enable the V2 scheduler in the serve code. + self.maxUsn = self.col._usn self.minUsn = minUsn self.lnewer = not lnewer From 119292be5d5b6b977679bf046bf0f27b2d4f09b1 Mon Sep 17 00:00:00 2001 From: cecini Date: Wed, 4 Nov 2020 18:37:08 +0800 Subject: [PATCH 67/75] Fix: conf should be synced to client when server mod time is newer --- src/ankisyncd/sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 66be132..c8a66b9 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -57,7 +57,7 @@ class Syncer(object): decks=self.getDecks(), tags=self.getTags()) if self.lnewer: - #d['conf'] = self.getConf() + d['conf'] = json.loads(self.col.backend.get_all_config()) d['crt'] = self.col.crt return d From 1ef3685820a7d58833e275aad3665287a41f07bf Mon Sep 17 00:00:00 2001 From: cecini Date: Wed, 4 Nov 2020 21:16:49 +0800 Subject: [PATCH 68/75] Improve: put all config item update in one transaction --- src/ankisyncd/sync.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index 66be132..940f158 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -304,9 +304,7 @@ from notes where %s""" % lim, self.maxUsn) return self.col.conf def mergeConf(self, conf): - newConf = ConfigManager(self.col) - for key, value in conf.items(): - self.col.set_config(key, value) + self.col.backend.set_all_config(json.dumps(conf).encode()) # Wrapper for requests that tracks upload/download progress ########################################################################## From 7639329289fa7b2c259373f2c4345c046dd5bc46 Mon Sep 17 00:00:00 2001 From: cecini Date: Tue, 27 Oct 2020 11:31:17 +0800 Subject: [PATCH 69/75] Update dep anki ankirspy to 2.1.36 fix the requirements file create --- poetry.lock | 6 +- pyproject.toml | 4 +- scripts/lock.sh | 10 ++- src/requirements-dev.txt | 153 ++++++++++++++++++++------------------- src/requirements.txt | 37 +++++----- 5 files changed, 110 insertions(+), 100 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3810d88..4649eae 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4,10 +4,10 @@ description = "Anki's library code" name = "anki" optional = false python-versions = ">=3.7" -version = "2.1.32" +version = "2.1.36" [package.dependencies] -ankirspy = "2.1.32" +ankirspy = "2.1.36" beautifulsoup4 = "*" decorator = "*" distro = "*" @@ -25,7 +25,7 @@ description = "Anki's Rust library code Python bindings" name = "ankirspy" optional = false python-versions = "*" -version = "2.1.32" +version = "2.1.36" [[package]] category = "dev" diff --git a/pyproject.toml b/pyproject.toml index f776a08..94649a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,12 @@ [tool.poetry] name = "anki-sync-server" -version = "0.1.0" +version = "2.2.0" description = "Self-hosted Anki Sync Server." authors = ["Vikash Kothary "] [tool.poetry.dependencies] python = "^3.7" -anki = "^2.1.32" +anki = "^2.1.36" beautifulsoup4 = "^4.9.1" requests = "^2.24.0" markdown = "^3.2.2" diff --git a/scripts/lock.sh b/scripts/lock.sh index cf7dc6d..f9d9039 100644 --- a/scripts/lock.sh +++ b/scripts/lock.sh @@ -2,8 +2,12 @@ # file: lock.sh # description: Lock dependencies and export requirements. +echo "THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!\n\n" > src/requirements.txt +echo "THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!\n\n" > src/requirements-dev.txt poetry lock -poetry export --without-hashes -f requirements.txt > src/requirements.txt -poetry export --dev --without-hashes -f requirements.txt > src/requirements-dev.txt +poetry export --without-hashes -f requirements.txt >> src/requirements.txt +poetry export --dev --without-hashes -f requirements.txt >> src/requirements-dev.txt + +echo "-e src/." >> src/requirements-dev.txt + -echo "-e src/." >> src/requirements-dev.txt \ No newline at end of file diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 49dca27..8d1c5aa 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -1,85 +1,88 @@ -anki==2.1.32 -ankirspy==2.1.32 -appnope==0.1.0; sys_platform == "darwin" or platform_system == "Darwin" or python_version >= "3.3" and sys_platform == "darwin" -argon2-cffi==20.1.0 -attrs==20.1.0 -backcall==0.2.0 +THE FILE WAS GENERATED BY POETRY, DO NOT EDIT! + +Warning: The lock file is not up to date with the latest changes in pyproject.toml. You may be getting outdated dependencies. Run update to update them. +anki==2.1.36; python_version >= "3.7" +ankirspy==2.1.36; python_version >= "3.7" +appnope==0.1.0; sys_platform == "darwin" and python_version >= "3.7" or sys_platform == "darwin" and python_version >= "3.7" and platform_system == "Darwin" or platform_system == "Darwin" and python_version >= "3.7" +argon2-cffi==20.1.0; python_version >= "3.5" +attrs==20.1.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" +backcall==0.2.0; python_version >= "3.7" beautifulsoup4==4.9.1 -bleach==3.1.5 -certifi==2020.6.20 -cffi==1.14.2 -chardet==3.0.4 -click==7.1.2 -colorama==0.4.3; python_version >= "3.3" and sys_platform == "win32" or sys_platform == "win32" -decorator==4.4.2 -defusedxml==0.6.0 +bleach==3.1.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +certifi==2020.6.20; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +cffi==1.14.2; python_version >= "3.5" +chardet==3.0.4; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +click==7.1.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" +colorama==0.4.3; python_version >= "3.7" and sys_platform == "win32" and python_full_version < "3.0.0" or python_version >= "3.7" and sys_platform == "win32" and python_full_version >= "3.5.0" +decorator==4.4.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.2.0") +defusedxml==0.6.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" distro==1.5.0 -entrypoints==0.3 -future==0.18.2 -idna==2.10 -importlib-metadata==1.7.0; python_version < "3.8" -ipykernel==5.3.4 -ipython==7.17.0 -ipython-genutils==0.2.0 +entrypoints==0.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "2.7" +future==0.18.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5" +idna==2.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +importlib-metadata==1.7.0; python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5" +ipykernel==5.3.4; python_version >= "3.5" +ipython-genutils==0.2.0; python_version >= "3.5" +ipython==7.17.0; python_version >= "3.7" ipywidgets==7.5.1 -jedi==0.17.2 -jinja2==2.11.2 -joblib==0.16.0; python_version > "2.7" -json5==0.9.5 -jsonschema==3.2.0 +jedi==0.17.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +jinja2==2.11.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" +joblib==0.16.0; python_version >= "3.6" +json5==0.9.5; python_version >= "3.5" +jsonschema==3.2.0; python_version >= "3.5" +jupyter-client==6.1.7; python_version >= "3.5" +jupyter-console==6.1.0; python_version >= "3.5" +jupyter-core==4.6.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_version >= "3.5" and python_full_version >= "3.5.0" jupyter==1.0.0 -jupyter-client==6.1.7 -jupyter-console==6.1.0 -jupyter-core==4.6.3 -jupyterlab==2.2.6 -jupyterlab-server==1.2.0 -livereload==2.6.3 -lunr==0.5.8 -markdown==3.2.2 -markupsafe==1.1.1 -mistune==0.8.4 -mkdocs==1.1.2 -nbconvert==5.6.1 -nbformat==5.0.7 -nltk==3.5; python_version > "2.7" -notebook==6.1.3 -orjson==3.3.1; platform_machine == "x86_64" -packaging==20.4 -pandocfilters==1.4.2 -parso==0.7.1 -pexpect==4.8.0; python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32" -pickleshare==0.7.5 -prometheus-client==0.8.0 -prompt-toolkit==3.0.6 -protobuf==3.13.0 -psutil==5.7.2 -ptyprocess==0.6.0; sys_platform != "win32" or os_name != "nt" or python_version >= "3.3" and sys_platform != "win32" +jupyterlab-server==1.2.0; python_version >= "3.5" +jupyterlab==2.2.6; python_version >= "3.5" +livereload==2.6.3; python_version >= "3.5" +lunr==0.5.8; python_version >= "3.5" +markdown==3.2.2; python_version >= "3.5" +markupsafe==1.1.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" +mistune==0.8.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +mkdocs==1.1.2; python_version >= "3.5" +nbconvert==5.6.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" +nbformat==5.0.7; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" +nltk==3.5; python_version >= "3.5" +notebook==6.1.3; python_version >= "3.5" +orjson==3.3.1; platform_machine == "x86_64" and python_version >= "3.7" +packaging==20.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +pandocfilters==1.4.2; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +parso==0.7.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +pexpect==4.8.0; python_version >= "3.7" and sys_platform != "win32" +pickleshare==0.7.5; python_version >= "3.7" +prometheus-client==0.8.0; python_version >= "3.5" +prompt-toolkit==3.0.6; python_full_version >= "3.6.1" and python_version >= "3.7" +protobuf==3.13.0; python_version >= "3.7" +psutil==5.7.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") +ptyprocess==0.6.0; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") or python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") and os_name != "nt" or python_full_version >= "3.4.0" and python_version >= "3.7" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") or python_full_version >= "3.4.0" and python_version >= "3.7" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") and os_name != "nt" pyaudio==0.2.11 -pycparser==2.20 -pygments==2.6.1 -pyparsing==2.4.7 -pyrsistent==0.16.0 -python-dateutil==2.8.1 -pywin32==228; sys_platform == "win32" -pywinpty==0.5.7; os_name == "nt" -pyyaml==5.3.1 -pyzmq==19.0.2 +pycparser==2.20; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" +pygments==2.6.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +pyparsing==2.4.7; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +pyrsistent==0.16.0; python_version >= "3.5" +python-dateutil==2.8.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5" +pywin32==228; python_version >= "2.7" and python_version < "3.0" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version >= "2.7" and python_version < "3.0" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.0" and python_version < "3.1" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.0" and python_version < "3.1" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.1" and python_version < "3.2" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.1" and python_version < "3.2" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.2" and python_version < "3.3" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.2" and python_version < "3.3" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.3" and python_version < "3.4" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.3" and python_version < "3.4" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.4" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.4" and python_full_version >= "3.5.0" and sys_platform == "win32" +pywinpty==0.5.7; python_version >= "3.5" and python_full_version < "3.0.0" and os_name == "nt" or python_full_version >= "3.4.0" and python_version >= "3.5" and os_name == "nt" +pyyaml==5.3.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" +pyzmq==19.0.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5" qtconsole==4.7.6 qtpy==1.9.0 -regex==2020.7.14; python_version > "2.7" -requests==2.24.0 +regex==2020.7.14; python_version >= "3.5" +requests==2.24.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") send2trash==1.5.0 -six==1.15.0 -soupsieve==1.9.6 -terminado==0.8.3 -testpath==0.4.4 -tornado==6.0.4 -tqdm==4.48.2; python_version > "2.7" -traitlets==4.3.3 -urllib3==1.25.10 -wcwidth==0.2.5 -webencodings==0.5.1 -webob==1.8.6 +six==1.15.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +soupsieve==1.9.6; python_version >= "3.7" +terminado==0.8.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" +testpath==0.4.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +tornado==6.0.4; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" +tqdm==4.48.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" and python_version >= "3.5" +traitlets==4.3.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +urllib3==1.25.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.7" +wcwidth==0.2.5; python_full_version >= "3.6.1" and python_version >= "3.5" +webencodings==0.5.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +webob==1.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") widgetsnbextension==3.5.1 -zipp==3.1.0; python_version < "3.8" +zipp==3.1.0; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5") -e src/. diff --git a/src/requirements.txt b/src/requirements.txt index 3a1dc74..77ba117 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,21 +1,24 @@ -anki==2.1.32 -ankirspy==2.1.32 +THE FILE WAS GENERATED BY POETRY, DO NOT EDIT! + +Warning: The lock file is not up to date with the latest changes in pyproject.toml. You may be getting outdated dependencies. Run update to update them. +anki==2.1.36; python_version >= "3.7" +ankirspy==2.1.36; python_version >= "3.7" beautifulsoup4==4.9.1 -certifi==2020.6.20 -chardet==3.0.4 -decorator==4.4.2 +certifi==2020.6.20; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +chardet==3.0.4; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +decorator==4.4.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.2.0") distro==1.5.0 -idna==2.10 -importlib-metadata==1.7.0; python_version < "3.8" -markdown==3.2.2 -orjson==3.3.1; platform_machine == "x86_64" -protobuf==3.13.0 -psutil==5.7.2 +idna==2.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +importlib-metadata==1.7.0; python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5" +markdown==3.2.2; python_version >= "3.5" +orjson==3.3.1; platform_machine == "x86_64" and python_version >= "3.7" +protobuf==3.13.0; python_version >= "3.7" +psutil==5.7.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") pyaudio==0.2.11 -requests==2.24.0 +requests==2.24.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") send2trash==1.5.0 -six==1.15.0 -soupsieve==1.9.6 -urllib3==1.25.10 -webob==1.8.6 -zipp==3.1.0; python_version < "3.8" +six==1.15.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7" +soupsieve==1.9.6; python_version >= "3.7" +urllib3==1.25.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.7" +webob==1.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") +zipp==3.1.0; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5") From 5e56a2edcd68cf4ad2a585329529ba0764d8d47d Mon Sep 17 00:00:00 2001 From: cecini Date: Fri, 30 Oct 2020 10:32:28 +0800 Subject: [PATCH 70/75] Fix client add then delete item then sync issue, deck/card/note item must not been synced to server after client add and delete --- src/ankisyncd/sync.py | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/ankisyncd/sync.py b/src/ankisyncd/sync.py index ca5df12..f50cbd5 100644 --- a/src/ankisyncd/sync.py +++ b/src/ankisyncd/sync.py @@ -185,16 +185,46 @@ from notes where %s""" % lim, self.maxUsn) return dict(cards=cards, notes=notes, decks=decks) def remove(self, graves): + # remove card and the card's orphaned notes + self.col.remove_cards_and_orphaned_notes(graves['cards']) - # notes first, so we don't end up with duplicate graves - self.col._remNotes(graves['notes']) - # then cards - self.col.remCards(graves['cards'], notes=False) - # and decks + # only notes + self.col.remove_notes(graves['notes']) + + # since level 0 deck ,we only remove deck ,but backend will delete child,it is ok, the delete + # will have once effect for oid in graves['decks']: self.col.decks.rem(oid) + # we can place non-exist grave after above delete. + localgcards = [] + localgnotes = [] + localgdecks = [] + curs = self.col.db.execute( + "select oid, type from graves where usn = %d" % self.col.usn()) + + for oid, type in curs: + if type == REM_CARD: + localgcards.append(oid) + elif type == REM_NOTE: + localgnotes.append(oid) + else: + localgdecks.append(oid) + + # n meaning non-exsiting grave in the server compared to client + ncards = [ oid for oid in graves['cards'] if oid not in localgcards] + for oid in ncards: + self.col._logRem([oid], REM_CARD) + + nnotes = [ oid for oid in graves['notes'] if oid not in localgnotes] + for oid in nnotes: + self.col._logRem([oid], REM_NOTE) + + ndecks = [ oid for oid in graves['decks'] if oid not in localgdecks] + for oid in ndecks: + self.col._logRem([oid], REM_DECK) + # Models ########################################################################## From caf5c71b883a3c44295094d1d6da12c0bbb90550 Mon Sep 17 00:00:00 2001 From: samyak-jain Date: Sun, 29 Nov 2020 21:23:24 +0530 Subject: [PATCH 71/75] Fix issue with arch linux client --- src/ankisyncd/sync_app.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ankisyncd/sync_app.py b/src/ankisyncd/sync_app.py index 1ecace3..2147232 100644 --- a/src/ankisyncd/sync_app.py +++ b/src/ankisyncd/sync_app.py @@ -62,11 +62,12 @@ class SyncCollectionHandler(Syncer): note = {"alpha": 0, "beta": 0, "rc": 0} client, version, platform = cv.split(',') - for name in note.keys(): - if name in version: - vs = version.split(name) - version = vs[0] - note[name] = int(vs[-1]) + if 'arch' not in version: + for name in note.keys(): + if name in version: + vs = version.split(name) + version = vs[0] + note[name] = int(vs[-1]) # convert the version string, ignoring non-numeric suffixes like in beta versions of Anki version_nosuffix = re.sub(r'[^0-9.].*$', '', version) From 4f6261204387a2ff3d5b33912ee9bfb262b3015c Mon Sep 17 00:00:00 2001 From: samyak-jain Date: Thu, 17 Dec 2020 06:48:31 +0530 Subject: [PATCH 72/75] Added unittest --- tests/test_sync_app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sync_app.py b/tests/test_sync_app.py index d49adec..3147daf 100644 --- a/tests/test_sync_app.py +++ b/tests/test_sync_app.py @@ -43,6 +43,7 @@ class SyncCollectionHandlerTest(CollectionTestBase): ','.join(('ankidesktop', '2.1.0', 'lin::')), ','.join(('ankidesktop', '2.1.6-beta2', 'lin::')), ','.join(('ankidesktop', '2.1.9 (dev)', 'lin::')), + ','.join(('ankidesktop', '2.1.26 (arch-linux-2.1.26-1)', 'lin:arch:')), ','.join(('ankidroid', '2.2.3', '')), ','.join(('ankidroid', '2.3alpha4', '')), ','.join(('ankidroid', '2.3alpha5', '')), From a0675b4f50d9cc07bbc363acbedff94e6576a0d6 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Wed, 23 Dec 2020 22:45:41 +0000 Subject: [PATCH 73/75] Update Python version from 3.7 to 3.8 --- poetry.lock | 1342 +++++++++++++++++++++++++----------------------- pyproject.toml | 2 +- 2 files changed, 707 insertions(+), 637 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4649eae..b5612f7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,101 +1,97 @@ [[package]] -category = "main" -description = "Anki's library code" name = "anki" +version = "2.1.37" +description = "" +category = "main" optional = false -python-versions = ">=3.7" -version = "2.1.36" +python-versions = ">=3.8" [package.dependencies] -ankirspy = "2.1.36" beautifulsoup4 = "*" decorator = "*" -distro = "*" +distro = {version = "*", markers = "sys_platform != \"darwin\" and sys_platform != \"win32\""} orjson = "*" protobuf = "*" -psutil = "*" - -[package.dependencies.requests] -extras = ["socks"] -version = "*" +psutil = {version = "*", markers = "sys_platform == \"win32\""} +requests = {version = "*", extras = ["socks"]} [[package]] -category = "main" -description = "Anki's Rust library code Python bindings" -name = "ankirspy" -optional = false -python-versions = "*" -version = "2.1.36" - -[[package]] -category = "dev" -description = "Disable App Nap on OS X 10.9" -marker = "sys_platform == \"darwin\" or platform_system == \"Darwin\" or python_version >= \"3.3\" and sys_platform == \"darwin\"" name = "appnope" +version = "0.1.2" +description = "Disable App Nap on macOS >= 10.9" +category = "dev" optional = false python-versions = "*" -version = "0.1.0" [[package]] -category = "dev" -description = "The secure Argon2 password hashing algorithm." name = "argon2-cffi" +version = "20.1.0" +description = "The secure Argon2 password hashing algorithm." +category = "dev" optional = false python-versions = "*" -version = "20.1.0" [package.dependencies] cffi = ">=1.0.0" six = "*" [package.extras] -dev = ["coverage (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] docs = ["sphinx"] -tests = ["coverage (>=5.0.2)", "hypothesis", "pytest"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] [[package]] +name = "async-generator" +version = "1.10" +description = "Async generators and context managers for Python 3.5+" category = "dev" -description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.5" + +[[package]] name = "attrs" +version = "20.3.0" +description = "Classes Without Boilerplate" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.1.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"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] -category = "dev" -description = "Specifications for callback functions passed in to an API" name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "main" -description = "Screen-scraping library" name = "beautifulsoup4" +version = "4.9.3" +description = "Screen-scraping library" +category = "main" optional = false python-versions = "*" -version = "4.9.1" [package.dependencies] -soupsieve = [">1.2", "<2.0"] +soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} [package.extras] html5lib = ["html5lib"] lxml = ["lxml"] [[package]] -category = "dev" -description = "An easy safelist-based HTML-sanitizing tool." name = "bleach" +version = "3.2.1" +description = "An easy safelist-based HTML-sanitizing tool." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "3.1.5" [package.dependencies] packaging = "*" @@ -103,123 +99,106 @@ six = ">=1.9.0" webencodings = "*" [[package]] -category = "main" -description = "Python package for providing Mozilla's CA Bundle." name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = "*" -version = "2020.6.20" [[package]] -category = "dev" -description = "Foreign Function Interface for Python calling C code." name = "cffi" +version = "1.14.4" +description = "Foreign Function Interface for Python calling C code." +category = "dev" optional = false python-versions = "*" -version = "1.14.2" [package.dependencies] pycparser = "*" [[package]] -category = "main" -description = "Universal encoding detector for Python 2 and 3" name = "chardet" -optional = false -python-versions = "*" -version = "3.0.4" - -[[package]] -category = "dev" -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 = "dev" -description = "Cross-platform colored terminal text." -marker = "python_version >= \"3.3\" and sys_platform == \"win32\" or 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]] +version = "4.0.0" +description = "Universal encoding detector for Python 2 and 3" category = "main" -description = "Decorators for Humans" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] name = "decorator" +version = "4.4.2" +description = "Decorators for Humans" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.4.2" [[package]] -category = "dev" -description = "XML bomb protection for Python stdlib modules" name = "defusedxml" +version = "0.6.0" +description = "XML bomb protection for Python stdlib modules" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.6.0" [[package]] -category = "main" -description = "Distro - an OS platform information API" name = "distro" +version = "1.5.0" +description = "Distro - an OS platform information API" +category = "main" optional = false python-versions = "*" -version = "1.5.0" [[package]] -category = "dev" -description = "Discover and load entry points from installed packages." name = "entrypoints" +version = "0.3" +description = "Discover and load entry points from installed packages." +category = "dev" optional = false python-versions = ">=2.7" -version = "0.3" [[package]] -category = "dev" -description = "Clean single-source support for Python 3 and 2" name = "future" +version = "0.18.2" +description = "Clean single-source support for Python 3 and 2" +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.18.2" [[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.10" [[package]] -category = "main" -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 = "IPython Kernel for Jupyter" name = "ipykernel" +version = "5.4.2" +description = "IPython Kernel for Jupyter" +category = "dev" optional = false python-versions = ">=3.5" -version = "5.3.4" [package.dependencies] -appnope = "*" +appnope = {version = "*", markers = "platform_system == \"Darwin\""} ipython = ">=5.0.0" jupyter-client = "*" tornado = ">=4.2" @@ -229,24 +208,23 @@ traitlets = ">=4.1.0" test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose"] [[package]] -category = "dev" -description = "IPython: Productive Interactive Computing" name = "ipython" +version = "7.19.0" +description = "IPython: Productive Interactive Computing" +category = "dev" optional = false python-versions = ">=3.7" -version = "7.17.0" [package.dependencies] -appnope = "*" +appnope = {version = "*", markers = "sys_platform == \"darwin\""} backcall = "*" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" jedi = ">=0.10" -pexpect = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} pickleshare = "*" prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" -setuptools = ">=18.5" traitlets = ">=4.2" [package.extras] @@ -261,56 +239,53 @@ qtconsole = ["qtconsole"] test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] [[package]] -category = "dev" -description = "Vestigial utilities from IPython" name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "dev" -description = "IPython HTML widgets for Jupyter" name = "ipywidgets" +version = "7.5.1" +description = "IPython HTML widgets for Jupyter" +category = "dev" optional = false python-versions = "*" -version = "7.5.1" [package.dependencies] ipykernel = ">=4.5.1" +ipython = {version = ">=4.0.0", markers = "python_version >= \"3.3\""} nbformat = ">=4.2.0" traitlets = ">=4.3.1" widgetsnbextension = ">=3.5.0,<3.6.0" -[package.dependencies.ipython] -python = ">=3.3" -version = ">=4.0.0" - [package.extras] test = ["pytest (>=3.6.0)", "pytest-cov", "mock"] [[package]] -category = "dev" -description = "An autocompletion tool for Python that can be used for text editors." name = "jedi" +version = "0.17.2" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" 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)"] +qa = ["flake8 (==3.7.9)"] testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] -category = "dev" -description = "A very fast and expressive template engine." name = "jinja2" +version = "2.11.2" +description = "A very fast and expressive template engine." +category = "dev" 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" @@ -319,54 +294,48 @@ MarkupSafe = ">=0.23" i18n = ["Babel (>=0.8)"] [[package]] -category = "dev" -description = "Lightweight pipelining: using Python functions as pipeline jobs." -marker = "python_version > \"2.7\"" name = "joblib" +version = "1.0.0" +description = "Lightweight pipelining with Python functions" +category = "dev" optional = false python-versions = ">=3.6" -version = "0.16.0" [[package]] -category = "dev" -description = "A Python implementation of the JSON5 data format." name = "json5" +version = "0.9.5" +description = "A Python implementation of the JSON5 data format." +category = "dev" optional = false python-versions = "*" -version = "0.9.5" [package.extras] dev = ["hypothesis"] [[package]] -category = "dev" -description = "An implementation of JSON Schema validation for Python" name = "jsonschema" +version = "3.2.0" +description = "An implementation of JSON Schema validation for Python" +category = "dev" optional = false python-versions = "*" -version = "3.2.0" [package.dependencies] attrs = ">=17.4.0" pyrsistent = ">=0.14.0" -setuptools = "*" six = ">=1.11.0" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" - [package.extras] format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] [[package]] -category = "dev" -description = "Jupyter metapackage. Install all the Jupyter components in one go." name = "jupyter" +version = "1.0.0" +description = "Jupyter metapackage. Install all the Jupyter components in one go." +category = "dev" optional = false python-versions = "*" -version = "1.0.0" [package.dependencies] ipykernel = "*" @@ -377,12 +346,12 @@ notebook = "*" qtconsole = "*" [[package]] -category = "dev" -description = "Jupyter protocol implementation and client libraries" name = "jupyter-client" +version = "6.1.7" +description = "Jupyter protocol implementation and client libraries" +category = "dev" optional = false python-versions = ">=3.5" -version = "6.1.7" [package.dependencies] jupyter-core = ">=4.6.0" @@ -395,12 +364,12 @@ traitlets = "*" test = ["ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "async-generator", "pytest-timeout"] [[package]] -category = "dev" -description = "Jupyter terminal console" name = "jupyter-console" +version = "6.2.0" +description = "Jupyter terminal console" +category = "dev" optional = false -python-versions = ">=3.5" -version = "6.1.0" +python-versions = ">=3.6" [package.dependencies] ipykernel = "*" @@ -413,24 +382,24 @@ pygments = "*" test = ["pexpect"] [[package]] -category = "dev" -description = "Jupyter core package. A base package on which Jupyter projects rely." name = "jupyter-core" +version = "4.7.0" +description = "Jupyter core package. A base package on which Jupyter projects rely." +category = "dev" optional = false -python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" -version = "4.6.3" +python-versions = ">=3.6" [package.dependencies] -pywin32 = ">=1.0" +pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\""} traitlets = "*" [[package]] -category = "dev" -description = "The JupyterLab notebook server extension." name = "jupyterlab" +version = "2.2.9" +description = "The JupyterLab notebook server extension." +category = "dev" optional = false python-versions = ">=3.5" -version = "2.2.6" [package.dependencies] jinja2 = ">=2.10" @@ -443,12 +412,23 @@ docs = ["jsx-lexer", "recommonmark", "sphinx", "sphinx-rtd-theme", "sphinx-copyb test = ["pytest", "pytest-check-links", "requests", "wheel", "virtualenv"] [[package]] +name = "jupyterlab-pygments" +version = "0.1.2" +description = "Pygments theme using JupyterLab CSS variables" category = "dev" -description = "JupyterLab Server" +optional = false +python-versions = "*" + +[package.dependencies] +pygments = ">=2.4.1,<3" + +[[package]] name = "jupyterlab-server" +version = "1.2.0" +description = "JupyterLab Server" +category = "dev" optional = false python-versions = ">=3.5" -version = "1.2.0" [package.dependencies] jinja2 = ">=2.10" @@ -461,99 +441,104 @@ requests = "*" test = ["pytest", "requests"] [[package]] -category = "dev" -description = "Python LiveReload is an awesome tool for web developers" name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "dev" optional = false python-versions = "*" -version = "2.6.3" [package.dependencies] six = "*" - -[package.dependencies.tornado] -python = ">=2.8" -version = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} [[package]] -category = "dev" -description = "A Python implementation of Lunr.js" name = "lunr" +version = "0.5.8" +description = "A Python implementation of Lunr.js" +category = "dev" optional = false python-versions = "*" -version = "0.5.8" [package.dependencies] future = ">=0.16.0" +nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} six = ">=1.11.0" -[package.dependencies.nltk] -optional = true -python = ">=2.8" -version = ">=3.2.5" - [package.extras] languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] [[package]] -category = "main" -description = "Python implementation of Markdown." name = "markdown" +version = "3.3.3" +description = "Python implementation of Markdown." +category = "main" optional = false -python-versions = ">=3.5" -version = "3.2.2" - -[package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" +python-versions = ">=3.6" [package.extras] testing = ["coverage", "pyyaml"] [[package]] -category = "dev" -description = "Safely add untrusted strings to HTML/XML markup." name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.1.1" [[package]] -category = "dev" -description = "The fastest markdown parser in pure Python" name = "mistune" +version = "0.8.4" +description = "The fastest markdown parser in pure Python" +category = "dev" optional = false python-versions = "*" -version = "0.8.4" [[package]] -category = "dev" -description = "Project documentation with Markdown." name = "mkdocs" +version = "1.1.2" +description = "Project documentation with Markdown." +category = "dev" optional = false python-versions = ">=3.5" -version = "1.1.2" [package.dependencies] +click = ">=3.3" Jinja2 = ">=2.10.1" +livereload = ">=2.5.1" +lunr = {version = "0.5.8", extras = ["languages"]} Markdown = ">=3.2.1" PyYAML = ">=3.10" -click = ">=3.3" -livereload = ">=2.5.1" tornado = ">=5.0" -[package.dependencies.lunr] -extras = ["languages"] -version = "0.5.8" +[[package]] +name = "nbclient" +version = "0.5.1" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +async-generator = "*" +jupyter-client = ">=6.1.5" +nbformat = ">=5.0" +nest-asyncio = "*" +traitlets = ">=4.2" + +[package.extras] +dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] +sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] +test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] [[package]] -category = "dev" -description = "Converting Jupyter Notebooks" name = "nbconvert" +version = "6.0.7" +description = "Converting Jupyter Notebooks" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.6.1" +python-versions = ">=3.6" [package.dependencies] bleach = "*" @@ -561,27 +546,29 @@ defusedxml = "*" entrypoints = ">=0.2.2" jinja2 = ">=2.4" jupyter-core = "*" +jupyterlab-pygments = "*" mistune = ">=0.8.1,<2" +nbclient = ">=0.5.0,<0.6.0" nbformat = ">=4.4" pandocfilters = ">=1.4.1" -pygments = "*" +pygments = ">=2.4.1" testpath = "*" traitlets = ">=4.2" [package.extras] -all = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "mock"] -docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "jupyter-client (>=5.3.1)"] -execute = ["jupyter-client (>=5.3.1)"] +all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] +docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] serve = ["tornado (>=4.0)"] -test = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "mock"] +test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)"] +webpdf = ["pyppeteer (==0.2.2)"] [[package]] -category = "dev" -description = "The Jupyter Notebook format" name = "nbformat" +version = "5.0.8" +description = "The Jupyter Notebook format" +category = "dev" optional = false python-versions = ">=3.5" -version = "5.0.7" [package.dependencies] ipython-genutils = "*" @@ -590,16 +577,24 @@ jupyter-core = "*" traitlets = ">=4.1" [package.extras] -test = ["pytest", "pytest-cov", "testpath"] +fast = ["fastjsonschema"] +test = ["fastjsonschema", "testpath", "pytest", "pytest-cov"] [[package]] +name = "nest-asyncio" +version = "1.4.3" +description = "Patch asyncio to allow nested event loops" category = "dev" -description = "Natural Language Toolkit" -marker = "python_version > \"2.7\"" +optional = false +python-versions = ">=3.5" + +[[package]] name = "nltk" +version = "3.5" +description = "Natural Language Toolkit" +category = "dev" optional = false python-versions = "*" -version = "3.5" [package.dependencies] click = "*" @@ -616,15 +611,14 @@ tgrep = ["pyparsing"] twitter = ["twython"] [[package]] -category = "dev" -description = "A web-based notebook environment for interactive computing" name = "notebook" +version = "6.1.5" +description = "A web-based notebook environment for interactive computing" +category = "dev" optional = false python-versions = ">=3.5" -version = "6.1.3" [package.dependencies] -Send2Trash = "*" argon2-cffi = "*" ipykernel = "*" ipython-genutils = "*" @@ -635,6 +629,7 @@ nbconvert = "*" nbformat = "*" prometheus-client = "*" pyzmq = ">=17" +Send2Trash = "*" terminado = ">=0.8.3" tornado = ">=5.0" traitlets = ">=4.2.1" @@ -644,214 +639,224 @@ docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt"] test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "requests-unixsocket"] [[package]] -category = "main" -description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -marker = "platform_machine == \"x86_64\"" name = "orjson" +version = "3.4.6" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +category = "main" optional = false python-versions = ">=3.6" -version = "3.3.1" [[package]] -category = "dev" -description = "Core utilities for Python packages" name = "packaging" +version = "20.8" +description = "Core utilities for Python packages" +category = "dev" 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 = "dev" -description = "Utilities for writing pandoc filters in python" name = "pandocfilters" -optional = false -python-versions = "*" -version = "1.4.2" - -[[package]] +version = "1.4.3" +description = "Utilities for writing pandoc filters in python" category = "dev" -description = "A Python Parser" -name = "parso" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "parso" version = "0.7.1" +description = "A Python Parser" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] testing = ["docopt", "pytest (>=3.0.7)"] [[package]] -category = "dev" -description = "Pexpect allows easy control of interactive console applications." -marker = "python_version >= \"3.3\" and sys_platform != \"win32\" or sys_platform != \"win32\"" name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "dev" optional = false python-versions = "*" -version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" [[package]] -category = "dev" -description = "Tiny 'shelve'-like database with concurrency support" name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" optional = false python-versions = "*" -version = "0.7.5" [[package]] -category = "dev" -description = "Python client for the Prometheus monitoring system." name = "prometheus-client" +version = "0.9.0" +description = "Python client for the Prometheus monitoring system." +category = "dev" optional = false python-versions = "*" -version = "0.8.0" [package.extras] twisted = ["twisted"] [[package]] -category = "dev" -description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" +version = "3.0.8" +description = "Library for building powerful interactive command lines in Python" +category = "dev" optional = false python-versions = ">=3.6.1" -version = "3.0.6" [package.dependencies] wcwidth = "*" [[package]] -category = "main" -description = "Protocol Buffers" name = "protobuf" +version = "3.14.0" +description = "Protocol Buffers" +category = "main" 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" +version = "5.8.0" +description = "Cross-platform lib for process and system monitoring in Python." +category = "main" 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 = "dev" -description = "Run a subprocess in a pseudo terminal" -marker = "sys_platform != \"win32\" or os_name != \"nt\" or python_version >= \"3.3\" and sys_platform != \"win32\"" name = "ptyprocess" -optional = false -python-versions = "*" version = "0.6.0" - -[[package]] -category = "main" -description = "Bindings for PortAudio v19, the cross-platform audio input/output stream library." -name = "pyaudio" +description = "Run a subprocess in a pseudo terminal" +category = "dev" optional = false python-versions = "*" -version = "0.2.11" [[package]] +name = "py" +version = "1.10.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" -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 = "dev" -description = "Pygments is a syntax highlighting package written in Python." -name = "pygments" -optional = false -python-versions = ">=3.5" -version = "2.6.1" - -[[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 = "dev" -description = "Persistent/Functional/Immutable data structures" -name = "pyrsistent" +name = "pyaudio" +version = "0.2.11" +description = "Bindings for PortAudio v19, the cross-platform audio input/output stream library." +category = "main" optional = false python-versions = "*" -version = "0.16.0" - -[package.dependencies] -six = "*" [[package]] +name = "pycparser" +version = "2.20" +description = "C parser in Python" category = "dev" -description = "Extensions to the standard Python datetime module" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pygments" +version = "2.7.3" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "pyrsistent" +version = "0.17.3" +description = "Persistent/Functional/Immutable data structures" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "pysocks" +version = "1.7.1" +description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] name = "python-dateutil" +version = "2.8.1" +description = "Extensions to the standard Python datetime module" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -version = "2.8.1" [package.dependencies] six = ">=1.5" [[package]] -category = "dev" -description = "Python for Window Extensions" -marker = "sys_platform == \"win32\"" name = "pywin32" +version = "300" +description = "Python for Window Extensions" +category = "dev" optional = false python-versions = "*" -version = "228" [[package]] -category = "dev" -description = "Python bindings for the winpty library" -marker = "os_name == \"nt\"" name = "pywinpty" +version = "0.5.7" +description = "Python bindings for the winpty library" +category = "dev" optional = false python-versions = "*" -version = "0.5.7" [[package]] -category = "dev" -description = "YAML parser and emitter for Python" name = "pyyaml" +version = "5.3.1" +description = "YAML parser and emitter for Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.1" [[package]] -category = "dev" -description = "Python bindings for 0MQ" name = "pyzmq" +version = "20.0.0" +description = "Python bindings for 0MQ" +category = "dev" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -version = "19.0.2" +python-versions = ">=3.5" + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name === \"pypy\""} +py = {version = "*", markers = "implementation_name === \"pypy\""} [[package]] -category = "dev" -description = "Jupyter Qt console" name = "qtconsole" +version = "5.0.1" +description = "Jupyter Qt console" +category = "dev" optional = false -python-versions = "*" -version = "4.7.6" +python-versions = ">= 3.6" [package.dependencies] ipykernel = ">=4.1" @@ -865,212 +870,190 @@ traitlets = "*" [package.extras] doc = ["Sphinx (>=1.3)"] -test = ["pytest", "mock"] +test = ["flaky", "pytest", "pytest-qt"] [[package]] -category = "dev" -description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5, PyQt4 and PySide) and additional custom QWidgets." name = "qtpy" -optional = false -python-versions = "*" version = "1.9.0" - -[[package]] +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5, PyQt4 and PySide) and additional custom QWidgets." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "regex" +version = "2020.11.13" +description = "Alternative regular expression module, to replace re." category = "dev" -description = "Alternative regular expression module, to replace re." -marker = "python_version > \"2.7\"" -name = "regex" optional = false python-versions = "*" -version = "2020.7.14" [[package]] -category = "main" -description = "Python HTTP for Humans." name = "requests" +version = "2.25.1" +description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" +chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +PySocks = {version = ">=1.5.6,<1.5.7 || >1.5.7", optional = true, markers = "extra == \"socks\""} +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] -category = "main" -description = "Send file to trash natively under Mac OS X, Windows and Linux." name = "send2trash" +version = "1.5.0" +description = "Send file to trash natively under Mac OS X, Windows and Linux." +category = "main" optional = false python-versions = "*" -version = "1.5.0" [[package]] -category = "main" -description = "Python 2 and 3 compatibility utilities" name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" [[package]] -category = "main" -description = "A modern CSS selector implementation for Beautiful Soup." name = "soupsieve" +version = "2.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" optional = false -python-versions = "*" -version = "1.9.6" +python-versions = ">=3.5" [[package]] -category = "dev" -description = "Terminals served to xterm.js using Tornado websockets" name = "terminado" +version = "0.9.1" +description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.8.3" +python-versions = ">=3.6" [package.dependencies] -ptyprocess = "*" -pywinpty = ">=0.5" +ptyprocess = {version = "*", markers = "os_name != \"nt\""} +pywinpty = {version = ">=0.5", markers = "os_name == \"nt\""} tornado = ">=4" [[package]] -category = "dev" -description = "Test utilities for code working with files and commands" name = "testpath" +version = "0.4.4" +description = "Test utilities for code working with files and commands" +category = "dev" optional = false python-versions = "*" -version = "0.4.4" [package.extras] test = ["pathlib2"] [[package]] -category = "dev" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." name = "tornado" +version = "6.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" optional = false python-versions = ">= 3.5" -version = "6.0.4" [[package]] -category = "dev" -description = "Fast, Extensible Progress Meter" -marker = "python_version > \"2.7\"" name = "tqdm" +version = "4.54.1" +description = "Fast, Extensible Progress Meter" +category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.48.2" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" [package.extras] -dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] +dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown", "wheel"] [[package]] -category = "dev" -description = "Traitlets Python config system" name = "traitlets" +version = "5.0.5" +description = "Traitlets Python configuration system" +category = "dev" optional = false -python-versions = "*" -version = "4.3.3" +python-versions = ">=3.7" [package.dependencies] -decorator = "*" ipython-genutils = "*" -six = "*" [package.extras] -test = ["pytest", "mock"] +test = ["pytest"] [[package]] -category = "main" -description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" +version = "1.26.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -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)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] -category = "dev" -description = "Measures the displayed width of unicode strings in a terminal" name = "wcwidth" -optional = false -python-versions = "*" version = "0.2.5" - -[[package]] +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" category = "dev" -description = "Character encoding aliases for legacy web content" -name = "webencodings" optional = false python-versions = "*" -version = "0.5.1" [[package]] -category = "main" -description = "WSGI request and response object" name = "webob" +version = "1.8.6" +description = "WSGI request and response object" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -version = "1.8.6" [package.extras] docs = ["Sphinx (>=1.7.5)", "pylons-sphinx-themes"] testing = ["pytest (>=3.1.0)", "coverage", "pytest-cov", "pytest-xdist"] [[package]] -category = "dev" -description = "IPython HTML widgets for Jupyter" name = "widgetsnbextension" +version = "3.5.1" +description = "IPython HTML widgets for Jupyter" +category = "dev" optional = false python-versions = "*" -version = "3.5.1" [package.dependencies] notebook = ">=4.4.1" -[[package]] -category = "main" -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.1.0" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] - [metadata] -content-hash = "85d03342e458196cc35e890733a1dd3c48a504cda333b46114dd57c58b42c9b6" -lock-version = "1.0" -python-versions = "^3.7" +lock-version = "1.1" +python-versions = "^3.8" +content-hash = "4652673e61d57bf6f9a67c42f717aa0d014e0073a274183a8d47e65e0c251ffa" [metadata.files] anki = [ - {file = "anki-2.1.32-py3-none-any.whl", hash = "sha256:97cfc292876196572b3d037ab218e3c9014ec7b31744c82e9847a45e796e3fdd"}, -] -ankirspy = [ - {file = "ankirspy-2.1.32-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:6cd446155ee56f2557ecee6cfa42857ef44f4e5322a9fd5a06ff25a3bffc6980"}, - {file = "ankirspy-2.1.32-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:59cf16a23f7afabfe011302ae47833c13d57fcbfd7bbf9e2ff78c52cbffea106"}, - {file = "ankirspy-2.1.32-cp37-none-win_amd64.whl", hash = "sha256:e5d133cda5a849a5734cd12d3e7d29f34907116e97712d70c895232cbba9a802"}, - {file = "ankirspy-2.1.32-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:8358846c61b575b163fb12bfcb28ba12d44611606f04eef7230f374f9c31c2a4"}, - {file = "ankirspy-2.1.32-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ce71ae0e9695246cc58bd6c51b3ca5d8958a32fa3cee77843eb1ed95a35739ff"}, - {file = "ankirspy-2.1.32-cp38-none-win_amd64.whl", hash = "sha256:1962126aaf72b678bde10bebb5108f988d6888be35870c46ec2e14af7fedee1e"}, + {file = "anki-2.1.37-cp38-abi3-macosx_10_7_x86_64.whl", hash = "sha256:959bb06f0a0e29a43f604fd876bcd552961de11d1014b6c3c1e6007611051264"}, + {file = "anki-2.1.37-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:f7d7f504d96bbe799d13a819607498fd9a7c61394a85c810eae642b2c0575bd9"}, + {file = "anki-2.1.37-cp38-abi3-win_amd64.whl", hash = "sha256:4b36d056f3b25c2d780e1bec7b5fe9ff9c253c60f1e8200bda4378f2a0310eb3"}, ] appnope = [ - {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, - {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, + {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, + {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] argon2-cffi = [ {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"}, @@ -1090,68 +1073,80 @@ argon2-cffi = [ {file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"}, {file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"}, ] +async-generator = [ + {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, + {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, +] attrs = [ - {file = "attrs-20.1.0-py2.py3-none-any.whl", hash = "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"}, - {file = "attrs-20.1.0.tar.gz", hash = "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a"}, + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] beautifulsoup4 = [ - {file = "beautifulsoup4-4.9.1-py2-none-any.whl", hash = "sha256:e718f2342e2e099b640a34ab782407b7b676f47ee272d6739e60b8ea23829f2c"}, - {file = "beautifulsoup4-4.9.1-py3-none-any.whl", hash = "sha256:a6237df3c32ccfaee4fd201c8f5f9d9df619b93121d01353a64a73ce8c6ef9a8"}, - {file = "beautifulsoup4-4.9.1.tar.gz", hash = "sha256:73cc4d115b96f79c7d77c1c7f7a0a8d4c57860d1041df407dd1aae7f07a77fd7"}, + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, ] bleach = [ - {file = "bleach-3.1.5-py2.py3-none-any.whl", hash = "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f"}, - {file = "bleach-3.1.5.tar.gz", hash = "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"}, + {file = "bleach-3.2.1-py2.py3-none-any.whl", hash = "sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"}, + {file = "bleach-3.2.1.tar.gz", hash = "sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080"}, ] certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] cffi = [ - {file = "cffi-1.14.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82"}, - {file = "cffi-1.14.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4"}, - {file = "cffi-1.14.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e"}, - {file = "cffi-1.14.2-cp27-cp27m-win32.whl", hash = "sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c"}, - {file = "cffi-1.14.2-cp27-cp27m-win_amd64.whl", hash = "sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1"}, - {file = "cffi-1.14.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7"}, - {file = "cffi-1.14.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c"}, - {file = "cffi-1.14.2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731"}, - {file = "cffi-1.14.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0"}, - {file = "cffi-1.14.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e"}, - {file = "cffi-1.14.2-cp35-cp35m-win32.whl", hash = "sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487"}, - {file = "cffi-1.14.2-cp35-cp35m-win_amd64.whl", hash = "sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad"}, - {file = "cffi-1.14.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2"}, - {file = "cffi-1.14.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123"}, - {file = "cffi-1.14.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1"}, - {file = "cffi-1.14.2-cp36-cp36m-win32.whl", hash = "sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281"}, - {file = "cffi-1.14.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4"}, - {file = "cffi-1.14.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798"}, - {file = "cffi-1.14.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4"}, - {file = "cffi-1.14.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f"}, - {file = "cffi-1.14.2-cp37-cp37m-win32.whl", hash = "sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650"}, - {file = "cffi-1.14.2-cp37-cp37m-win_amd64.whl", hash = "sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15"}, - {file = "cffi-1.14.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa"}, - {file = "cffi-1.14.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c"}, - {file = "cffi-1.14.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75"}, - {file = "cffi-1.14.2-cp38-cp38-win32.whl", hash = "sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e"}, - {file = "cffi-1.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c"}, - {file = "cffi-1.14.2.tar.gz", hash = "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b"}, + {file = "cffi-1.14.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775"}, + {file = "cffi-1.14.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06"}, + {file = "cffi-1.14.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26"}, + {file = "cffi-1.14.4-cp27-cp27m-win32.whl", hash = "sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c"}, + {file = "cffi-1.14.4-cp27-cp27m-win_amd64.whl", hash = "sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b"}, + {file = "cffi-1.14.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d"}, + {file = "cffi-1.14.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca"}, + {file = "cffi-1.14.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698"}, + {file = "cffi-1.14.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b"}, + {file = "cffi-1.14.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293"}, + {file = "cffi-1.14.4-cp35-cp35m-win32.whl", hash = "sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2"}, + {file = "cffi-1.14.4-cp35-cp35m-win_amd64.whl", hash = "sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7"}, + {file = "cffi-1.14.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f"}, + {file = "cffi-1.14.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362"}, + {file = "cffi-1.14.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec"}, + {file = "cffi-1.14.4-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b"}, + {file = "cffi-1.14.4-cp36-cp36m-win32.whl", hash = "sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668"}, + {file = "cffi-1.14.4-cp36-cp36m-win_amd64.whl", hash = "sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009"}, + {file = "cffi-1.14.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb"}, + {file = "cffi-1.14.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d"}, + {file = "cffi-1.14.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03"}, + {file = "cffi-1.14.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01"}, + {file = "cffi-1.14.4-cp37-cp37m-win32.whl", hash = "sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e"}, + {file = "cffi-1.14.4-cp37-cp37m-win_amd64.whl", hash = "sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35"}, + {file = "cffi-1.14.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d"}, + {file = "cffi-1.14.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b"}, + {file = "cffi-1.14.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53"}, + {file = "cffi-1.14.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e"}, + {file = "cffi-1.14.4-cp38-cp38-win32.whl", hash = "sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d"}, + {file = "cffi-1.14.4-cp38-cp38-win_amd64.whl", hash = "sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375"}, + {file = "cffi-1.14.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909"}, + {file = "cffi-1.14.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd"}, + {file = "cffi-1.14.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a"}, + {file = "cffi-1.14.4-cp39-cp39-win32.whl", hash = "sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3"}, + {file = "cffi-1.14.4-cp39-cp39-win_amd64.whl", hash = "sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b"}, + {file = "cffi-1.14.4.tar.gz", hash = "sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c"}, ] chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, @@ -1176,17 +1171,13 @@ 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"}, -] ipykernel = [ - {file = "ipykernel-5.3.4-py3-none-any.whl", hash = "sha256:d6fbba26dba3cebd411382bc484f7bc2caa98427ae0ddb4ab37fe8bfeb5c7dd3"}, - {file = "ipykernel-5.3.4.tar.gz", hash = "sha256:9b2652af1607986a1b231c62302d070bc0534f564c393a5d9d130db9abbbe89d"}, + {file = "ipykernel-5.4.2-py3-none-any.whl", hash = "sha256:63b4b96c513e1138874934e3e783a8e5e13c02b9036e37107bfe042ac8955005"}, + {file = "ipykernel-5.4.2.tar.gz", hash = "sha256:e20ceb7e52cb4d250452e1230be76e0b2323f33bd46c6b2bc7abb6601740e182"}, ] ipython = [ - {file = "ipython-7.17.0-py3-none-any.whl", hash = "sha256:5a8f159ca8b22b9a0a1f2a28befe5ad2b703339afb58c2ffe0d7c8d7a3af5999"}, - {file = "ipython-7.17.0.tar.gz", hash = "sha256:b70974aaa2674b05eb86a910c02ed09956a33f2dd6c71afc60f0b128a77e7f28"}, + {file = "ipython-7.19.0-py3-none-any.whl", hash = "sha256:c987e8178ced651532b3b1ff9965925bfd445c279239697052561a9ab806d28f"}, + {file = "ipython-7.19.0.tar.gz", hash = "sha256:cbb2ef3d5961d44e6a963b9817d4ea4e1fa2eb589c371a470fed14d8d40cbd6a"}, ] ipython-genutils = [ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, @@ -1205,8 +1196,8 @@ jinja2 = [ {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, ] joblib = [ - {file = "joblib-0.16.0-py3-none-any.whl", hash = "sha256:d348c5d4ae31496b2aa060d6d9b787864dd204f9480baaa52d18850cb43e9f49"}, - {file = "joblib-0.16.0.tar.gz", hash = "sha256:8f52bf24c64b608bf0b2563e0e47d6fcf516abc8cfafe10cfd98ad66d94f92d6"}, + {file = "joblib-1.0.0-py3-none-any.whl", hash = "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f"}, + {file = "joblib-1.0.0.tar.gz", hash = "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24"}, ] json5 = [ {file = "json5-0.9.5-py2.py3-none-any.whl", hash = "sha256:af1a1b9a2850c7f62c23fde18be4749b3599fd302f494eebf957e2ada6b9e42c"}, @@ -1226,16 +1217,20 @@ jupyter-client = [ {file = "jupyter_client-6.1.7.tar.gz", hash = "sha256:49e390b36fe4b4226724704ea28d9fb903f1a3601b6882ce3105221cd09377a1"}, ] jupyter-console = [ - {file = "jupyter_console-6.1.0-py2.py3-none-any.whl", hash = "sha256:b392155112ec86a329df03b225749a0fa903aa80811e8eda55796a40b5e470d8"}, - {file = "jupyter_console-6.1.0.tar.gz", hash = "sha256:6f6ead433b0534909df789ea64f0a14cdf9b6b2360757756f08182be4b9e431b"}, + {file = "jupyter_console-6.2.0-py3-none-any.whl", hash = "sha256:1d80c06b2d85bfb10bd5cc731b3db18e9023bc81ab00491d3ac31f206490aee3"}, + {file = "jupyter_console-6.2.0.tar.gz", hash = "sha256:7f6194f4f4692d292da3f501c7f343ccd5e36c6a1becf7b7515e23e66d6bf1e9"}, ] jupyter-core = [ - {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, - {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, + {file = "jupyter_core-4.7.0-py3-none-any.whl", hash = "sha256:0a451c9b295e4db772bdd8d06f2f1eb31caeec0e81fbb77ba37d4a3024e3b315"}, + {file = "jupyter_core-4.7.0.tar.gz", hash = "sha256:aa1f9496ab3abe72da4efe0daab0cb2233997914581f9a071e07498c6add8ed3"}, ] jupyterlab = [ - {file = "jupyterlab-2.2.6-py3-none-any.whl", hash = "sha256:ae557386633fcb74359f436f2b87788a451260a07f2f14a1880fca8f4a9f64de"}, - {file = "jupyterlab-2.2.6.tar.gz", hash = "sha256:6554b022d2cd120100e165ec537c6511d70de7f89e253b3c667ea28f2a9263ff"}, + {file = "jupyterlab-2.2.9-py3-none-any.whl", hash = "sha256:59af02c26a15ec2d2862a15bc72e41ae304b406a0b0d3f4f705eeb7caf91902b"}, + {file = "jupyterlab-2.2.9.tar.gz", hash = "sha256:3be8f8edea173753dd838c1b6d3bbcb6f5c801121f824a477025c1b6a1d33dc6"}, +] +jupyterlab-pygments = [ + {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"}, + {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"}, ] jupyterlab-server = [ {file = "jupyterlab_server-1.2.0-py3-none-any.whl", hash = "sha256:55d256077bf13e5bc9e8fbd5aac51bef82f6315111cec6b712b9a5ededbba924"}, @@ -1249,8 +1244,8 @@ lunr = [ {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, ] markdown = [ - {file = "Markdown-3.2.2-py3-none-any.whl", hash = "sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"}, - {file = "Markdown-3.2.2.tar.gz", hash = "sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17"}, + {file = "Markdown-3.3.3-py3-none-any.whl", hash = "sha256:c109c15b7dc20a9ac454c9e6025927d44460b85bd039da028d85e2b6d0bcc328"}, + {file = "Markdown-3.3.3.tar.gz", hash = "sha256:5d9f2b5ca24bc4c7a390d22323ca4bad200368612b5aaa7796babf971d2b2f18"}, ] markupsafe = [ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, @@ -1280,11 +1275,6 @@ markupsafe = [ {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"}, ] mistune = [ @@ -1295,47 +1285,54 @@ mkdocs = [ {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] +nbclient = [ + {file = "nbclient-0.5.1-py3-none-any.whl", hash = "sha256:4d6b116187c795c99b9dba13d46e764d596574b14c296d60670c8dfe454db364"}, + {file = "nbclient-0.5.1.tar.gz", hash = "sha256:01e2d726d16eaf2cde6db74a87e2451453547e8832d142f73f72fddcd4fe0250"}, +] nbconvert = [ - {file = "nbconvert-5.6.1-py2.py3-none-any.whl", hash = "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee"}, - {file = "nbconvert-5.6.1.tar.gz", hash = "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523"}, + {file = "nbconvert-6.0.7-py3-none-any.whl", hash = "sha256:39e9f977920b203baea0be67eea59f7b37a761caa542abe80f5897ce3cf6311d"}, + {file = "nbconvert-6.0.7.tar.gz", hash = "sha256:cbbc13a86dfbd4d1b5dee106539de0795b4db156c894c2c5dc382062bbc29002"}, ] nbformat = [ - {file = "nbformat-5.0.7-py3-none-any.whl", hash = "sha256:ea55c9b817855e2dfcd3f66d74857342612a60b1f09653440f4a5845e6e3523f"}, - {file = "nbformat-5.0.7.tar.gz", hash = "sha256:54d4d6354835a936bad7e8182dcd003ca3dc0cedfee5a306090e04854343b340"}, + {file = "nbformat-5.0.8-py3-none-any.whl", hash = "sha256:aa9450c16d29286dc69b92ea4913c1bffe86488f90184445996ccc03a2f60382"}, + {file = "nbformat-5.0.8.tar.gz", hash = "sha256:f545b22138865bfbcc6b1ffe89ed5a2b8e2dc5d4fe876f2ca60d8e6f702a30f8"}, +] +nest-asyncio = [ + {file = "nest_asyncio-1.4.3-py3-none-any.whl", hash = "sha256:dbe032f3e9ff7f120e76be22bf6e7958e867aed1743e6894b8a9585fe8495cc9"}, + {file = "nest_asyncio-1.4.3.tar.gz", hash = "sha256:eaa09ef1353ebefae19162ad423eef7a12166bcc63866f8bff8f3635353cd9fa"}, ] nltk = [ {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, ] notebook = [ - {file = "notebook-6.1.3-py3-none-any.whl", hash = "sha256:964cc40cff68e473f3778aef9266e867f7703cb4aebdfd250f334efe02f64c86"}, - {file = "notebook-6.1.3.tar.gz", hash = "sha256:9990d51b9931a31e681635899aeb198b4c4b41586a9e87fbfaaed1a71d0a05b6"}, + {file = "notebook-6.1.5-py3-none-any.whl", hash = "sha256:508cf9dad7cdb3188f1aa27017dc78179029dfe83814fc505329f689bc2ab50f"}, + {file = "notebook-6.1.5.tar.gz", hash = "sha256:3db37ae834c5f3b6378381229d0e5dfcbfb558d08c8ce646b1ad355147f5e91d"}, ] orjson = [ - {file = "orjson-3.3.1-cp36-cp36m-macosx_10_7_x86_64.whl", hash = "sha256:0f33d28083819579976669f54ca79675d8e95fd5d75e7db21b798354ed8dd15b"}, - {file = "orjson-3.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4c290f1c0b6665d60181ee2f0ef631640d04ead2002ca4eadce4991ea5d6a4ed"}, - {file = "orjson-3.3.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf542f372162533550e86003d48664ab5fc1b44fb2b88923b9794cc8db6f0cf0"}, - {file = "orjson-3.3.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:28e6116ebd2082357bb9c66a76a3a1dc6aa4de0754801ac10b9903d31b752a1b"}, - {file = "orjson-3.3.1-cp36-none-win_amd64.whl", hash = "sha256:c4ac5a1d1767733708fd9b45cbbab3f8871af57b54b707a2dc6fddb47e51a81a"}, - {file = "orjson-3.3.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:0f11fd620b74fbdcf29021b3a9c36fb6e13efcdd63cbacc292d0786b54b4b2e8"}, - {file = "orjson-3.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e455c5b42a023f4777526c623d2e9ae415084de5130f93aefe689ea482de5f67"}, - {file = "orjson-3.3.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8c90083c67653d88b132820719e604250f26ba04229efe3149bf82ba2a08f8cf"}, - {file = "orjson-3.3.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:bc23eed41167b4454cddd51f72a7ee4163c33565c509bb9469adf56384b1cce2"}, - {file = "orjson-3.3.1-cp37-none-win_amd64.whl", hash = "sha256:3bff4765281da6fa8ddbbe692e5061f950d11aabdfe64837fb53ead4756e9af6"}, - {file = "orjson-3.3.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:1e19907c1ccf82976c2d111f3914a2c0697720b91908e8ef02405e4dc21c662a"}, - {file = "orjson-3.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:aa8332a3ee0fa03a331bea4f28cdcc4d363b53af2ea41630d7eb580422514a1f"}, - {file = "orjson-3.3.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:4ab9536c3776136303ab9e6432691d970e6aa5d27dbc2b5e0ca0d0db3e12f1c4"}, - {file = "orjson-3.3.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:28dc7e1f89440a68c1ccb937f6f0ae40fa3875de84f747262c00bc18aa25c5ec"}, - {file = "orjson-3.3.1-cp38-none-win_amd64.whl", hash = "sha256:fa4d5d734e76d9f21a94444fbf1de7eea185b355b324d38c8a7456ce63c3bbeb"}, - {file = "orjson-3.3.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b0533d6719b781db7563c478672d91faeac9ea810f30f16ebb5e917c4451b098"}, - {file = "orjson-3.3.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:a7d634eb69083ca5a49baf412625604813f9e3365cb869f445c388d15fe60122"}, - {file = "orjson-3.3.1.tar.gz", hash = "sha256:149d6a2bc71514826979b9d053f3df0c2397a99e2b87213ba71605a1626d662c"}, + {file = "orjson-3.4.6-cp36-cp36m-macosx_10_7_x86_64.whl", hash = "sha256:4e258f4696255de8038fd01ead8277a7c5c6d1e453cc7ca5aad8c1e9f74af62e"}, + {file = "orjson-3.4.6-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:283e54f0e2175ffe3f3acb20473da9d13f944a5faca6b066e0df2096ca8dda58"}, + {file = "orjson-3.4.6-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9864c587a009cc266fce02fbb2d99dd25c773bdd650d4728ef419686c4130380"}, + {file = "orjson-3.4.6-cp36-none-win_amd64.whl", hash = "sha256:9a861504727f3ded5e13ca321fb4187ace3300113c6bf1554088619bbb557f89"}, + {file = "orjson-3.4.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:3fe17a3f0f68b29a2f096817afd98ef680dec7c7577d12de6465e942cd9e4e71"}, + {file = "orjson-3.4.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:38f01ee249813d80e18eaeb5c434e026ddce631a7f1a93265f7035bc7e6621ff"}, + {file = "orjson-3.4.6-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:c961711a8e1ec688fcc978638a1b618c1bfff65929f99edecfa8b67ab26ec2de"}, + {file = "orjson-3.4.6-cp37-none-win_amd64.whl", hash = "sha256:218f164aa917b82e328f177c4121fb45c178b746f917c21739fc3eb5f5b7ca8b"}, + {file = "orjson-3.4.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:67d8e09030342d0153c86676cebdbca5cd12e257a436c8238a25e52f800de98a"}, + {file = "orjson-3.4.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:bac00616ee44c78c8a8bd7e3d6c394ff97d2a45e1b3f453d6a29ffce97b6ffca"}, + {file = "orjson-3.4.6-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:f5008f92ecf5d0cb0cb172d6d9aa76f48d54cc1b6abc4fc83f430d58de9148ba"}, + {file = "orjson-3.4.6-cp38-none-win_amd64.whl", hash = "sha256:5fe9097f622c7ad47a511a3d2189576b11d1be4b067f094089c45a01ae80b34f"}, + {file = "orjson-3.4.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7132aa4779388f0c0ef2d944efd7f170b41f9d5eadd69813b715afe05af23fbc"}, + {file = "orjson-3.4.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8b246b9234d920fb8f1373167e63254581639482e710ea515354979ec13a47a9"}, + {file = "orjson-3.4.6-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:b62c64d2336fe9e1a21f0b89f12946d988fd1feb365c2e6f90071c21aca3127d"}, + {file = "orjson-3.4.6-cp39-none-win_amd64.whl", hash = "sha256:a60db27bcba1645c0199ebe4edc1290a91ee22644dde61ee9257ebbacbf5d81e"}, + {file = "orjson-3.4.6.tar.gz", hash = "sha256:e1b4128baebf7968572343834b282794e20c5082f55f42b9675b04df0749e087"}, ] packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, + {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, + {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, ] pandocfilters = [ - {file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"}, + {file = "pandocfilters-1.4.3.tar.gz", hash = "sha256:bc63fbb50534b4b1f8ebe1860889289e8af94a23bff7445259592df25a3906eb"}, ] parso = [ {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, @@ -1350,50 +1347,71 @@ pickleshare = [ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] prometheus-client = [ - {file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"}, - {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, + {file = "prometheus_client-0.9.0-py2.py3-none-any.whl", hash = "sha256:b08c34c328e1bf5961f0b4352668e6c8f145b4a087e09b7296ef62cbe4693d35"}, + {file = "prometheus_client-0.9.0.tar.gz", hash = "sha256:9da7b32f02439d8c04f7777021c304ed51d9ec180604700c1ba72a4d44dceb03"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.6-py3-none-any.whl", hash = "sha256:683397077a64cd1f750b71c05afcfc6612a7300cb6932666531e5a54f38ea564"}, - {file = "prompt_toolkit-3.0.6.tar.gz", hash = "sha256:7630ab85a23302839a0f26b31cc24f518e6155dea1ed395ea61b42c45941b6a6"}, + {file = "prompt_toolkit-3.0.8-py3-none-any.whl", hash = "sha256:7debb9a521e0b1ee7d2fe96ee4bd60ef03c6492784de0547337ca4433e46aa63"}, + {file = "prompt_toolkit-3.0.8.tar.gz", hash = "sha256:25c95d2ac813909f813c93fde734b6e44406d1477a9faef7c915ff37d39c0a8c"}, ] 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"}, + {file = "protobuf-3.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:629b03fd3caae7f815b0c66b41273f6b1900a579e2ccb41ef4493a4f5fb84f3a"}, + {file = "protobuf-3.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5b7a637212cc9b2bcf85dd828b1178d19efdf74dbfe1ddf8cd1b8e01fdaaa7f5"}, + {file = "protobuf-3.14.0-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:43b554b9e73a07ba84ed6cf25db0ff88b1e06be610b37656e292e3cbb5437472"}, + {file = "protobuf-3.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:5e9806a43232a1fa0c9cf5da8dc06f6910d53e4390be1fa06f06454d888a9142"}, + {file = "protobuf-3.14.0-cp35-cp35m-win32.whl", hash = "sha256:1c51fda1bbc9634246e7be6016d860be01747354ed7015ebe38acf4452f470d2"}, + {file = "protobuf-3.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:4b74301b30513b1a7494d3055d95c714b560fbb630d8fb9956b6f27992c9f980"}, + {file = "protobuf-3.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:86a75477addde4918e9a1904e5c6af8d7b691f2a3f65587d73b16100fbe4c3b2"}, + {file = "protobuf-3.14.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ecc33531a213eee22ad60e0e2aaea6c8ba0021f0cce35dbf0ab03dee6e2a23a1"}, + {file = "protobuf-3.14.0-cp36-cp36m-win32.whl", hash = "sha256:72230ed56f026dd664c21d73c5db73ebba50d924d7ba6b7c0d81a121e390406e"}, + {file = "protobuf-3.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:0fc96785262042e4863b3f3b5c429d4636f10d90061e1840fce1baaf59b1a836"}, + {file = "protobuf-3.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4e75105c9dfe13719b7293f75bd53033108f4ba03d44e71db0ec2a0e8401eafd"}, + {file = "protobuf-3.14.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2a7e2fe101a7ace75e9327b9c946d247749e564a267b0515cf41dfe450b69bac"}, + {file = "protobuf-3.14.0-cp37-cp37m-win32.whl", hash = "sha256:b0d5d35faeb07e22a1ddf8dce620860c8fe145426c02d1a0ae2688c6e8ede36d"}, + {file = "protobuf-3.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:8971c421dbd7aad930c9bd2694122f332350b6ccb5202a8b7b06f3f1a5c41ed5"}, + {file = "protobuf-3.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9616f0b65a30851e62f1713336c931fcd32c057202b7ff2cfbfca0fc7d5e3043"}, + {file = "protobuf-3.14.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:22bcd2e284b3b1d969c12e84dc9b9a71701ec82d8ce975fdda19712e1cfd4e00"}, + {file = "protobuf-3.14.0-py2.py3-none-any.whl", hash = "sha256:0e247612fadda953047f53301a7b0407cb0c3cb4ae25a6fde661597a04039b3c"}, + {file = "protobuf-3.14.0.tar.gz", hash = "sha256:1d63eb389347293d8915fb47bee0951c7b5dab522a4a60118b9a18f33e21f8ce"}, ] 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"}, + {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"}, + {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"}, + {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"}, + {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"}, + {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"}, + {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"}, + {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"}, + {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"}, + {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"}, + {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"}, + {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"}, + {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"}, + {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"}, + {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"}, + {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"}, + {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"}, + {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"}, + {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"}, + {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"}, + {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"}, + {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"}, + {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"}, + {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"}, + {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"}, + {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"}, + {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"}, + {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"}, + {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, ] ptyprocess = [ {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, ] +py = [ + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, +] pyaudio = [ {file = "PyAudio-0.2.11-cp27-cp27m-win32.whl", hash = "sha256:f78d543a98b730e64621ebf7f3e2868a79ade0a373882ef51c0293455ffa8e6e"}, {file = "PyAudio-0.2.11-cp27-cp27m-win_amd64.whl", hash = "sha256:259bb9c1363be895b4f9a97e320a6017dd06bc540728c1a04eb4a7b6fe75035b"}, @@ -1410,33 +1428,36 @@ pycparser = [ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] pygments = [ - {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, - {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, + {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"}, + {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pyrsistent = [ - {file = "pyrsistent-0.16.0.tar.gz", hash = "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"}, + {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, +] +pysocks = [ + {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, + {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, + {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, ] 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"}, ] pywin32 = [ - {file = "pywin32-228-cp27-cp27m-win32.whl", hash = "sha256:37dc9935f6a383cc744315ae0c2882ba1768d9b06700a70f35dc1ce73cd4ba9c"}, - {file = "pywin32-228-cp27-cp27m-win_amd64.whl", hash = "sha256:11cb6610efc2f078c9e6d8f5d0f957620c333f4b23466931a247fb945ed35e89"}, - {file = "pywin32-228-cp35-cp35m-win32.whl", hash = "sha256:1f45db18af5d36195447b2cffacd182fe2d296849ba0aecdab24d3852fbf3f80"}, - {file = "pywin32-228-cp35-cp35m-win_amd64.whl", hash = "sha256:6e38c44097a834a4707c1b63efa9c2435f5a42afabff634a17f563bc478dfcc8"}, - {file = "pywin32-228-cp36-cp36m-win32.whl", hash = "sha256:ec16d44b49b5f34e99eb97cf270806fdc560dff6f84d281eb2fcb89a014a56a9"}, - {file = "pywin32-228-cp36-cp36m-win_amd64.whl", hash = "sha256:a60d795c6590a5b6baeacd16c583d91cce8038f959bd80c53bd9a68f40130f2d"}, - {file = "pywin32-228-cp37-cp37m-win32.whl", hash = "sha256:af40887b6fc200eafe4d7742c48417529a8702dcc1a60bf89eee152d1d11209f"}, - {file = "pywin32-228-cp37-cp37m-win_amd64.whl", hash = "sha256:00eaf43dbd05ba6a9b0080c77e161e0b7a601f9a3f660727a952e40140537de7"}, - {file = "pywin32-228-cp38-cp38-win32.whl", hash = "sha256:fa6ba028909cfc64ce9e24bcf22f588b14871980d9787f1e2002c99af8f1850c"}, - {file = "pywin32-228-cp38-cp38-win_amd64.whl", hash = "sha256:9b3466083f8271e1a5eb0329f4e0d61925d46b40b195a33413e0905dccb285e8"}, - {file = "pywin32-228-cp39-cp39-win32.whl", hash = "sha256:ed74b72d8059a6606f64842e7917aeee99159ebd6b8d6261c518d002837be298"}, - {file = "pywin32-228-cp39-cp39-win_amd64.whl", hash = "sha256:8319bafdcd90b7202c50d6014efdfe4fde9311b3ff15fd6f893a45c0868de203"}, + {file = "pywin32-300-cp35-cp35m-win32.whl", hash = "sha256:1c204a81daed2089e55d11eefa4826c05e604d27fe2be40b6bf8db7b6a39da63"}, + {file = "pywin32-300-cp35-cp35m-win_amd64.whl", hash = "sha256:350c5644775736351b77ba68da09a39c760d75d2467ecec37bd3c36a94fbed64"}, + {file = "pywin32-300-cp36-cp36m-win32.whl", hash = "sha256:a3b4c48c852d4107e8a8ec980b76c94ce596ea66d60f7a697582ea9dce7e0db7"}, + {file = "pywin32-300-cp36-cp36m-win_amd64.whl", hash = "sha256:27a30b887afbf05a9cbb05e3ffd43104a9b71ce292f64a635389dbad0ed1cd85"}, + {file = "pywin32-300-cp37-cp37m-win32.whl", hash = "sha256:d7e8c7efc221f10d6400c19c32a031add1c4a58733298c09216f57b4fde110dc"}, + {file = "pywin32-300-cp37-cp37m-win_amd64.whl", hash = "sha256:8151e4d7a19262d6694162d6da85d99a16f8b908949797fd99c83a0bfaf5807d"}, + {file = "pywin32-300-cp38-cp38-win32.whl", hash = "sha256:fbb3b1b0fbd0b4fc2a3d1d81fe0783e30062c1abed1d17c32b7879d55858cfae"}, + {file = "pywin32-300-cp38-cp38-win_amd64.whl", hash = "sha256:60a8fa361091b2eea27f15718f8eb7f9297e8d51b54dbc4f55f3d238093d5190"}, + {file = "pywin32-300-cp39-cp39-win32.whl", hash = "sha256:638b68eea5cfc8def537e43e9554747f8dee786b090e47ead94bfdafdb0f2f50"}, + {file = "pywin32-300-cp39-cp39-win_amd64.whl", hash = "sha256:b1609ce9bd5c411b81f941b246d683d6508992093203d4eb7f278f4ed1085c3f"}, ] pywinpty = [ {file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"}, @@ -1464,69 +1485,90 @@ pyyaml = [ {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, ] pyzmq = [ - {file = "pyzmq-19.0.2-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:59f1e54627483dcf61c663941d94c4af9bf4163aec334171686cdaee67974fe5"}, - {file = "pyzmq-19.0.2-cp27-cp27m-win32.whl", hash = "sha256:c36ffe1e5aa35a1af6a96640d723d0d211c5f48841735c2aa8d034204e87eb87"}, - {file = "pyzmq-19.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:0a422fc290d03958899743db091f8154958410fc76ce7ee0ceb66150f72c2c97"}, - {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c20dd60b9428f532bc59f2ef6d3b1029a28fc790d408af82f871a7db03e722ff"}, - {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d46fb17f5693244de83e434648b3dbb4f4b0fec88415d6cbab1c1452b6f2ae17"}, - {file = "pyzmq-19.0.2-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:f1a25a61495b6f7bb986accc5b597a3541d9bd3ef0016f50be16dbb32025b302"}, - {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ab0d01148d13854de716786ca73701012e07dff4dfbbd68c4e06d8888743526e"}, - {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:720d2b6083498a9281eaee3f2927486e9fe02cd16d13a844f2e95217f243efea"}, - {file = "pyzmq-19.0.2-cp35-cp35m-win32.whl", hash = "sha256:29d51279060d0a70f551663bc592418bcad7f4be4eea7b324f6dd81de05cb4c1"}, - {file = "pyzmq-19.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:5120c64646e75f6db20cc16b9a94203926ead5d633de9feba4f137004241221d"}, - {file = "pyzmq-19.0.2-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:8a6ada5a3f719bf46a04ba38595073df8d6b067316c011180102ba2a1925f5b5"}, - {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fa411b1d8f371d3a49d31b0789eb6da2537dadbb2aef74a43aa99a78195c3f76"}, - {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:00dca814469436455399660247d74045172955459c0bd49b54a540ce4d652185"}, - {file = "pyzmq-19.0.2-cp36-cp36m-win32.whl", hash = "sha256:046b92e860914e39612e84fa760fc3f16054d268c11e0e25dcb011fb1bc6a075"}, - {file = "pyzmq-19.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99cc0e339a731c6a34109e5c4072aaa06d8e32c0b93dc2c2d90345dd45fa196c"}, - {file = "pyzmq-19.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e36f12f503511d72d9bdfae11cadbadca22ff632ff67c1b5459f69756a029c19"}, - {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c40fbb2b9933369e994b837ee72193d6a4c35dfb9a7c573257ef7ff28961272c"}, - {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5d9fc809aa8d636e757e4ced2302569d6e60e9b9c26114a83f0d9d6519c40493"}, - {file = "pyzmq-19.0.2-cp37-cp37m-win32.whl", hash = "sha256:3fa6debf4bf9412e59353defad1f8035a1e68b66095a94ead8f7a61ae90b2675"}, - {file = "pyzmq-19.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:73483a2caaa0264ac717af33d6fb3f143d8379e60a422730ee8d010526ce1913"}, - {file = "pyzmq-19.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36ab114021c0cab1a423fe6689355e8f813979f2c750968833b318c1fa10a0fd"}, - {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8b66b94fe6243d2d1d89bca336b2424399aac57932858b9a30309803ffc28112"}, - {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:654d3e06a4edc566b416c10293064732516cf8871a4522e0a2ba00cc2a2e600c"}, - {file = "pyzmq-19.0.2-cp38-cp38-win32.whl", hash = "sha256:276ad604bffd70992a386a84bea34883e696a6b22e7378053e5d3227321d9702"}, - {file = "pyzmq-19.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:09d24a80ccb8cbda1af6ed8eb26b005b6743e58e9290566d2a6841f4e31fa8e0"}, - {file = "pyzmq-19.0.2-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:c1a31cd42905b405530e92bdb70a8a56f048c8a371728b8acf9d746ecd4482c0"}, - {file = "pyzmq-19.0.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7e7f930039ee0c4c26e4dfee015f20bd6919cd8b97c9cd7afbde2923a5167b6"}, - {file = "pyzmq-19.0.2.tar.gz", hash = "sha256:296540a065c8c21b26d63e3cea2d1d57902373b16e4256afe46422691903a438"}, + {file = "pyzmq-20.0.0-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:523d542823cabb94065178090e05347bd204365f6e7cb260f0071c995d392fc2"}, + {file = "pyzmq-20.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:225774a48ed7414c0395335e7123ef8c418dbcbe172caabdc2496133b03254c2"}, + {file = "pyzmq-20.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:bc7dd697356b31389d5118b9bcdef3e8d8079e8181800c4e8d72dccd56e1ff68"}, + {file = "pyzmq-20.0.0-cp35-cp35m-win32.whl", hash = "sha256:d81184489369ec325bd50ba1c935361e63f31f578430b9ad95471899361a8253"}, + {file = "pyzmq-20.0.0-cp35-cp35m-win_amd64.whl", hash = "sha256:7113eb93dcd0a5750c65d123ed0099e036a3a3f2dcb48afedd025ffa125c983b"}, + {file = "pyzmq-20.0.0-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:b62113eeb9a0649cebed9b21fd578f3a0175ef214a2a91dcb7b31bbf55805295"}, + {file = "pyzmq-20.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f0beef935efe78a63c785bb21ed56c1c24448511383e3994927c8bb2caf5e714"}, + {file = "pyzmq-20.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:46250789730489009fe139cbf576679557c070a6a3628077d09a4153d52fd381"}, + {file = "pyzmq-20.0.0-cp36-cp36m-win32.whl", hash = "sha256:bf755905a7d30d2749079611b9a89924c1f2da2695dc09ce221f42122c9808e3"}, + {file = "pyzmq-20.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2742e380d186673eee6a570ef83d4568741945434ba36d92b98d36cdbfedbd44"}, + {file = "pyzmq-20.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1e9b75a119606732023a305d1c214146c09a91f8116f6aff3e8b7d0a60b6f0ff"}, + {file = "pyzmq-20.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:03638e46d486dd1c118e03c8bf9c634bdcae679600eac6573ae1e54906de7c2f"}, + {file = "pyzmq-20.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:63ee08e35be72fdd7568065a249a5b5cf51a2e8ab6ee63cf9f73786fcb9e710b"}, + {file = "pyzmq-20.0.0-cp37-cp37m-win32.whl", hash = "sha256:c95dda497a7c1b1e734b5e8353173ca5dd7b67784d8821d13413a97856588057"}, + {file = "pyzmq-20.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:cc09c5cd1a4332611c8564d65e6a432dc6db3e10793d0254da9fa1e31d9ffd6d"}, + {file = "pyzmq-20.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6e24907857c80dc67692e31f5bf3ad5bf483ee0142cec95b3d47e2db8c43bdda"}, + {file = "pyzmq-20.0.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:53706f4a792cdae422121fb6a5e65119bad02373153364fc9d004cf6a90394de"}, + {file = "pyzmq-20.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:895695be380f0f85d2e3ec5ccf68a93c92d45bd298567525ad5633071589872c"}, + {file = "pyzmq-20.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d92c7f41a53ece82b91703ea433c7d34143248cf0cead33aa11c5fc621c764bf"}, + {file = "pyzmq-20.0.0-cp38-cp38-win32.whl", hash = "sha256:309d763d89ec1845c0e0fa14e1fb6558fd8c9ef05ed32baec27d7a8499cc7bb0"}, + {file = "pyzmq-20.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:0e554fd390021edbe0330b67226325a820b0319c5b45e1b0a59bf22ccc36e793"}, + {file = "pyzmq-20.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cfa54a162a7b32641665e99b2c12084555afe9fc8fe80ec8b2f71a57320d10e1"}, + {file = "pyzmq-20.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:dc2f48b575dff6edefd572f1ac84cf0c3f18ad5fcf13384de32df740a010594a"}, + {file = "pyzmq-20.0.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:5efe02bdcc5eafcac0aab531292294298f0ab8d28ed43be9e507d0e09173d1a4"}, + {file = "pyzmq-20.0.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0af84f34f27b5c6a0e906c648bdf46d4caebf9c8e6e16db0728f30a58141cad6"}, + {file = "pyzmq-20.0.0-cp39-cp39-win32.whl", hash = "sha256:c63fafd2556d218368c51d18588f8e6f8d86d09d493032415057faf6de869b34"}, + {file = "pyzmq-20.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:f110a4d3f8f01209eec304ed542f6c8054cce9b0f16dfe3d571e57c290e4e133"}, + {file = "pyzmq-20.0.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4d9259a5eb3f71abbaf61f165cacf42240bfeea3783bebd8255341abdfe206f1"}, + {file = "pyzmq-20.0.0.tar.gz", hash = "sha256:824ad5888331aadeac772bce27e1c2fbcab82fade92edbd234542c4e12f0dca9"}, ] qtconsole = [ - {file = "qtconsole-4.7.6-py2.py3-none-any.whl", hash = "sha256:570b9e1dd4f9b727699b0ed04c6943d9d32d5a2085aa69d82d814e039bbcf74b"}, - {file = "qtconsole-4.7.6.tar.gz", hash = "sha256:6c24397c19a49a5cf69582c931db4b0f6b00a78530a2bfd122936f2ebfae2fef"}, + {file = "qtconsole-5.0.1-py3-none-any.whl", hash = "sha256:4d70967aeb62a5bd13a109d61b169a3cf844afc24a35c11f5518574bb8abe670"}, + {file = "qtconsole-5.0.1.tar.gz", hash = "sha256:4d7dd4eae8a90d0b2b19b31794b30f137238463998989734a3acb8a53b506bab"}, ] qtpy = [ {file = "QtPy-1.9.0-py2.py3-none-any.whl", hash = "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea"}, {file = "QtPy-1.9.0.tar.gz", hash = "sha256:2db72c44b55d0fe1407be8fba35c838ad0d6d3bb81f23007886dc1fc0f459c8d"}, ] regex = [ - {file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, - {file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"}, - {file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"}, - {file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"}, - {file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"}, - {file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a"}, - {file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, - {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, - {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, + {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, + {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, + {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, + {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, + {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, + {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, + {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, + {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, + {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, + {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, + {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, + {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, + {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] send2trash = [ {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"}, @@ -1537,39 +1579,71 @@ six = [ {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] soupsieve = [ - {file = "soupsieve-1.9.6-py2.py3-none-any.whl", hash = "sha256:feb1e937fa26a69e08436aad4a9037cd7e1d4c7212909502ba30701247ff8abd"}, - {file = "soupsieve-1.9.6.tar.gz", hash = "sha256:7985bacc98c34923a439967c1a602dc4f1e15f923b6fcf02344184f86cc7efaa"}, + {file = "soupsieve-2.1-py3-none-any.whl", hash = "sha256:4bb21a6ee4707bf43b61230e80740e71bfe56e55d1f1f50924b087bb2975c851"}, + {file = "soupsieve-2.1.tar.gz", hash = "sha256:6dc52924dc0bc710a5d16794e6b3480b2c7c08b07729505feab2b2c16661ff6e"}, ] terminado = [ - {file = "terminado-0.8.3-py2.py3-none-any.whl", hash = "sha256:a43dcb3e353bc680dd0783b1d9c3fc28d529f190bc54ba9a229f72fe6e7a54d7"}, - {file = "terminado-0.8.3.tar.gz", hash = "sha256:4804a774f802306a7d9af7322193c5390f1da0abb429e082a10ef1d46e6fb2c2"}, + {file = "terminado-0.9.1-py3-none-any.whl", hash = "sha256:c55f025beb06c2e2669f7ba5a04f47bb3304c30c05842d4981d8f0fc9ab3b4e3"}, + {file = "terminado-0.9.1.tar.gz", hash = "sha256:3da72a155b807b01c9e8a5babd214e052a0a45a975751da3521a1c3381ce6d76"}, ] testpath = [ {file = "testpath-0.4.4-py2.py3-none-any.whl", hash = "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4"}, {file = "testpath-0.4.4.tar.gz", hash = "sha256:60e0a3261c149755f4399a1fff7d37523179a70fdc3abdf78de9fc2604aeec7e"}, ] tornado = [ - {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, - {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, - {file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"}, - {file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"}, - {file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"}, - {file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"}, - {file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"}, - {file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"}, - {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, ] tqdm = [ - {file = "tqdm-4.48.2-py2.py3-none-any.whl", hash = "sha256:1a336d2b829be50e46b84668691e0a2719f26c97c62846298dd5ae2937e4d5cf"}, - {file = "tqdm-4.48.2.tar.gz", hash = "sha256:564d632ea2b9cb52979f7956e093e831c28d441c11751682f84c86fc46e4fd21"}, + {file = "tqdm-4.54.1-py2.py3-none-any.whl", hash = "sha256:d4f413aecb61c9779888c64ddf0c62910ad56dcbe857d8922bb505d4dbff0df1"}, + {file = "tqdm-4.54.1.tar.gz", hash = "sha256:38b658a3e4ecf9b4f6f8ff75ca16221ae3378b2e175d846b6b33ea3a20852cf5"}, ] traitlets = [ - {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, - {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, + {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, + {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, ] urllib3 = [ - {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, - {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -1587,7 +1661,3 @@ widgetsnbextension = [ {file = "widgetsnbextension-3.5.1-py2.py3-none-any.whl", hash = "sha256:bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd"}, {file = "widgetsnbextension-3.5.1.tar.gz", hash = "sha256:079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7"}, ] -zipp = [ - {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, - {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, -] diff --git a/pyproject.toml b/pyproject.toml index 94649a9..418a362 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Self-hosted Anki Sync Server." authors = ["Vikash Kothary "] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.8" anki = "^2.1.36" beautifulsoup4 = "^4.9.1" requests = "^2.24.0" From a9343028dbd9f083562cf9325eed592b516a6c2e Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Wed, 23 Dec 2020 22:46:01 +0000 Subject: [PATCH 74/75] Run lock requirements script --- src/requirements-dev.txt | 121 ++++++++++++++++++++------------------- src/requirements.txt | 32 +++++------ 2 files changed, 77 insertions(+), 76 deletions(-) diff --git a/src/requirements-dev.txt b/src/requirements-dev.txt index 8d1c5aa..007ad7c 100644 --- a/src/requirements-dev.txt +++ b/src/requirements-dev.txt @@ -1,88 +1,91 @@ THE FILE WAS GENERATED BY POETRY, DO NOT EDIT! -Warning: The lock file is not up to date with the latest changes in pyproject.toml. You may be getting outdated dependencies. Run update to update them. -anki==2.1.36; python_version >= "3.7" -ankirspy==2.1.36; python_version >= "3.7" -appnope==0.1.0; sys_platform == "darwin" and python_version >= "3.7" or sys_platform == "darwin" and python_version >= "3.7" and platform_system == "Darwin" or platform_system == "Darwin" and python_version >= "3.7" + +anki==2.1.37; python_version >= "3.8" +appnope==0.1.2; platform_system == "Darwin" and python_version >= "3.7" and sys_platform == "darwin" argon2-cffi==20.1.0; python_version >= "3.5" -attrs==20.1.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" +async-generator==1.10; python_version >= "3.6" +attrs==20.3.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" backcall==0.2.0; python_version >= "3.7" -beautifulsoup4==4.9.1 -bleach==3.1.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" -certifi==2020.6.20; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -cffi==1.14.2; python_version >= "3.5" -chardet==3.0.4; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +beautifulsoup4==4.9.3 +bleach==3.2.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +certifi==2020.12.5; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" +cffi==1.14.4; implementation_name === "pypy" and python_version >= "3.5" +chardet==4.0.0; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" click==7.1.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" -colorama==0.4.3; python_version >= "3.7" and sys_platform == "win32" and python_full_version < "3.0.0" or python_version >= "3.7" and sys_platform == "win32" and python_full_version >= "3.5.0" +colorama==0.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.5.0" decorator==4.4.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.2.0") -defusedxml==0.6.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +defusedxml==0.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" distro==1.5.0 -entrypoints==0.3; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "2.7" +entrypoints==0.3; python_version >= "3.6" future==0.18.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5" -idna==2.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -importlib-metadata==1.7.0; python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5" -ipykernel==5.3.4; python_version >= "3.5" -ipython-genutils==0.2.0; python_version >= "3.5" -ipython==7.17.0; python_version >= "3.7" +idna==2.10; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" +ipykernel==5.4.2; python_version >= "3.6" +ipython-genutils==0.2.0; python_version >= "3.7" +ipython==7.19.0; python_version >= "3.7" ipywidgets==7.5.1 jedi==0.17.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -jinja2==2.11.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" -joblib==0.16.0; python_version >= "3.6" +jinja2==2.11.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +joblib==1.0.0; python_version >= "3.6" json5==0.9.5; python_version >= "3.5" jsonschema==3.2.0; python_version >= "3.5" -jupyter-client==6.1.7; python_version >= "3.5" -jupyter-console==6.1.0; python_version >= "3.5" -jupyter-core==4.6.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_version >= "3.5" and python_full_version >= "3.5.0" +jupyter-client==6.1.7; python_version >= "3.6" +jupyter-console==6.2.0; python_version >= "3.6" +jupyter-core==4.7.0; python_version >= "3.6" jupyter==1.0.0 +jupyterlab-pygments==0.1.2; python_version >= "3.6" jupyterlab-server==1.2.0; python_version >= "3.5" -jupyterlab==2.2.6; python_version >= "3.5" +jupyterlab==2.2.9; python_version >= "3.5" livereload==2.6.3; python_version >= "3.5" lunr==0.5.8; python_version >= "3.5" -markdown==3.2.2; python_version >= "3.5" +markdown==3.3.3; python_version >= "3.6" markupsafe==1.1.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" -mistune==0.8.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +mistune==0.8.4; python_version >= "3.6" mkdocs==1.1.2; python_version >= "3.5" -nbconvert==5.6.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" -nbformat==5.0.7; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" +nbclient==0.5.1; python_version >= "3.6" +nbconvert==6.0.7; python_version >= "3.6" +nbformat==5.0.8; python_version >= "3.6" +nest-asyncio==1.4.3; python_version >= "3.6" nltk==3.5; python_version >= "3.5" -notebook==6.1.3; python_version >= "3.5" -orjson==3.3.1; platform_machine == "x86_64" and python_version >= "3.7" -packaging==20.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" -pandocfilters==1.4.2; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +notebook==6.1.5; python_version >= "3.5" +orjson==3.4.6; python_version >= "3.8" +packaging==20.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +pandocfilters==1.4.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" parso==0.7.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -pexpect==4.8.0; python_version >= "3.7" and sys_platform != "win32" +pexpect==4.8.0; sys_platform != "win32" and python_version >= "3.7" pickleshare==0.7.5; python_version >= "3.7" -prometheus-client==0.8.0; python_version >= "3.5" -prompt-toolkit==3.0.6; python_full_version >= "3.6.1" and python_version >= "3.7" -protobuf==3.13.0; python_version >= "3.7" -psutil==5.7.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") -ptyprocess==0.6.0; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") or python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") and os_name != "nt" or python_full_version >= "3.4.0" and python_version >= "3.7" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") or python_full_version >= "3.4.0" and python_version >= "3.7" and sys_platform != "win32" and (python_version >= "3.3" and sys_platform != "win32" or sys_platform != "win32") and os_name != "nt" +prometheus-client==0.9.0; python_version >= "3.5" +prompt-toolkit==3.0.8; python_full_version >= "3.6.1" and python_version >= "3.7" +protobuf==3.14.0; python_version >= "3.8" +psutil==5.8.0; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") +ptyprocess==0.6.0; os_name != "nt" and python_version >= "3.7" and sys_platform != "win32" +py==1.10.0; python_version >= "3.5" and python_full_version < "3.0.0" and implementation_name === "pypy" or implementation_name === "pypy" and python_version >= "3.5" and python_full_version >= "3.4.0" pyaudio==0.2.11 pycparser==2.20; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" -pygments==2.6.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -pyparsing==2.4.7; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" -pyrsistent==0.16.0; python_version >= "3.5" +pygments==2.7.3; python_version >= "3.7" +pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +pyrsistent==0.17.3; python_version >= "3.5" +pysocks==1.7.1; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" python-dateutil==2.8.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5" -pywin32==228; python_version >= "2.7" and python_version < "3.0" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version >= "2.7" and python_version < "3.0" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.0" and python_version < "3.1" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.0" and python_version < "3.1" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.1" and python_version < "3.2" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.1" and python_version < "3.2" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.2" and python_version < "3.3" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.2" and python_version < "3.3" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.3" and python_version < "3.4" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.3" and python_version < "3.4" and python_full_version >= "3.5.0" and sys_platform == "win32" or python_version > "3.4" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version > "3.4" and python_full_version >= "3.5.0" and sys_platform == "win32" -pywinpty==0.5.7; python_version >= "3.5" and python_full_version < "3.0.0" and os_name == "nt" or python_full_version >= "3.4.0" and python_version >= "3.5" and os_name == "nt" +pywin32==300; sys_platform == "win32" and python_version >= "3.6" +pywinpty==0.5.7; os_name == "nt" and python_version >= "3.6" pyyaml==5.3.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" -pyzmq==19.0.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.5" -qtconsole==4.7.6 -qtpy==1.9.0 -regex==2020.7.14; python_version >= "3.5" -requests==2.24.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") +pyzmq==20.0.0; python_version >= "3.6" +qtconsole==5.0.1; python_version >= "3.6" +qtpy==1.9.0; python_version >= "3.6" +regex==2020.11.13; python_version >= "3.5" +requests==2.25.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") send2trash==1.5.0 -six==1.15.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -soupsieve==1.9.6; python_version >= "3.7" -terminado==0.8.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" -testpath==0.4.4; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" -tornado==6.0.4; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" -tqdm==4.48.2; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.2.0" and python_version >= "3.5" -traitlets==4.3.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -urllib3==1.25.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.7" -wcwidth==0.2.5; python_full_version >= "3.6.1" and python_version >= "3.5" -webencodings==0.5.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +six==1.15.0; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" +soupsieve==2.1; python_version >= "3.8" +terminado==0.9.1; python_version >= "3.6" +testpath==0.4.4; python_version >= "3.6" +tornado==6.1; python_version >= "3.6" +tqdm==4.54.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_version >= "3.5" and python_full_version >= "3.4.0" +traitlets==5.0.5; python_version >= "3.7" +urllib3==1.26.2; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.8" +wcwidth==0.2.5; python_full_version >= "3.6.1" and python_version >= "3.6" +webencodings==0.5.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" webob==1.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") widgetsnbextension==3.5.1 -zipp==3.1.0; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5") -e src/. diff --git a/src/requirements.txt b/src/requirements.txt index 77ba117..251170b 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,24 +1,22 @@ THE FILE WAS GENERATED BY POETRY, DO NOT EDIT! -Warning: The lock file is not up to date with the latest changes in pyproject.toml. You may be getting outdated dependencies. Run update to update them. -anki==2.1.36; python_version >= "3.7" -ankirspy==2.1.36; python_version >= "3.7" -beautifulsoup4==4.9.1 -certifi==2020.6.20; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -chardet==3.0.4; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" + +anki==2.1.37; python_version >= "3.8" +beautifulsoup4==4.9.3 +certifi==2020.12.5; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" +chardet==4.0.0; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" decorator==4.4.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.2.0") distro==1.5.0 -idna==2.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" -importlib-metadata==1.7.0; python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5" -markdown==3.2.2; python_version >= "3.5" -orjson==3.3.1; platform_machine == "x86_64" and python_version >= "3.7" -protobuf==3.13.0; python_version >= "3.7" -psutil==5.7.2; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") +idna==2.10; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" +markdown==3.3.3; python_version >= "3.6" +orjson==3.4.6; python_version >= "3.8" +protobuf==3.14.0; python_version >= "3.8" +psutil==5.8.0; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") pyaudio==0.2.11 -requests==2.24.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") +pysocks==1.7.1; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.8" +requests==2.25.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") send2trash==1.5.0 -six==1.15.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7" -soupsieve==1.9.6; python_version >= "3.7" -urllib3==1.25.10; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.7" +six==1.15.0; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.8" +soupsieve==2.1; python_version >= "3.8" +urllib3==1.26.2; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.8" webob==1.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") -zipp==3.1.0; python_version < "3.8" and python_version >= "3.6" and (python_version >= "3.5" and python_full_version < "3.0.0" and python_version < "3.8" or python_full_version >= "3.5.0" and python_version < "3.8" and python_version >= "3.5") From 4a71447b1d7bd0a7712270ada7bbfae0c8bafcc5 Mon Sep 17 00:00:00 2001 From: Vikash Kothary Date: Sun, 27 Dec 2020 20:43:05 +0000 Subject: [PATCH 75/75] Bump version from 2.2.0 to 2.3.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 418a362..0b956d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "anki-sync-server" -version = "2.2.0" +version = "2.3.0" description = "Self-hosted Anki Sync Server." authors = ["Vikash Kothary "]