dsnopek/anki-sync-server

Python 3 Support

pganssle opened this issue · 11 comments

This app does not seem to work with Python 3. I think it would be a good idea to have at least basic polyglot Python 2/3 support.

I also think running using Python 3 or at least emulating python 3 would make things a bit less thorny when dealing with, for example, unicode filenames.

I guess actually running this under Python 3 will be a problem since it pulls in anki as a dependency, and Anki still does not support Python 3. Probably not a bad idea to still use polyglot code in preparation for the eventuality that Anki is ported to Python 3.

I gave it a quick look and it seems like most places just need print x -> print(x) and except Exception, e -> except Exception as e.

So long as this will still work with the default Python 2 installed on the most common Linux distributions, this sounds fine. PRs welcome! :-)

Yeah, I don't see much in the way of Python 3 exclusive stuff out there, so there's a good number of idioms and libraries (e.g. six) that help you bridge the gap for writing polyglot 2/3 code.

It kinda surprises me any time I see print x, honestly, because for most uses of print you can just add parens and it makes it 2/3 compatible even without importing print_function from __future__ (though there are a few places in this application that would require that, since you print to a stream).

I can work up a quick PR, but it's tough to know how thorough a job I'd be doing without being able to actually run it with Python 3.

cdpm commented

Anki Desktop is being ported to Python 3: ankitects/anki@15b349e.

I have started porting anki-sync-server to Python 3. It currently works, but hasn't been tested very thoroughly. If there's anyone interested in improving the code, it's at https://github.com/tsudoko/anki-sync-server/tree/anki_2_1. I'll make a PR later if the code is considered good enough.

Hello,

Thanks for your work, @tsudoko. I've been testing out your fork, but I'm having trouble starting the sever. Here's what I did:

I installed both the latest Anki Alpha version and tsudoko's Python 3 fork:

git clone https://github.com/dae/anki
cd anki
sudo make install

cd ~
git clone https://github.com/tsudoko/anki-sync-server
sudo pip install git+file:///home/test/anki-sync-server

Then I created the production.ini file per the instructions. But when I tried to run the server under debug:

cd /usr/local/bin
sudo python3 ./ankiserverctl.py adduser test
sudo python3 ./ankiserverctl.py debug

I get the following error:

/usr/local/bin$ sudo python3 ./ankiserverctl.py debug
Traceback (most recent call last):
  File "/usr/local/bin/paster", line 11, in <module>
    sys.exit(run())
  File "/usr/local/lib/python2.7/dist-packages/paste/script/command.py", line 102, in run
    invoke(command, command_name, options, args[1:])
  File "/usr/local/lib/python2.7/dist-packages/paste/script/command.py", line 141, in invoke
    exit_code = runner.run(args)
  File "/usr/local/lib/python2.7/dist-packages/paste/script/command.py", line 236, in run
    result = self.command()
  File "/usr/local/lib/python2.7/dist-packages/paste/script/serve.py", line 284, in command
    relative_to=base, global_conf=vars)
  File "/usr/local/lib/python2.7/dist-packages/paste/script/serve.py", line 329, in loadapp
    **kw)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 247, in loadapp
    return loadobj(APP, uri, name=name, **kw)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 272, in loadobj
    return context.create()
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 710, in create
    return self.object_type.invoke(self)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 217, in invoke
    next_app = context.next_context.create()
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 710, in create
    return self.object_type.invoke(self)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 144, in invoke
    **context.local_conf)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/util.py", line 55, in fix_call
    val = callable(*args, **kw)
  File "/usr/local/lib/python2.7/dist-packages/paste/urlmap.py", line 31, in urlmap_factory
    app = loader.get_app(app_name, global_conf=global_conf)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 350, in get_app
    name=name, global_conf=global_conf).create()
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 362, in app_context
    APP, name=name, global_conf=global_conf)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 454, in get_context
    section)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 476, in _context_from_use
    object_type, name=use, global_conf=global_conf)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 406, in get_context
    global_conf=global_conf)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 296, in loadcontext
    global_conf=global_conf)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 328, in _loadegg
    return loader.get_context(object_type, name, global_conf)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 620, in get_context
    object_type, name=name)
  File "/usr/local/lib/python2.7/dist-packages/paste/deploy/loadwsgi.py", line 646, in find_egg_entry_point
    possible.append((entry.load(), protocol, entry.name))
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2229, in load
    return self.resolve()
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 2235, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/usr/local/lib/python2.7/dist-packages/AnkiServer/apps/sync_app.py", line 36, in <module>
    import anki
  File "/usr/share/anki/anki/__init__.py", line 8, in <module>
    raise Exception("Anki should be run with Python 3")
Exception: Anki should be run with Python 3

Thanks!

Are you sure you're using the right version of pip? I managed to reproduce the issue in a clean Debian stretch chroot by running pip for Python 2 instead of the Python 3 one. On most Linux distributions the Python 3 pip has a binary called pip3, you might need to install it before you can use it.

Thanks for the quick reply! It looks like I should have used pip3. We're making progress, but I still have issues getting things running:

sudo apt-get install python3-pip
sudo pip3 install git+file:///home/test/anki-sync-server

Then I set up production.ini using the contents of example.ini:

cd /usr/local/bin
sudo touch production.ini
sudo gedit production.ini

Then I configured the server:

sudo ./ankiserverctl.py adduser test
sudo ./ankiserverctl.py debug

I ended up getting an error message saying I needed pyaudio, which I then installed

sudo apt-get install python3-pyaudio
sudo ./ankiserverctl.py debug

Which led to...

Starting server in PID 7029.
serving on http://127.0.0.1:27701

Woohoo! let's start the server:

sudo ./ankiserverctl.py start

I then downloaded the Anki 2.1 Alpha 8 here
https://apps.ankiweb.net/downloads/alpha/alpha8/

Started Anki, then closed it.

cd ~/Downloads/anki-2.1.0a8-amd64/bin
./anki

Then I added mysyncserver.py to the ~/Documents/Anki/addons21/ folder with the following contents:

import anki.sync
anki.sync.SYNC_BASE = 'http://127.0.0.1:27701/'
anki.sync.SYNC_MEDIA_BASE = 'http://127.0.0.1:27701/msync/'

Started up anki

cd ~/Downloads/anki-2.1.0a8-amd64/bin
./anki

Tried to sync with server and got the following message

Syncing failed:
AnkiWeb encountered an error. Please try again in a few minutes, and if the problem persists, please file a bug report.

Then I tried a couple of things

sudo ufw allow 27701

and changing the contents of mysyncserver.py to:

import anki.sync
anki.sync.SYNC_BASE = 'http://10.0.2.15:27701/'
anki.sync.SYNC_MEDIA_BASE = 'http://10.0.2.15:27701/msync/'

When I tried to sync again, I got the following error

Syncing failed:
Traceback (most recent call last):
  File "site-packages/requests/packages/urllib3/connection.py", line 138, in _new_conn
  File "site-packages/requests/packages/urllib3/util/connection.py", line 98, in create_connection
  File "site-packages/requests/packages/urllib3/util/connection.py", line 88, in create_connection
ConnectionRefusedError: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "site-packages/requests/packages/urllib3/connectionpool.py", line 594, in urlopen
  File "site-packages/requests/packages/urllib3/connectionpool.py", line 361, in _make_request
  File "http/client.py", line 1083, in request
  File "http/client.py", line 1128, in _send_request
  File "http/client.py", line 1079, in endheaders
  File "http/client.py", line 911, in _send_output
  File "http/client.py", line 854, in send
  File "site-packages/requests/packages/urllib3/connection.py", line 163, in connect
  File "site-packages/requests/packages/urllib3/connection.py", line 147, in _new_conn
requests.packages.urllib3.exceptions.NewConnectionError: <requests.packages.urllib3.connection.HTTPConnection object at 0x7fdae21e6b38>: Failed to establish a new connection: [Errno 111] Connection refused

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "site-packages/requests/adapters.py", line 423, in send
  File "site-packages/requests/packages/urllib3/connectionpool.py", line 643, in urlopen
  File "site-packages/requests/packages/urllib3/util/retry.py", line 363, in increment
requests.packages.urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='10.0.2.15', port=27701): Max retries exceeded with url: /sync/hostKey (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x7fdae21e6b38>: Failed to establish a new connection: [Errno 111] Connection refused',))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "aqt/sync.py", line 323, in run
  File "aqt/sync.py", line 338, in _sync
  File "anki/sync.py", line 587, in hostKey
  File "anki/sync.py", line 561, in req
  File "anki/sync.py", line 468, in post
  File "site-packages/requests/sessions.py", line 535, in post
  File "site-packages/requests/sessions.py", line 488, in request
  File "site-packages/requests/sessions.py", line 609, in send
  File "site-packages/requests/adapters.py", line 487, in send
requests.exceptions.ConnectionError: HTTPConnectionPool(host='10.0.2.15', port=27701): Max retries exceeded with url: /sync/hostKey (Caused by NewConnectionError('<requests.packages.urllib3.connection.HTTPConnection object at 0x7fdae21e6b38>: Failed to establish a new connection: [Errno 111] Connection refused',))

Some success - I have been able to get @tsudoko's version of the server to sync with Anki 2.0.39. However, I still haven't gotten it to sync with Anki 2.1.0a8.

I did have to change the dependency WebOb in setup.py to an older version 1.6.0. There was a new version 1.7.0 released after this fork was created that causes an error.

I changed

    install_requires=[
        "PasteDeploy>=1.3.2",
        "PasteScript>=1.7.3",
        "WebOb>=0.9.7",
        "SQLAlchemy>=0.6.3",
],

to

    install_requires=[
        "PasteDeploy>=1.3.2",
        "PasteScript>=1.7.3",
        "WebOb==1.6.0",
        "SQLAlchemy>=0.6.3",
],

That gets Anki 2.0.39 to sync with the server. Using the latest WebOb 1.7.0 causes the following error when I try to run the server in debug mode:

Starting server in PID 11993.
serving on http://10.0.2.15:27701
----------------------------------------
Exception happened during processing of request from ('10.0.2.15', 60504)
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/paste/httpserver.py", line 304, in wsgi_execute
    self.wsgi_start_response)
  File "/usr/local/lib/python3.5/dist-packages/paste/translogger.py", line 69, in __call__
    return self.application(environ, replacement_start_response)
  File "/usr/local/lib/python3.5/dist-packages/paste/urlmap.py", line 216, in __call__
    return app(environ, start_response)
  File "/usr/local/lib/python3.5/dist-packages/webob/dec.py", line 131, in __call__
    resp = self.call_func(req, *args, **self.kwargs)
  File "/usr/local/lib/python3.5/dist-packages/webob/dec.py", line 196, in call_func
    return self.func(req, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/AnkiServer/apps/sync_app.py", line 544, in __call__
    body=json.dumps(result))
  File "/usr/local/lib/python3.5/dist-packages/webob/response.py", line 310, in __init__
    "You cannot set the body to a text value without a "
TypeError: You cannot set the body to a text value without a charset

Even after changing WebOb to 1.6.0, I still can't get Anki 2.1.0a8 to sync. The program displays the message:

Syncing failed: 'list indices must be integers or slices, not str'

Unfortunately I do not know how to display the actual error that occurred.

The server itself does not display any error messages in debug mode.

Thank you for actually trying it out. Both problems should be fixed now. By the way, it might be better to report further issues at tsudoko/anki-sync-server if they're related to my Python 3 fork, at least for now.

Thanks for the fixes. I have gotten collections to sync back and forth with the latest revisions. I am getting some weird behavior from the GUI but maybe that's due to bugs in Anki alpha itself. I'll keep testing it and report issues in your fork if I find any.