Merge remote-tracking branch 'upstream/master' into add-tags-on-update

This commit is contained in:
Erez Volk 2020-01-03 08:13:04 +02:00
commit e481cce816
602 changed files with 202264 additions and 148 deletions

3
.gitignore vendored
View file

@ -6,8 +6,7 @@
.build
.coverage
.DS_Store
build
dist
pyenv
.mypy_cache
__pycache__
i18n

52
CONTRIBUTORS Normal file
View file

@ -0,0 +1,52 @@
If you have made changes to Anki's AGPL code, you are welcome to distribute
the changed code under the AGPL license.
If you would like to contribute your code back to the official release, we ask
that you license your contributions under the BSD 3 clause license. Portions
of the code are also used in AnkiWeb and AnkiMobile, and accepting
contributions under an AGPL license would mean we could no longer use the code
we have written in those projects.
In your first pull request, please add your name below. By adding your name to
this file, you assert that any code you contribute to the Anki project is
licensed under the BSD 3 clause license. If any pull request you make contains
code that you don't own the copyright to, you agree to make that clear when
submitting the request.
For users who previously confirmed the license of their contributions on the
support site, it would be great if you could add your name below as well.
********************
- Sample Name
********************
The text of the 3 clause BSD license follows:
Contributions copyright the above contributors, 2010-Present.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

49
LICENSE
View file

@ -1 +1,48 @@
Primarily GNU AGPL3+ - please see the LICENSE file in each subfolder.
Anki is licensed under the GNU Affero General Public License, version 3 or
later, with portions contributed by Anki users licensed under the BSD-3
license (see CONTRIBUTORS).
The following included source code items use a license other than AGPL3:
In the pylib folder:
* The anki/template/ folder is based off pystache: MIT.
* The SuperMemo importer: GPL3.
* The Pauker importer: BSD-3.
* statsbg.py: CC BY-SA 3.0.
In the qt folder:
* Anki's translations are a mix of BSD and public domain.
* mpv.py: MIT.
* winpaths.py: MIT.
* MathJax: Apache 2.
* jQuery and jQuery-UI: MIT.
* browsersel.js: CC BY 2.5.
* plot.js: MIT.
The above list only covers the source code that is vendored in this
repository. Binary distributions also include copies of Qt translation
files (LGPL), and all of the Python, Rust and Javascript libraries
that this code references.
Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3 like the
rest of Anki's code.
The logo is also available under a limited alternative license for inclusion
in books, blogs, videos and so on. If the following conditions are met, you
may use the logo in your work without the need to license your work under an
AGPL3-compatible license:
* The logo must be used to refer to Anki, AnkiWeb, AnkiMobile or AnkiDroid,
and a link to https://apps.ankiweb.net must be provided. When your
content is focused specifically on AnkiDroid, a link to
https://play.google.com/store/apps/details?id=com.ichi2.anki&hl=en
may be provided instead of the first link.
* The work must make it clear that the text/video/etc you
are publishing is your own content and not something originating
from the Anki project.
* The logo must be used unmodified - no cropping, changing of colours
or adding or deleting content is allowed. You may resize the image
provided the horizontal and vertical dimensions are resized
equally.

View file

@ -100,10 +100,7 @@ requests instead.
License
-------
Please add yourself to the contributors file before sending a pull request
for other components:
https://github.com/ankitects/anki-contributors
Please add yourself to the contributors file in your first pull request.
Add-ons
========

View file

@ -36,14 +36,13 @@ of http://ankisrs.net/docs/manual.html#_contributing
Subcomponents
--------------
- pylib contains a Python module (anki) with most of the non-GUI code.
- qt contains the Qt GUI implementation (aqt).
- rspy contains a Python module (ankirspy) for accessing the Rust code.
- rslib contains the parts of the code implemented in Rust. This
is only a tiny subsection at the moment.
- proto contains the interface used to communicate between the Rust and
Python code.
- lib-rust contains the parts of the code implemented in Rust. This
is only a tiny subsection at the moment.
- lib-rspy contains a Python module (ankirspy) for accessing the Rust code.
- lib-python contains a Python module (anki) that covers all of the work
not related to the user interface.
- anki-qt contains the Qt GUI implementation (aqt).
Helper scripts
--------------

View file

@ -1,17 +0,0 @@
Anki is licensed under the GNU Affero General Public License, version 3 or
later, with portions contributed by Anki users licensed under the BSD-3
license: https://github.com/ankitects/anki-contributors
Please see LICENSE.logo for the copyright and license of Anki's logo.
The following included source code items use a license other than AGPL3:
* Anki's translations are a mix of BSD and public domain.
* The Qt translations that are copied in as part of the build process
are licensed under the LGPL.
* mpv.py: MIT.
* winpaths.py: MIT.
* MathJax: Apache 2.
* jQuery and jQuery-UI: MIT.
* browsersel.js: CC BY 2.5.
* plot.js: MIT.

View file

@ -1,20 +0,0 @@
Anki's logo is copyright Alex Fraser, and is licensed under the AGPL3 like the
rest of Anki's code.
The logo is also available under a limited alternative license for inclusion
in books, blogs, videos and so on. If the following conditions are met, you
may use the logo in your work without the need to license your work under an
AGPL3-compatible license:
* The logo must be used to refer to Anki, AnkiMobile or AnkiDroid,
and a link to https://apps.ankiweb.net must be provided. When your
content is focused specifically on AnkiDroid, a link to
https://play.google.com/store/apps/details?id=com.ichi2.anki&hl=en
may be provided instead of the first link.
* The work must make it clear that the text/video/etc you
are publishing is your own content and not something originating
from the Anki project.
* The logo must be used unmodified - no cropping, changing of colours
or adding or deleting content is allowed. You may resize the image
provided the horizontal and vertical dimensions are resized
equally.

View file

@ -1,23 +0,0 @@
#!/bin/bash
#
# Usage:
# tools/tests.sh # run all tests
# tools/tests.sh decks # test only test_decks.py
# coverage=1 tools/tests.sh # run with coverage test
set -e
BIN="$(cd "`dirname "$0"`"; pwd)"
export PYTHONPATH=${BIN}/..:${PYTHONPATH}
nose="python -m nose2 --plugin=nose2.plugins.mp -N 16"
dir=.
if [ x$1 = x ]; then
lim="tests"
else
lim="tests.test_$1"
fi
(cd $dir && $nose $lim $args)

17
build Executable file
View file

@ -0,0 +1,17 @@
#!/bin/bash
set -e
. scripts.inc
rm -rf dist
for dir in $DEVEL; do
echo $dir
(cd $dir && make build BUILDFLAGS="$BUILDFLAGS")
done
# add build hash to outputs
ver=$(cat meta/version)
hash=$(cat meta/buildhash)
(cd dist && rename "s/$ver/${ver}+$hash/" *.whl)

10
bundle
View file

@ -1,10 +0,0 @@
#!/bin/bash
set -e
. scripts.inc
for dir in $DEVEL; do
echo $dir
(cd $dir && make build BUILDFLAGS="$BUILDFLAGS")
done

2
clean
View file

@ -4,6 +4,8 @@ set -e
. scripts.inc
rm -rf dist
for dir in $DEVEL; do
echo $dir
(cd $dir && make clean)

View file

@ -1 +0,0 @@
Licensed under the GNU AGPL 3 or later.

View file

@ -1 +0,0 @@
Licensed under the GNU AGPL 3 or later.

1
meta/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
buildhash

1
meta/version Normal file
View file

@ -0,0 +1 @@
2.1.17

View file

@ -6,10 +6,10 @@ MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
RUNARGS :=
.SUFFIXES:
BLACKARGS := -t py36 anki tests --exclude=backend_pb2
BLACKARGS := -t py36 anki tests --exclude='backend_pb2|buildinfo'
ISORTARGS := anki tests
$(shell mkdir -p .build ../build)
$(shell mkdir -p .build ../dist)
PHONY: all
all: check
@ -28,7 +28,7 @@ PROTODEPS := $(wildcard ../proto/*.proto)
protoc --proto_path=../proto --python_out=anki --mypy_out=anki $(PROTODEPS)
@touch $@
BUILD_STEPS := .build/run-deps .build/dev-deps .build/py-proto
BUILD_STEPS := .build/run-deps .build/dev-deps .build/py-proto anki/buildinfo.py
# Checking
######################
@ -48,14 +48,14 @@ clean:
# Checking python
######################
CHECKDEPS := $(shell find anki tests -name '*.py' | grep -v buildhash.py)
CHECKDEPS := $(shell find anki tests -name '*.py')
.build/mypy: $(CHECKDEPS)
mypy anki
@touch $@
.build/test: $(CHECKDEPS)
python -m nose2 --plugin=nose2.plugins.mp -N 16
python -m pytest --durations=1
@touch $@
.build/lint: $(CHECKDEPS)
@ -78,10 +78,13 @@ CHECKDEPS := $(shell find anki tests -name '*.py' | grep -v buildhash.py)
.PHONY: build
build: $(BUILD_STEPS) $(CHECKDEPS)
rm -rf dist
echo "build='$$(git rev-parse --short HEAD)'" > anki/buildhash.py
python setup.py bdist_wheel
rsync -a dist/*.whl ../build/
rsync -a dist/*.whl ../dist/
# prepare code for running in place
.PHONY: develop
develop: $(BUILD_STEPS)
anki/buildinfo.py: ../meta/version ../meta/buildhash
echo "buildhash='$$(cat ../meta/buildhash)'" > $@
echo "version='$$(cat ../meta/version)'" >> $@

1
pylib/anki/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
buildinfo.py

View file

@ -3,6 +3,7 @@
import sys
from anki.buildinfo import version
from anki.storage import Collection
if sys.version_info[0] < 3 or sys.version_info[1] < 5:
@ -11,8 +12,5 @@ if sys.version_info[0] < 3 or sys.version_info[1] < 5:
if sys.getfilesystemencoding().lower() in ("ascii", "ansi_x3.4-1968"):
raise Exception("Anki requires a UTF-8 locale.")
# fmt: off
version="2.1.17" # build scripts grep this line, so preserve formatting
# fmt: on
__all__ = ["Collection"]

View file

@ -5,9 +5,12 @@ from typing import Dict, List
import ankirspy # pytype: disable=import-error
import anki.backend_pb2 as pb
import anki.buildinfo
from .types import AllTemplateReqs
assert ankirspy.buildhash() == anki.buildinfo.buildhash
SchedTimingToday = pb.SchedTimingTodayOut

View file

@ -101,8 +101,6 @@ def addClozeModel(col) -> NoteType:
txt = _("Text")
fm = mm.newField(txt)
mm.addField(m, fm)
fm = mm.newField(_("Extra"))
mm.addField(m, fm)
t = mm.newTemplate(_("Cloze"))
fmt = "{{cloze:%s}}" % txt
m[
@ -116,7 +114,7 @@ def addClozeModel(col) -> NoteType:
color: lightblue;
}"""
t["qfmt"] = fmt
t["afmt"] = fmt + "<br>\n{{%s}}" % _("Extra")
t["afmt"] = fmt
mm.addTemplate(m, t)
mm.add(m)
return m

View file

@ -494,10 +494,6 @@ class TimedLog:
def versionWithBuild() -> str:
from anki import version
from anki.buildinfo import version, buildhash
try:
from anki.buildhash import build # type: ignore # pylint: disable=import-error,no-name-in-module
except:
build = "dev"
return "%s (%s)" % (version, build)
return "%s (%s)" % (version, buildhash)

View file

@ -2,7 +2,7 @@ wheel
mypy
mypy_protobuf
black
nose2
pytest
# fixme: when isort 5.0 is released, switch to pypi
git+https://github.com/ankitects/isort#egg=isort
# fixme: when pylint supports isort 5.0, switch to pypi

View file

@ -3,6 +3,9 @@ import setuptools, sys
with open("README.md", "r") as fh:
long_description = fh.read()
with open("../meta/version") as fh:
version = fh.read().strip()
platform_reqs = []
if sys.platform == "win32":
platform_reqs.append("psutil")
@ -11,7 +14,7 @@ if sys.platform != "win32" and sys.platform != "darwin":
setuptools.setup(
name="anki",
version="0.1.0",
version=version,
author="Ankitects Pty Ltd",
description="Anki's library code",
long_description=long_description,

View file

@ -3,8 +3,6 @@
import os
import tempfile
from nose2.tools.decorators import with_setup
from anki import Collection as aopen
from anki.exporting import *
from anki.importing import Anki2Importer
@ -41,8 +39,8 @@ def setup1():
##########################################################################
@with_setup(setup1)
def test_export_anki():
setup1()
# create a new deck with its own conf to test conf copying
did = deck.decks.id("test")
dobj = deck.decks.get(did)
@ -83,8 +81,8 @@ def test_export_anki():
assert d2.cardCount() == 1
@with_setup(setup1)
def test_export_ankipkg():
setup1()
# add a test file to the media folder
with open(os.path.join(deck.media.dir(), "今日.mp3"), "w") as f:
f.write("test")
@ -99,8 +97,8 @@ def test_export_ankipkg():
e.exportInto(newname)
@with_setup(setup1)
def test_export_anki_due():
setup1()
deck = getEmptyCol()
f = deck.newNote()
f["Front"] = "foo"
@ -132,8 +130,8 @@ def test_export_anki_due():
assert c.due - deck2.sched.today == 1
# @with_setup(setup1)
# def test_export_textcard():
# setup1()
# e = TextCardExporter(deck)
# f = unicode(tempfile.mkstemp(prefix="ankitest")[1])
# os.unlink(f)
@ -142,8 +140,8 @@ def test_export_anki_due():
# e.exportInto(f)
@with_setup(setup1)
def test_export_textnote():
setup1()
e = TextNoteExporter(deck)
fd, f = tempfile.mkstemp(prefix="ankitest")
f = str(f)

View file

@ -1,5 +1,5 @@
# coding: utf-8
from nose2.tools.such import helper
import pytest
from anki.find import Finder
from tests.shared import getEmptyCol
@ -111,7 +111,7 @@ def test_findCards():
assert len(deck.findCards("nid:%d" % f.id)) == 2
assert len(deck.findCards("nid:%d,%d" % (f1id, f2id))) == 2
# templates
with helper.assertRaises(Exception):
with pytest.raises(Exception):
deck.findCards("card:foo")
assert len(deck.findCards("'card:card 1'")) == 4
assert len(deck.findCards("card:reverse")) == 1
@ -147,7 +147,7 @@ def test_findCards():
assert len(deck.findCards("-deck:foo")) == 5
assert len(deck.findCards("deck:def*")) == 5
assert len(deck.findCards("deck:*EFAULT")) == 5
with helper.assertRaises(Exception):
with pytest.raises(Exception):
deck.findCards("deck:*cefault")
# full search
f = deck.newNote()
@ -164,7 +164,7 @@ def test_findCards():
# assert len(deck.findCards("helloworld", full=True)) == 2
# assert len(deck.findCards("back:helloworld", full=True)) == 2
# searching for an invalid special tag should not error
with helper.assertRaises(Exception):
with pytest.raises(Exception):
len(deck.findCards("is:invalid"))
# should be able to limit to parent deck, no children
id = deck.db.scalar("select id from cards limit 1")
@ -238,9 +238,9 @@ def test_findCards():
assert len(deck.findCards("added:1")) == deck.cardCount() - 1
assert len(deck.findCards("added:2")) == deck.cardCount()
# flag
with helper.assertRaises(Exception):
with pytest.raises(Exception):
deck.findCards("flag:01")
with helper.assertRaises(Exception):
with pytest.raises(Exception):
deck.findCards("flag:12")

View file

@ -88,6 +88,10 @@ def test_changes():
def removed():
return d.media.db.execute("select fname from media where csum is null")
def advanceTime():
d.media.db.execute("update media set mtime=mtime-1")
d.media.db.execute("update meta set dirMod = dirMod - 1")
assert not list(added())
assert not list(removed())
# add a file
@ -95,27 +99,26 @@ def test_changes():
path = os.path.join(dir, "foo.jpg")
with open(path, "w") as f:
f.write("hello")
time.sleep(1)
path = d.media.addFile(path)
# should have been logged
d.media.findChanges()
assert list(added())
assert not list(removed())
# if we modify it, the cache won't notice
time.sleep(1)
advanceTime()
with open(path, "w") as f:
f.write("world")
assert len(list(added())) == 1
assert not list(removed())
# but if we add another file, it will
time.sleep(1)
advanceTime()
with open(path + "2", "w") as f:
f.write("yo")
d.media.findChanges()
assert len(list(added())) == 2
assert not list(removed())
# deletions should get noticed too
time.sleep(1)
advanceTime()
os.unlink(path + "2")
d.media.findChanges()
assert len(list(added())) == 1

Some files were not shown because too many files have changed in this diff Show more