Skip to content

Enhanced Python 3 Support #2819

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 66 commits into from
Closed

Enhanced Python 3 Support #2819

wants to merge 66 commits into from

Conversation

gthank
Copy link
Contributor

@gthank gthank commented May 4, 2017

I just set up a new instance of ReadTheDocs under Python 3.5 on FreeBSD. These are the changes I needed to make to complete the installation instructions. There are probably additional changes required to achieve comprehensive Python 3 support, but I hope this is seen as a good start.

@agjohnson agjohnson added PR: ready for review PR: work in progress Pull request is not ready for full review and removed PR: ready for review labels May 4, 2017
@agjohnson
Copy link
Contributor

Thanks for giving this a go! The larger issue is all of the dependencies that we have that are python 2 only and perhaps unmaintained. This is where I stopped last time I was working on python 3 support.

This PR is going to be prone to breakage while it is still a WIP, as we can't easily do this change incrementally.

@gthank
Copy link
Contributor Author

gthank commented May 5, 2017

Dependencies are obviously a potential source of headaches. I can report that as of right now, the changes I submitted are enough to get RTD installed and running under Gunicorn on FreeBSD 11 w/ Python 3.5. I've so far managed to sign up a new user (though it won't stay logged in; I suspect I've missed some config overrides) and am not seeing any actual exceptions.

Since I'm only self-hosting, that's probably not enough to test all the integrations, etc. and flush out the bugs, but the changes I've submitted so far should be fairly safe for doing single codebase. It's consisted mostly of using six to handle the move of urlparse, a bunch of __future__ imports to get the print() function (and subsequent usage of it instead of the print statement), and a couple of tweaks to the requirements file (one to drop distutils2, since it seems to be obsolete, and a version bump to slumber to get on a version that would install under 3.5.

I'd be happy to work through whatever process is appropriate for getting these changes more fully tested with the goal of getting the PR accepted, as well as to submit more enhancements to fix issues I find while running ReadTheDocs locally.

--Hank

@agjohnson
Copy link
Contributor

Great! I'll check in next week, let us know how testing is going on this. As I recall, there were some old django packages that weren't looking like they would get python3 support -- slumber was the main package lacking python3 support though. Perhaps that has changed. The packages that were going to be a problem also weren't heavily used, so it might introduce some edge case bugs.

It would help to have a core dev pull this down to run tests and QA once it's about ready.

@gthank
Copy link
Contributor Author

gthank commented May 5, 2017

On the subject of dependencies, I found a tool—checkmyreqs—to automatically check the entries in a requirements.txt to see if they are tagged with a given Python version classifier on PyPI. I had to fork it to use HTTPS when talking to PyPI, but after I did that, a check against the dependencies is fairly encouraging:

$ checkmyreqs -p 3.4 -f requirements/pip.txt
Checking for compatibility with Python 3.4
requirements/pip.txt
*****
pip
compatible
-----
appdirs
compatible
-----
virtualenv
compatible
-----
docutils
compatible
-----
Sphinx
compatible
-----
sphinx_rtd_theme
compatible
-----
Pygments
compatible
-----
mkdocs
compatible
-----
readthedocs-build
not specified
-----
django
not listed on pypi.python.org
-----
django-tastypie
compatible
-----
django-haystack
compatible
-----
celery-haystack
not compatible - update to v0.10 for support
-----
django-guardian
compatible
-----
django-extensions
compatible
-----
djangorestframework
compatible
-----
django-vanilla-views
compatible
-----
jsonfield
compatible
-----
pytest-django
compatible
-----
requests
compatible
-----
slumber
not specified
-----
lxml
compatible
-----
django-countries
compatible
-----
redis
compatible
-----
celery
compatible
-----
django-celery
compatible
-----
dnspython
not specified - update to v1.15.0 for explicit support
-----
httplib2
compatible
-----
elasticsearch
compatible
-----
pyelasticsearch
compatible
-----
pyquery
compatible
-----
django-gravatar2
compatible
-----
doc2dash
compatible
-----
pytz
compatible
-----
beautifulsoup4
compatible
-----
Unipath
not specified - update to v1.1 for explicit support
-----
django-kombu
not specified
-----
django-secure
not compatible
-----
mimeparse
not specified
-----
mock
compatible
-----
stripe
compatible
-----
django-copyright
not specified
-----
django-formtools
compatible
-----
django-dynamic-fixture
compatible
-----
docker-py
compatible
-----
django-textclassifier
compatible
-----
django-annoying
compatible
-----
django-messages-extends
not specified
-----
django-filter
compatible
-----
djangorestframework-jsonp
compatible
-----
sphinxcontrib-httpdomain
not specified
-----
commonmark
not listed on pypi.python.org
-----
recommonmark
not specified
-----
packaging
compatible
-----
nilsimsa
not specified
-----

@ericholscher
Copy link
Member

I believe we should be able to slowly upgrade the codebase to support Python 3 without breaking it on Python 2. Things like print() and whatnot are supported in 2.7, so I think thats a good way to start moving forward.

gthank and others added 21 commits May 22, 2017 10:14
django-formtools is passing us the `items()` of a `dict`. In Python 2,
this is a `list`, and can be directly indexed. In Python 3, it is a
"View Object", which cannot be directly indexed.

Since I thought it unlikely for there to be more than a few forms in this
collection, I decided it was easier to simply wrap it in a `list` no
matter what, rather than using `itertools.islice` to grab the first
element.
Python 3 got rid of `unicode`; I used `six.text_type` to support 2 and
3 both.

For Python 3-based projects, Python got rid of `__unicode__`. Now you
use `__str__` and a decorator that is a no-op on Python 3 but maps
things appropriately for Python 2.
* Docstring/PEP 257 fixes for the privacy app

* Docstring/PEP 257 fixes for the profiles app

* Docstring/PEP 257 fixes for the projects app
This will be required before migrating to 4.x, as well as picking up
a few miscellaneous fixes.
@ericholscher
Copy link
Member

Got this running locally:

========================= 26 failed, 408 passed, 1 xfailed, 993 warnings in 75.20 seconds ==========================

Will take a peek.

@ericholscher
Copy link
Member

This got me down to 17 failures:

diff --git a/readthedocs/projects/version_handling.py b/readthedocs/projects/version_handling.py
index fcb725a..2a69c9f 100644
--- a/readthedocs/projects/version_handling.py
+++ b/readthedocs/projects/version_handling.py
@@ -113,7 +113,7 @@ def version_windows(versions, major=1, minor=1, point=1):
 def parse_version_failsafe(version_string):
     try:
         return Version(
-            text_type(unicodedata.normalize('NFKD', text_type(version_string)).encode('ascii', 'ignore'))
+            text_type(unicodedata.normalize('NFKD', text_type(version_string)))
         )
     except (UnicodeError, InvalidVersion):
         return None

@ericholscher
Copy link
Member

Pushed a couple fixes, which got my tests down to ~15 failing. Definitely still need to go through and clean up our unicode fails :)

After inspecting `BuildCommand`, I determined that it always expects
bytes from the `communicate` call, and attempts to decode them (as
UTF-8) into a unicode string. Accordingly, I've changed these unit tests
to explicitly mock the results of `communicate` as UTF-8 bytes, and to
assert equality with unicode strings.
@gthank
Copy link
Contributor Author

gthank commented Jun 9, 2017

Closing this one because I made a different one that is way better.

@gthank gthank closed this Jun 9, 2017
@agjohnson agjohnson removed the PR: work in progress Pull request is not ready for full review label Jun 9, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants