Merge pull request #119 from ankicommunity/release/v2.3.0
This commit is contained in:
commit
a8c91b449c
43
Makefile
Normal file → Executable file
43
Makefile
Normal file → Executable file
@ -1,26 +1,43 @@
|
||||
#/bin/make
|
||||
#!/usr/bin/env make
|
||||
|
||||
ANKISYNCD_NAME ?= Anki Sync Server
|
||||
ANKISYNCD_VERSION ?= v2.3.0
|
||||
ANKISYNCD_DESCRIPTION ?= Self-hosted Anki Sync Server.
|
||||
|
||||
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}
|
||||
-include config/secrets/.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<command>\033[0m\n\nCommands:"} /^.PHONY: ?[a-zA-Z_-]/ { printf " \033[36m%-10s\033[0m %s\n", $$2, $$3 }' $(MAKEFILE_LIST)
|
||||
@${AWK} 'BEGIN {FS = " ?#?: "; print "${ANKISYNCD_NAME} ${ANKISYNCD_VERSION}\n${ANKISYNCD_DESCRIPTION}\n\nUsage: make \033[36m<command>\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
|
||||
docs:
|
||||
@${MKDOCS} ${MKDOCS_CMD} -f docs/mkdocs.yml ${MKDOCS_OPTS}
|
||||
|
||||
.PHONY: notebooks #: Run jupyter notebooks.
|
||||
notebooks:
|
||||
@${JUPYTER} ${JUPYTER_OPTION}
|
||||
.PHONY: tests #: Run unit tests.
|
||||
tests:
|
||||
@${UNITTEST} discover -s tests
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
@${PYTHON} src/ankisyncd/__main__.py
|
||||
|
||||
# Run scripts using make
|
||||
%:
|
||||
@test -f scripts/${*}.sh
|
||||
@${SHELL} scripts/${*}.sh
|
||||
@if [[ -f "scripts/${*}.sh" ]]; then \
|
||||
${BASH} "scripts/${*}.sh"; fi
|
||||
|
||||
.PHONY: init #: Download Python dependencies.
|
||||
init:
|
||||
@${POETRY} install
|
||||
|
||||
.PHONY: release #: Create new Git release and tags.
|
||||
release: release-branch release-tags
|
||||
|
||||
.PHONY: open
|
||||
open:
|
||||
@${OPEN} ${ANKISYNCD_URL}
|
||||
77
README.md
77
README.md
@ -26,9 +26,11 @@ It supports Python 3 and Anki 2.1.
|
||||
- [Installing](#installing)
|
||||
- [Installing (Docker)](#installing-docker)
|
||||
- [Setting up Anki](#setting-up-anki)
|
||||
- [Anki 2.1](#anki-21)
|
||||
- [Anki 2.0](#anki-20)
|
||||
- [AnkiDroid](#ankidroid)
|
||||
- [Anki 2.1](#anki-21)
|
||||
- [Anki 2.0](#anki-20)
|
||||
- [AnkiDroid](#ankidroid)
|
||||
- [Development](#development)
|
||||
- [Testing](#testing)
|
||||
- [ENVVAR configuration overrides](#envvar-configuration-overrides)
|
||||
- [Support for other database backends](#support-for-other-database-backends)
|
||||
</details>
|
||||
@ -59,34 +61,46 @@ Installing
|
||||
requests to ankisyncd.
|
||||
|
||||
An example configuration with ankisyncd running on the same machine as Nginx
|
||||
and listening on port `27702` may look like:
|
||||
and listening on port `27702` may look like ([entire config template click me](https://github.com/ankicommunity/anki-sync-server/blob/develop/docs/nginx.conf)):
|
||||
|
||||
```
|
||||
```nginx
|
||||
server {
|
||||
listen 27701;
|
||||
server_name default;
|
||||
|
||||
location / {
|
||||
proxy_http_version 1.0;
|
||||
proxy_pass http://localhost:27702/;
|
||||
}
|
||||
listen 27701;
|
||||
server_name default;
|
||||
location / {
|
||||
proxy_http_version 1.0;
|
||||
proxy_pass http://127.0.0.1:27702/;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
5. Run ankisyncd:
|
||||
|
||||
$ python -m ankisyncd
|
||||
```
|
||||
$ python -m ankisyncd
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Installing (Docker)
|
||||
-------------------
|
||||
|
||||
Follow [these instructions](https://github.com/kuklinistvan/docker-anki-sync-server#usage).
|
||||
Follow [these instructions](https://github.com/ankicommunity/anki-devops-services#about-this-docker-image).
|
||||
|
||||
Setting up Anki
|
||||
---------------
|
||||
|
||||
### Install addon from ankiweb (support 2.1)
|
||||
|
||||
1.on add-on window,click `Get Add-ons` and fill in the textbox with the code `358444159`
|
||||
|
||||
2.there,you get add-on `custom sync server redirector`,choose it.Then click `config` below right
|
||||
|
||||
3.apply your server ip address
|
||||
|
||||
if this step is taken,the following instructions regarding addon setting 2.1( including 2.1.28 and above) can be skipped.
|
||||
|
||||
### Anki 2.1.28 and above
|
||||
|
||||
Create a new directory in [the add-ons folder][addons21] (name it something
|
||||
@ -94,7 +108,7 @@ 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/"
|
||||
@ -106,7 +120,7 @@ like ankisyncd), create a file named `__init__.py` containing the code below
|
||||
and put it in the `ankisyncd` directory.
|
||||
|
||||
import anki.sync, anki.hooks, aqt
|
||||
|
||||
|
||||
addr = "http://127.0.0.1:27701/" # put your server address here
|
||||
anki.sync.SYNC_BASE = "%s" + addr
|
||||
def resetHostNum():
|
||||
@ -119,7 +133,7 @@ Create a file (name it something like ankisyncd.py) containing the code below
|
||||
and put it in `~/Anki/addons`.
|
||||
|
||||
import anki.sync
|
||||
|
||||
|
||||
addr = "http://127.0.0.1:27701/" # put your server address here
|
||||
anki.sync.SYNC_BASE = addr
|
||||
anki.sync.SYNC_MEDIA_BASE = addr + "msync/"
|
||||
@ -142,6 +156,35 @@ 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`.
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
### Testing
|
||||
|
||||
0. Prerequites
|
||||
|
||||
This project uses [GNU Make](https://www.gnu.org/software/make/) to simplify the development commands. It also uses [Poetry](https://python-poetry.org/) to manage the Python dependencies. Ensure they are installed.
|
||||
|
||||
1. Create a config for your local environment.
|
||||
|
||||
```bash
|
||||
$ cp config/.env.example config/.env.local
|
||||
```
|
||||
|
||||
See [ENVVAR configuration overrides](#envvar-configuration-overrides) for more information.
|
||||
|
||||
2. Download Python dependencies.
|
||||
|
||||
```bash
|
||||
$ make init
|
||||
```
|
||||
|
||||
3. Run unit tests.
|
||||
|
||||
```bash
|
||||
$ make tests
|
||||
```
|
||||
|
||||
ENVVAR configuration overrides
|
||||
------------------------------
|
||||
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
# .env.example (anki-sync-server)
|
||||
|
||||
## Make
|
||||
MKDOCS=mkdocs
|
||||
JUPYTER=jupyter
|
||||
# file: .env.example (ankisyncd)
|
||||
# cp config/.env.example config/.env.local
|
||||
|
||||
## Ankisyncd
|
||||
ANKISYNCD_HOST=0.0.0.0
|
||||
@ -12,17 +9,31 @@ 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
|
||||
### ANKISYNCD_FULL_SYNC_MANAGER
|
||||
### ANKISYNCD_SESSION_MANAGER
|
||||
### ANKISYNCD_USER_MANAGER
|
||||
### ANKISYNCD_COLLECTION_WRAPPER
|
||||
ANKISYNCD_URL=http://${ANKISYNCD_HOST}:${ANKISYNCD_PORT}
|
||||
|
||||
## Mkdocs
|
||||
MKDOCS_OPTION=serve
|
||||
MKDOCS_HOST=localhost
|
||||
MKDOCS_PORT=5000
|
||||
MKDOCS_OPTS=-a ${MKDOCS_HOST}:${MKDOCS_PORT}
|
||||
MKDOCS_CMD=serve
|
||||
|
||||
## Jupyter
|
||||
JUPYTER_OPTION=lab
|
||||
### JUPYTER_CMD
|
||||
### JUPYTER_NOTEBOOK_DIR
|
||||
|
||||
## Path
|
||||
PATH:=.venv/bin/path:${PATH}
|
||||
## Make
|
||||
AWK=awk
|
||||
BASH=bash
|
||||
POETRY=poetry
|
||||
PYTHON=python
|
||||
MKDOCS=mkdocs
|
||||
JUPYTER=jupyter
|
||||
UNITTEST=python -m unittest
|
||||
|
||||
## Shell
|
||||
SHELL=${BASH}
|
||||
PATH:=.venv/bin:${PATH}
|
||||
@ -6,4 +6,6 @@ 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
|
||||
site_dir: build
|
||||
plugins:
|
||||
- mkdocs-jupyter
|
||||
29
docs/src/nginx/nginx.example.conf
Normal file
29
docs/src/nginx/nginx.example.conf
Normal file
@ -0,0 +1,29 @@
|
||||
# file: nginx.example.conf
|
||||
# description: Example nginx.conf to set up a reverse proxy.
|
||||
|
||||
worker_processes 1;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
client_max_body_size 2048m;
|
||||
|
||||
server {
|
||||
listen 27701;
|
||||
# server_name should be modified (LAN eg: 192.168.1.43 )
|
||||
server_name default;
|
||||
|
||||
|
||||
location / {
|
||||
proxy_http_version 1.0;
|
||||
proxy_pass http://127.0.0.1:27702/;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,13 +149,6 @@
|
||||
"source": [
|
||||
"col.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
1876
poetry.lock
generated
1876
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -3,15 +3,17 @@ name = "anki-sync-server"
|
||||
version = "2.3.0"
|
||||
description = "Self-hosted Anki Sync Server."
|
||||
authors = ["Vikash Kothary <kothary.vikash@gmail.com>"]
|
||||
packages = [
|
||||
{ include = "ankisyncd", from = "src" }
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
anki = "^2.1.36"
|
||||
anki = "2.1.43"
|
||||
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"
|
||||
@ -21,6 +23,8 @@ webob = "^1.8.6"
|
||||
mkdocs = "^1.1.2"
|
||||
jupyter = "^1.0.0"
|
||||
jupyterlab = "^2.2.2"
|
||||
webtest = "^2.0.35"
|
||||
mkdocs-jupyter = "^0.19.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
|
||||
7
scripts/jupyter.sh
Normal file
7
scripts/jupyter.sh
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: jupyter.sh
|
||||
# description: Run Jupyter Notebooks.
|
||||
|
||||
[[ ! -z "${JUPYTER_CMD}" ]] || JUPYTER_CMD=lab
|
||||
[[ ! -z "${JUPYTER_NOTEBOOK_DIR}" ]] || JUPYTER_NOTEBOOK_DIR=docs/src/notebooks
|
||||
${JUPYTER} ${JUPYTER_CMD} --notebook-dir=${JUPYTER_NOTEBOOK_DIR}
|
||||
@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 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
|
||||
|
||||
echo "-e src/." >> src/requirements-dev.txt
|
||||
|
||||
|
||||
15
scripts/poetry-export.sh
Normal file
15
scripts/poetry-export.sh
Normal file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: poetry-export.sh
|
||||
# description: Lock dependencies and export them as backward-compatible requirements.txt files.
|
||||
|
||||
echo '[INFO] Updating poetry.lock file.'
|
||||
poetry lock
|
||||
|
||||
echo '[INFO] Generating requirements.txt.'
|
||||
echo -e "# THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!\n\n" > src/requirements.txt
|
||||
poetry export --without-hashes -f requirements.txt >> src/requirements.txt
|
||||
|
||||
echo '[INFO] Generating requirements-dev.txt.'
|
||||
echo -e "# THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!\n\n" > src/requirements-dev.txt
|
||||
poetry export --dev --without-hashes -f requirements.txt >> src/requirements-dev.txt
|
||||
echo "-e src/." >> src/requirements-dev.txt
|
||||
@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
# file: print-env.sh
|
||||
# description: Print env variable.
|
||||
|
||||
echo "${ENV}"
|
||||
5
scripts/printenv.sh
Normal file
5
scripts/printenv.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: printenv.sh
|
||||
# description: Print all environment variables. Used for debugging.
|
||||
|
||||
printenv | sort
|
||||
39
scripts/release-branch.sh
Normal file
39
scripts/release-branch.sh
Normal file
@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: release-branch.sh
|
||||
# description: Prepare a release branch from develop to master.
|
||||
|
||||
## Build release context as environment variables.
|
||||
GIT_BRANCH=$(git symbolic-ref --short HEAD)
|
||||
|
||||
## TODO: get package version from pyproject.toml
|
||||
CURRENT_VERSION=v2.2.0
|
||||
## TODO: get new package version e.g. minor, major, bugfix
|
||||
NEW_VERSION=v2.3.0
|
||||
|
||||
## TODO: ensure you're on the develop branch else fail
|
||||
if [[ "${GIT_BRANCH}" != "develop" ]]; then
|
||||
echo 'Please switch to to the develop branch to create a release branch.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
## Create release branch
|
||||
git checkout -b "release/${NEW_VERSION}" develop
|
||||
|
||||
## TODO: bump package version in pyproject.toml
|
||||
## TODO: commit changes to pyproject.toml
|
||||
## TODO: generate new CHANGELOG entry from commits
|
||||
## TODO: commit changes to CHANGELOG
|
||||
|
||||
## Return to develop
|
||||
git checkout develop
|
||||
|
||||
if [[ -z "${CI}" ]]; then
|
||||
echo "Please confirm the release branch is correct."
|
||||
read -p "Press enter to continue"
|
||||
echo
|
||||
fi
|
||||
|
||||
## Push branch and tags
|
||||
git push origin "release/${NEW_VERSION}"
|
||||
|
||||
## TODO: create PR for review
|
||||
32
scripts/release-tags.sh
Normal file
32
scripts/release-tags.sh
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: release-tags.sh
|
||||
# description: Automatic consistent release tags following the SemVer convension.
|
||||
|
||||
# set -x
|
||||
# trap read debug
|
||||
|
||||
## Build release context as environment variables.
|
||||
GIT_BRANCH=$(git symbolic-ref --short HEAD)
|
||||
|
||||
## TODO: ensure this is the main branch
|
||||
if [[ "${GIT_BRANCH}" != "main" ]]; then
|
||||
echo 'Please switch to the main branch to create release tags.'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
## TODO: get package version from pyproject.toml
|
||||
CURRENT_VERSION=2.3.0
|
||||
|
||||
## Create GitHub Release
|
||||
git tag -a ${CURRENT_VERSION} -m "v${CURRENT_VERSION}"
|
||||
|
||||
if [[ -z "${CI}" ]]; then
|
||||
echo "Please confirm the release tag is correct."
|
||||
read -p "Press enter to continue"
|
||||
echo
|
||||
fi
|
||||
|
||||
git push --tags
|
||||
|
||||
## TODO: publish to PyPI.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import getpass
|
||||
|
||||
@ -10,6 +10,7 @@ import random
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
from typing import List,Tuple
|
||||
|
||||
from anki.db import DB, DBError
|
||||
from anki.utils import ids2str, intTime, platDesc, checksum, devMode
|
||||
@ -72,9 +73,40 @@ class Syncer(object):
|
||||
if 'crt' in rchg:
|
||||
self.col.crt = rchg['crt']
|
||||
self.prepareToChunk()
|
||||
|
||||
# this fn was cloned from anki module(version 2.1.36)
|
||||
def basicCheck(self) -> bool:
|
||||
"Basic integrity check for syncing. True if ok."
|
||||
# cards without notes
|
||||
if self.col.db.scalar(
|
||||
"""
|
||||
select 1 from cards where nid not in (select id from notes) limit 1"""
|
||||
):
|
||||
return False
|
||||
# notes without cards or models
|
||||
if self.col.db.scalar(
|
||||
"""
|
||||
select 1 from notes where id not in (select distinct nid from cards)
|
||||
or mid not in %s limit 1"""
|
||||
% ids2str(self.col.models.ids())
|
||||
):
|
||||
return False
|
||||
# invalid ords
|
||||
for m in self.col.models.all():
|
||||
# ignore clozes
|
||||
if m["type"] != MODEL_STD:
|
||||
continue
|
||||
if self.col.db.scalar(
|
||||
"""
|
||||
select 1 from cards where ord not in %s and nid in (
|
||||
select id from notes where mid = ?) limit 1"""
|
||||
% ids2str([t["ord"] for t in m["tmpls"]]),
|
||||
m["id"],
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
def sanityCheck(self, full):
|
||||
if not self.col.basicCheck():
|
||||
if not self.basicCheck():
|
||||
return "failed basic check"
|
||||
for t in "cards", "notes", "revlog", "graves":
|
||||
if self.col.db.scalar(
|
||||
@ -83,7 +115,7 @@ class Syncer(object):
|
||||
for g in self.col.decks.all():
|
||||
if g['usn'] == -1:
|
||||
return "deck had usn = -1"
|
||||
for t, usn in self.col.tags.allItems():
|
||||
for t, usn in self.allItems():
|
||||
if usn == -1:
|
||||
return "tag had usn = -1"
|
||||
found = False
|
||||
@ -276,10 +308,12 @@ from notes where %s""" % lim, self.maxUsn)
|
||||
|
||||
# Tags
|
||||
##########################################################################
|
||||
|
||||
def allItems(self) -> List[Tuple[str, int]]:
|
||||
tags=self.col.db.execute("select tag, usn from tags")
|
||||
return [(tag, int(usn)) for tag,usn in tags]
|
||||
def getTags(self):
|
||||
tags = []
|
||||
for t, usn in self.col.tags.allItems():
|
||||
for t, usn in self.allItems():
|
||||
if usn == -1:
|
||||
self.col.tags.tags[t] = self.maxUsn
|
||||
tags.append(t)
|
||||
@ -331,7 +365,9 @@ from notes where %s""" % lim, self.maxUsn)
|
||||
return self.col.conf
|
||||
|
||||
def mergeConf(self, conf):
|
||||
self.col.backend.set_all_config(json.dumps(conf).encode())
|
||||
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
|
||||
##########################################################################
|
||||
@ -568,7 +604,7 @@ class FullSyncer(HttpSyncer):
|
||||
# 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():
|
||||
if not self.basicCheck():
|
||||
return False
|
||||
# apply some adjustments, then upload
|
||||
self.col.beforeUpload()
|
||||
|
||||
@ -121,6 +121,10 @@ class SyncCollectionHandler(Syncer):
|
||||
self.minUsn = minUsn
|
||||
self.lnewer = not lnewer
|
||||
lgraves = self.removed()
|
||||
# convert grave:None to {'cards': [], 'notes': [], 'decks': []}
|
||||
# because req.POST['data'] returned value of grave is None
|
||||
if graves==None:
|
||||
graves={'cards': [], 'notes': [], 'decks': []}
|
||||
self.remove(graves)
|
||||
return lgraves
|
||||
|
||||
@ -178,7 +182,7 @@ class SyncCollectionHandler(Syncer):
|
||||
]
|
||||
|
||||
def getTags(self):
|
||||
return [t for t, usn in self.col.tags.allItems()
|
||||
return [t for t, usn in self.allItems()
|
||||
if usn >= self.minUsn]
|
||||
|
||||
class SyncMediaHandler:
|
||||
|
||||
@ -1,91 +1,113 @@
|
||||
THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!
|
||||
# THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!
|
||||
|
||||
|
||||
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"
|
||||
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.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.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"
|
||||
anki==2.1.43; python_version >= "3.8"
|
||||
appnope==0.1.2; platform_system == "Darwin" and python_version >= "3.8" and sys_platform == "darwin"
|
||||
argon2-cffi-bindings==21.2.0; python_version >= "3.6"
|
||||
argon2-cffi==21.3.0; python_version >= "3.6"
|
||||
asttokens==2.0.5; python_version >= "3.8"
|
||||
attrs==21.4.0; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
backcall==0.2.0; python_version >= "3.8"
|
||||
beautifulsoup4==4.10.0; python_full_version > "3.0.0"
|
||||
black==21.12b0; python_full_version >= "3.6.2" and python_version >= "3.8"
|
||||
bleach==4.1.0; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
certifi==2021.10.8; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
cffi==1.15.0; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.6.1"
|
||||
charset-normalizer==2.0.10; python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
click==8.0.3; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.8"
|
||||
colorama==0.4.4; python_version >= "3.8" and python_full_version < "3.0.0" and platform_system == "Windows" and sys_platform == "win32" or platform_system == "Windows" and python_version >= "3.8" and python_full_version >= "3.5.0" and sys_platform == "win32"
|
||||
debugpy==1.5.1; 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")
|
||||
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 >= "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.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.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.6"
|
||||
jupyter-console==6.2.0; python_version >= "3.6"
|
||||
jupyter-core==4.7.0; python_version >= "3.6"
|
||||
defusedxml==0.7.1; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
distro==1.6.0
|
||||
entrypoints==0.3; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
executing==0.8.2; python_version >= "3.8"
|
||||
ghp-import==2.0.2; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6"
|
||||
idna==3.3; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
importlib-metadata==4.10.0; python_version < "3.10" and python_version >= "3.7" and python_full_version >= "3.7.1"
|
||||
importlib-resources==5.4.0; python_version < "3.9" and python_version >= "3.7" and python_full_version >= "3.7.1"
|
||||
ipykernel==6.7.0; python_version >= "3.7"
|
||||
ipython-genutils==0.2.0; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
ipython==8.0.0; python_version >= "3.8"
|
||||
ipywidgets==7.6.5
|
||||
jedi==0.18.1; python_version >= "3.8"
|
||||
jinja2==3.0.3; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
json5==0.9.6; python_version >= "3.5"
|
||||
jsonschema==4.4.0; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
jupyter-client==7.1.1; python_full_version >= "3.7.1" and python_version >= "3.7" and python_version < "4"
|
||||
jupyter-console==6.4.0; python_version >= "3.6"
|
||||
jupyter-core==4.9.1; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
jupyter==1.0.0
|
||||
jupyterlab-pygments==0.1.2; python_version >= "3.6"
|
||||
jupyterlab-pygments==0.1.2; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
jupyterlab-server==1.2.0; 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.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 >= "3.6"
|
||||
mkdocs==1.1.2; 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.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; sys_platform != "win32" and python_version >= "3.7"
|
||||
pickleshare==0.7.5; python_version >= "3.7"
|
||||
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.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==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==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.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
|
||||
jupyterlab-widgets==1.0.2; python_version >= "3.6"
|
||||
jupyterlab==2.3.2; python_version >= "3.5"
|
||||
jupytext==1.13.6; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.7.1"
|
||||
markdown-it-py==1.1.0; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.7.1"
|
||||
markdown==3.3.6; python_version >= "3.6"
|
||||
markupsafe==2.0.1; python_version >= "3.6"
|
||||
matplotlib-inline==0.1.3; python_version >= "3.8"
|
||||
mdit-py-plugins==0.3.0; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.7.1"
|
||||
mergedeep==1.3.4; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6"
|
||||
mistune==0.8.4; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
mkdocs-jupyter==0.19.0; python_full_version >= "3.7.1" and python_version < "4"
|
||||
mkdocs-material-extensions==1.0.3; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6"
|
||||
mkdocs-material==8.1.7; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6"
|
||||
mkdocs==1.2.3; python_version >= "3.6"
|
||||
mypy-extensions==0.4.3; python_full_version >= "3.6.2" and python_version >= "3.8"
|
||||
nbclient==0.5.10; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
nbconvert==6.4.0; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
nbformat==5.1.3; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
nest-asyncio==1.5.4; python_full_version >= "3.7.1" and python_version >= "3.7" and python_version < "4"
|
||||
notebook==6.4.7; python_version >= "3.6"
|
||||
orjson==3.6.5; platform_machine == "x86_64" and python_version >= "3.8"
|
||||
packaging==21.3; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
pandocfilters==1.5.0; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
parso==0.8.3; python_version >= "3.8"
|
||||
pathspec==0.9.0; python_full_version >= "3.6.2" and python_version >= "3.8"
|
||||
pexpect==4.8.0; sys_platform != "win32" and python_version >= "3.8"
|
||||
pickleshare==0.7.5; python_version >= "3.8"
|
||||
platformdirs==2.4.1; python_full_version >= "3.6.2" and python_version >= "3.8"
|
||||
prometheus-client==0.12.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||
prompt-toolkit==3.0.24; python_full_version >= "3.6.2" and python_version >= "3.8"
|
||||
protobuf==3.19.3; python_version >= "3.8"
|
||||
psutil==5.9.0; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
|
||||
ptyprocess==0.7.0; os_name != "nt" and python_version >= "3.8" and sys_platform != "win32"
|
||||
pure-eval==0.2.1; python_version >= "3.8"
|
||||
py==1.11.0; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.6.1"
|
||||
pycparser==2.21; implementation_name == "pypy" and python_version >= "3.7" and python_full_version >= "3.6.1"
|
||||
pygments==2.11.2; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.8"
|
||||
pymdown-extensions==9.1; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6"
|
||||
pyparsing==3.0.6; python_version >= "3.6"
|
||||
pyrsistent==0.18.1; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
pysocks==1.7.1; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
python-dateutil==2.8.2; python_full_version >= "3.6.1" and python_version >= "3.7"
|
||||
pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
pywinpty==1.1.6; os_name == "nt" and python_version >= "3.6"
|
||||
pyyaml-env-tag==0.1; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6"
|
||||
pyyaml==6.0; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.7.1"
|
||||
pyzmq==22.3.0; python_full_version >= "3.6.1" and python_version >= "3.7"
|
||||
qtconsole==5.2.2; python_version >= "3.6"
|
||||
qtpy==2.0.0; python_version >= "3.6"
|
||||
requests==2.27.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0")
|
||||
send2trash==1.8.0
|
||||
six==1.16.0; python_full_version >= "3.7.1" and python_version >= "3.7" and python_version < "4" and (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.3.1; python_full_version > "3.0.0" and python_version >= "3.8"
|
||||
stack-data==0.1.4; python_version >= "3.8"
|
||||
terminado==0.12.1; python_version >= "3.6"
|
||||
testpath==0.5.0; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
toml==0.10.2; python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.7.1"
|
||||
tomli==1.2.3; python_full_version >= "3.6.2" and python_version >= "3.8"
|
||||
tornado==6.1; python_full_version >= "3.6.1" and python_version >= "3.7"
|
||||
traitlets==5.1.1; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.8"
|
||||
typing-extensions==4.0.1
|
||||
urllib3==1.26.8; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.8"
|
||||
waitress==2.0.0; python_full_version >= "3.6.0"
|
||||
watchdog==2.1.6; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.6"
|
||||
wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.8"
|
||||
webencodings==0.5.1; python_full_version >= "3.7.1" and python_version < "4" and python_version >= "3.7"
|
||||
webob==1.8.7; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
|
||||
webtest==2.0.35
|
||||
widgetsnbextension==3.5.2
|
||||
zipp==3.7.0; python_version < "3.9" and python_version >= "3.7" and python_full_version >= "3.7.1"
|
||||
-e src/.
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!
|
||||
# THE FILE WAS GENERATED BY POETRY, DO NOT EDIT!
|
||||
|
||||
|
||||
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"
|
||||
anki==2.1.43; python_version >= "3.8"
|
||||
beautifulsoup4==4.10.0; python_full_version > "3.0.0"
|
||||
certifi==2021.10.8; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
charset-normalizer==2.0.10; python_full_version >= "3.6.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.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
|
||||
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.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")
|
||||
distro==1.6.0
|
||||
idna==3.3; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
importlib-metadata==4.10.0; python_version < "3.10" and python_version >= "3.7"
|
||||
markdown==3.3.6; python_version >= "3.6"
|
||||
orjson==3.6.5; platform_machine == "x86_64" and python_version >= "3.8"
|
||||
protobuf==3.19.3; python_version >= "3.8"
|
||||
psutil==5.9.0; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
|
||||
pysocks==1.7.1; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.8"
|
||||
requests==2.27.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.6.0")
|
||||
send2trash==1.8.0
|
||||
soupsieve==2.3.1; python_full_version > "3.0.0" and python_version >= "3.8"
|
||||
urllib3==1.26.8; python_version >= "3.8" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.8"
|
||||
webob==1.8.7; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
|
||||
zipp==3.7.0; python_version < "3.10" and python_version >= "3.7"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user