0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-12-01 11:41:20 +01:00

Merge branch 'kaedroho-python3-take2'

This commit is contained in:
Matt Westcott 2014-07-02 17:18:15 +01:00
commit 4f92311b37
43 changed files with 278 additions and 153 deletions

View File

@ -6,11 +6,6 @@ python:
env:
- DJANGO_VERSION=Django==1.6.5
#- DJANGO_VERSION=Django==1.7.0
matrix:
allow_failures:
- python: 3.4
#- env: DJANGO_VERSION=Django==1.7.0
fast_finish: true
# Services
services:
- redis-server

View File

@ -1,5 +1,8 @@
#!/usr/bin/env python
import sys
try:
from setuptools import setup, find_packages
except ImportError:
@ -16,6 +19,31 @@ except ImportError:
pass
PY3 = sys.version_info[0] == 3
install_requires = [
"Django>=1.6.2,<1.7",
"South>=0.8.4",
"django-compressor>=1.3",
"django-libsass>=0.1",
"django-modelcluster>=0.1",
"django-taggit==0.11.2",
"django-treebeard==2.0",
"Pillow>=2.3.0",
"beautifulsoup4>=4.3.2",
"lxml>=3.3.0",
"Unidecode>=0.04.14",
"six==1.7.3",
]
if not PY3:
install_requires += [
"unicodecsv>=0.9.4"
]
setup(
name='wagtail',
version='0.3.1',
@ -37,23 +65,11 @@ setup(
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Framework :: Django',
'Topic :: Internet :: WWW/HTTP :: Site Management',
],
install_requires=[
"Django>=1.6.2,<1.7",
"South>=0.8.4",
"django-compressor>=1.3",
"django-libsass>=0.1",
"django-modelcluster>=0.1",
"django-taggit==0.11.2",
"django-treebeard==2.0",
"Pillow>=2.3.0",
"beautifulsoup4>=4.3.2",
"lxml>=3.3.0",
'unicodecsv>=0.9.4',
'Unidecode>=0.04.14',
"BeautifulSoup==3.2.1", # django-compressor gets confused if we have lxml but not BS3 installed
],
install_requires=install_requires,
zip_safe=False,
)

43
tox.ini
View File

@ -1,15 +1,17 @@
[deps]
dj16=
Django>=1.6,<1.7
pyelasticsearch==0.6.1
elasticutils==0.8.2
elasticsearch==1.0.0
mock==1.0.1
[tox]
envlist =
py26-dj16-postgres,
py26-dj16-sqlite,
py27-dj16-postgres,
py27-dj16-sqlite
py27-dj16-sqlite,
py33-dj16-postgres,
py34-dj16-postgres
# mysql not currently supported
# (wagtail.wagtailimages.tests.TestImageEditView currently fails with a
@ -17,6 +19,11 @@ envlist =
# py26-dj16-mysql
# py27-dj16-mysql
# South fails with sqlite on python3, because it tries to use DryRunMigrator which uses iteritems
# py33-dj16-sqlite,
# py34-dj16-sqlite
[testenv]
commands=./runtests.py
@ -67,3 +74,33 @@ deps =
setenv =
DATABASE_ENGINE=django.db.backends.mysql
DATABASE_USER=wagtail
[testenv:py33-dj16-postgres]
basepython=python3.3
deps =
{[deps]dj16}
psycopg2==2.5.2
setenv =
DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
[testenv:py33-dj16-sqlite]
basepython=python3.3
deps =
{[deps]dj16}
setenv =
DATABASE_ENGINE=django.db.backends.sqlite3
[testenv:py34-dj16-postgres]
basepython=python3.4
deps =
{[deps]dj16}
psycopg2==2.5.2
setenv =
DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
[testenv:py34-dj16-sqlite]
basepython=python3.4
deps =
{[deps]dj16}
setenv =
DATABASE_ENGINE=django.db.backends.sqlite3

View File

@ -1,6 +1,9 @@
from django.db import models
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.encoding import python_2_unicode_compatible
from modelcluster.fields import ParentalKey
from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel, PageChooserPanel
@ -259,6 +262,7 @@ FormPage.content_panels = [
# Snippets
@python_2_unicode_compatible
class Advert(models.Model):
url = models.URLField(null=True, blank=True)
text = models.CharField(max_length=255)
@ -268,7 +272,7 @@ class Advert(models.Model):
FieldPanel('text'),
]
def __unicode__(self):
def __str__(self):
return self.text
@ -281,18 +285,20 @@ register_snippet(Advert)
# to ensure specific [in]correct register ordering
# AlphaSnippet is registered during TestSnippetOrdering
@python_2_unicode_compatible
class AlphaSnippet(models.Model):
text = models.CharField(max_length=255)
def __unicode__(self):
def __str__(self):
return self.text
# ZuluSnippet is registered during TestSnippetOrdering
@python_2_unicode_compatible
class ZuluSnippet(models.Model):
text = models.CharField(max_length=255)
def __unicode__(self):
def __str__(self):
return self.text

View File

@ -2,6 +2,9 @@ import copy
import re
import datetime
from six import string_types
from six import text_type
from taggit.forms import TagWidget
from modelcluster.forms import ClusterForm, ClusterFormMetaclass
@ -245,7 +248,7 @@ class EditHandler(object):
"""
rendered_fields = self.rendered_fields()
missing_fields_html = [
unicode(self.form[field_name])
text_type(self.form[field_name])
for field_name in self.form.fields
if field_name not in rendered_fields
]
@ -483,7 +486,7 @@ class BasePageChooserPanel(BaseChooserPanel):
def target_content_type(cls):
if cls._target_content_type is None:
if cls.page_type:
if isinstance(cls.page_type, basestring):
if isinstance(cls.page_type, string_types):
# translate the passed model name into an actual model class
from django.db.models import get_model
try:

View File

@ -1,3 +1,5 @@
from six import text_type
from django.utils.text import slugify
from django.utils.html import format_html
@ -7,7 +9,7 @@ class MenuItem(object):
self.label = label
self.url = url
self.classnames = classnames
self.name = (name or slugify(unicode(label)))
self.name = (name or slugify(text_type(label)))
self.order = order
def render_html(self):

View File

@ -8,9 +8,11 @@
### <img src="{% gravatar_url sometemplatevariable %}">
### just make sure to update the "default" image path below
import urllib
import hashlib
from six import b
from six.moves.urllib.parse import urlencode
from django import template
register = template.Library()
@ -30,8 +32,8 @@ class GravatarUrlNode(template.Node):
default = "blank"
size = int(self.size) * 2 # requested at retina size by default and scaled down at point of use with css
gravatar_url = "//www.gravatar.com/avatar/" + hashlib.md5(email.lower()).hexdigest() + "?"
gravatar_url += urllib.urlencode({'s': str(size), 'd': default})
gravatar_url = "//www.gravatar.com/avatar/" + hashlib.md5(b(email.lower())).hexdigest() + "?"
gravatar_url += urlencode({'s': str(size), 'd': default})
return gravatar_url

View File

@ -343,7 +343,7 @@ def edit(request, page_id):
edit_handler = edit_handler_class(instance=page, form=form)
errors_debug = (
repr(edit_handler.form.errors)
+ repr([(name, formset.errors) for (name, formset) in edit_handler.form.formsets.iteritems() if formset.errors])
+ repr([(name, formset.errors) for (name, formset) in edit_handler.form.formsets.items() if formset.errors])
)
else:
form = form_class(instance=page)

View File

@ -1,3 +1,5 @@
from __future__ import print_function
import datetime
import json
from optparse import make_option
@ -31,7 +33,7 @@ class Command(BaseCommand):
def handle(self, *args, **options):
dryrun = False
if options['dryrun']:
print "Will do a dry run."
print("Will do a dry run.")
dryrun = True
# 1. get all expired pages with live = True
@ -41,17 +43,17 @@ class Command(BaseCommand):
)
if dryrun:
if expired_pages:
print "Expired pages to be deactivated:"
print "Expiry datetime\t\tSlug\t\tName"
print "---------------\t\t----\t\t----"
print("Expired pages to be deactivated:")
print("Expiry datetime\t\tSlug\t\tName")
print("---------------\t\t----\t\t----")
for ep in expired_pages:
print "{0}\t{1}\t{2}".format(
print("{0}\t{1}\t{2}".format(
ep.expire_at.strftime("%Y-%m-%d %H:%M"),
ep.slug,
ep.title
)
))
else:
print "No expired pages to be deactivated found."
print("No expired pages to be deactivated found.")
else:
expired_pages.update(expired=True, live=False)
@ -62,22 +64,22 @@ class Command(BaseCommand):
) if revision_date_expired(r)
]
if dryrun:
print "---------------------------------"
print("---------------------------------")
if expired_revs:
print "Expired revisions to be dropped from moderation queue:"
print "Expiry datetime\t\tSlug\t\tName"
print "---------------\t\t----\t\t----"
print("Expired revisions to be dropped from moderation queue:")
print("Expiry datetime\t\tSlug\t\tName")
print("---------------\t\t----\t\t----")
for er in expired_revs:
rev_data = json.loads(er.content_json)
print "{0}\t{1}\t{2}".format(
print("{0}\t{1}\t{2}".format(
dateparse.parse_datetime(
rev_data.get('expire_at')
).strftime("%Y-%m-%d %H:%M"),
rev_data.get('slug'),
rev_data.get('title')
)
))
else:
print "No expired revision to be dropped from moderation."
print("No expired revision to be dropped from moderation.")
else:
for er in expired_revs:
er.submitted_for_moderation = False
@ -88,20 +90,20 @@ class Command(BaseCommand):
approved_go_live_at__lt=timezone.now()
)
if dryrun:
print "---------------------------------"
print("---------------------------------")
if revs_for_publishing:
print "Revisions to be published:"
print "Go live datetime\t\tSlug\t\tName"
print "---------------\t\t\t----\t\t----"
print("Revisions to be published:")
print("Go live datetime\t\tSlug\t\tName")
print("---------------\t\t\t----\t\t----")
for rp in revs_for_publishing:
rev_data = json.loads(rp.content_json)
print "{0}\t\t{1}\t{2}".format(
print("{0}\t\t{1}\t{2}".format(
rp.approved_go_live_at.strftime("%Y-%m-%d %H:%M"),
rev_data.get('slug'),
rev_data.get('title')
)
))
else:
print "No pages to go live."
print("No pages to go live.")
else:
for rp in revs_for_publishing:
# just run publish for the revision -- since the approved go

View File

@ -1,7 +1,10 @@
from StringIO import StringIO
from urlparse import urlparse
import warnings
import six
from six import string_types
from six import StringIO
from six.moves.urllib.parse import urlparse
from modelcluster.models import ClusterableModel
from django.db import models, connection, transaction
@ -19,6 +22,7 @@ from django.utils.translation import ugettext
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.utils.functional import cached_property
from django.utils.encoding import python_2_unicode_compatible
from treebeard.mp_tree import MP_Node
@ -33,6 +37,7 @@ class SiteManager(models.Manager):
return self.get(hostname=hostname, port=port)
@python_2_unicode_compatible
class Site(models.Model):
hostname = models.CharField(max_length=255, db_index=True)
port = models.IntegerField(default=80, help_text=_("Set this to something other than 80 if you need a specific port number to appear in URLs (e.g. development on port 8000). Does not affect request handling (so port forwarding still works)."))
@ -45,7 +50,7 @@ class Site(models.Model):
def natural_key(self):
return (self.hostname, self.port)
def __unicode__(self):
def __str__(self):
return self.hostname + ("" if self.port == 80 else (":%d" % self.port)) + (" [default]" if self.is_default_site else "")
@staticmethod
@ -255,9 +260,8 @@ class PageBase(models.base.ModelBase):
PAGE_MODEL_CLASSES.append(cls)
class Page(MP_Node, ClusterableModel, Indexed):
__metaclass__ = PageBase
@python_2_unicode_compatible
class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, Indexed)):
title = models.CharField(max_length=255, help_text=_("The page title as you'd like it to be seen by the public"))
slug = models.SlugField(help_text=_("The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/"))
# TODO: enforce uniqueness on slug field per parent (will have to be done at the Django
@ -300,7 +304,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
# created as
self.content_type = ContentType.objects.get_for_model(self)
def __unicode__(self):
def __str__(self):
return self.title
is_abstract = True # don't offer Page in the list of page types a superuser can create
@ -536,7 +540,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
else:
res = []
for page_type in cls.subpage_types:
if isinstance(page_type, basestring):
if isinstance(page_type, string_types):
try:
app_label, model_name = page_type.split(".")
except ValueError:
@ -770,6 +774,7 @@ class SubmittedRevisionsManager(models.Manager):
return super(SubmittedRevisionsManager, self).get_queryset().filter(submitted_for_moderation=True)
@python_2_unicode_compatible
class PageRevision(models.Model):
page = models.ForeignKey('Page', related_name='revisions')
submitted_for_moderation = models.BooleanField(default=False)
@ -827,7 +832,7 @@ class PageRevision(models.Model):
self.submitted_for_moderation = False
page.revisions.update(submitted_for_moderation=False)
def __unicode__(self):
def __str__(self):
return '"' + unicode(self.page) + '" at ' + unicode(self.created_at)

View File

@ -166,8 +166,8 @@ class DbWhitelister(Whitelister):
def clean(cls, html):
if not cls.has_loaded_custom_whitelist_rules:
for fn in hooks.get_hooks('construct_whitelister_element_rules'):
cls.element_rules = dict(
cls.element_rules.items() + fn().items())
cls.element_rules = cls.element_rules.copy()
cls.element_rules.update(fn())
cls.has_loaded_custom_whitelist_rules = True
return super(DbWhitelister, cls).clean(html)

View File

@ -1,6 +1,7 @@
from StringIO import StringIO
from datetime import timedelta
from six import StringIO
from django.test import TestCase, Client
from django.http import HttpRequest, Http404
from django.core import management

View File

@ -1,4 +1,4 @@
from StringIO import StringIO
from six import StringIO
from django.test import TestCase, Client
from django.http import HttpRequest, Http404

View File

@ -1,4 +1,4 @@
from StringIO import StringIO
from six import StringIO
from django.test import TestCase, Client
from django.http import HttpRequest, Http404

View File

@ -1,4 +1,4 @@
from StringIO import StringIO
from six import StringIO
from django.test import TestCase, Client
from django.http import HttpRequest, Http404

View File

@ -2,8 +2,9 @@
A generic HTML whitelisting engine, designed to accommodate subclassing to override
specific rules.
"""
from six.moves.urllib.parse import urlparse
from bs4 import BeautifulSoup, NavigableString, Tag
from urlparse import urlparse
ALLOWED_URL_SCHEMES = ['', 'http', 'https', 'ftp', 'mailto', 'tel']
@ -28,7 +29,7 @@ def attribute_rule(allowed_attrs):
* if the lookup returns a truthy value, keep the attribute; if falsy, drop it
"""
def fn(tag):
for attr, val in tag.attrs.items():
for attr, val in list(tag.attrs.items()):
rule = allowed_attrs.get(attr)
if rule:
if callable(rule):
@ -82,7 +83,7 @@ class Whitelister(object):
attributes"""
doc = BeautifulSoup(html, 'lxml')
cls.clean_node(doc, doc)
return unicode(doc)
return doc.decode()
@classmethod
def clean_node(cls, doc, node):

View File

@ -9,10 +9,12 @@ from django.dispatch import Signal
from django.core.urlresolvers import reverse
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible
from wagtail.wagtailadmin.taggable import TagSearchable
@python_2_unicode_compatible
class Document(models.Model, TagSearchable):
title = models.CharField(max_length=255, verbose_name=_('Title'))
file = models.FileField(upload_to='documents' , verbose_name=_('File'))
@ -30,7 +32,7 @@ class Document(models.Model, TagSearchable):
},
}
def __unicode__(self):
def __str__(self):
return self.title
@property

View File

@ -1,10 +1,14 @@
from six import b
from django.test import TestCase
from wagtail.wagtaildocs import models
from wagtail.tests.utils import WagtailTestUtils
from django.contrib.auth.models import User, Group, Permission
from django.core.urlresolvers import reverse
from django.core.files.base import ContentFile
from wagtail.wagtaildocs import models
from wagtail.tests.utils import WagtailTestUtils
# TODO: Test serve view
@ -112,7 +116,7 @@ class TestDocumentAddView(TestCase, WagtailTestUtils):
def test_post(self):
# Build a fake file
fake_file = ContentFile("A boring example document")
fake_file = ContentFile(b("A boring example document"))
fake_file.name = 'test.txt'
# Submit
@ -134,7 +138,7 @@ class TestDocumentEditView(TestCase, WagtailTestUtils):
self.login()
# Build a fake file
fake_file = ContentFile("A boring example document")
fake_file = ContentFile(b("A boring example document"))
fake_file.name = 'test.txt'
# Create a document to edit
@ -147,7 +151,7 @@ class TestDocumentEditView(TestCase, WagtailTestUtils):
def test_post(self):
# Build a fake file
fake_file = ContentFile("A boring example document")
fake_file = ContentFile(b("A boring example document"))
fake_file.name = 'test.txt'
# Submit title change
@ -272,7 +276,7 @@ class TestDocumentChooserUploadView(TestCase, WagtailTestUtils):
def test_post(self):
# Build a fake file
fake_file = ContentFile("A boring example document")
fake_file = ContentFile(b("A boring example document"))
fake_file.name = 'test.txt'
# Submit

View File

@ -1,4 +1,6 @@
import sys
from datetime import datetime
import json
try:
from importlib import import_module
@ -6,13 +8,20 @@ except ImportError:
# for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7)
from django.utils.importlib import import_module
# Needs to be imported like this to allow @patch to work in tests
from six.moves.urllib import request as urllib_request
from six.moves.urllib.request import Request
from six.moves.urllib.error import URLError
from six.moves.urllib.parse import urlencode
from django.conf import settings
from datetime import datetime
from django.utils import six
from wagtail.wagtailembeds.oembed_providers import get_oembed_provider
from wagtail.wagtailembeds.models import Embed
import urllib2, urllib
import json
class EmbedNotFoundException(Exception): pass
@ -99,11 +108,11 @@ def oembed(url, max_width=None):
params['maxwidth'] = max_width
# Perform request
request = urllib2.Request(provider + '?' + urllib.urlencode(params))
request = Request(provider + '?' + urlencode(params))
request.add_header('User-agent', 'Mozilla/5.0')
try:
r = urllib2.urlopen(request)
except urllib2.URLError:
r = urllib_request.urlopen(request)
except URLError:
raise EmbedNotFoundException
oembed = json.loads(r.read())

View File

@ -1,4 +1,5 @@
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
EMBED_TYPES = (
@ -9,6 +10,7 @@ EMBED_TYPES = (
)
@python_2_unicode_compatible
class Embed(models.Model):
url = models.URLField()
max_width = models.SmallIntegerField(null=True, blank=True)
@ -25,5 +27,5 @@ class Embed(models.Model):
class Meta:
unique_together = ('url', 'max_width')
def __unicode__(self):
def __str__(self):
return self.url

View File

@ -1,5 +1,8 @@
from six.moves.urllib.request import urlopen
import six.moves.urllib.request
from six.moves.urllib.error import URLError
from mock import patch
import urllib2
try:
import embedly
@ -217,12 +220,12 @@ class TestOembed(TestCase):
self.assertRaises(EmbedNotFoundException, wagtail_oembed, "foo")
def test_oembed_invalid_request(self):
config = {'side_effect': urllib2.URLError('foo')}
with patch.object(urllib2, 'urlopen', **config) as urlopen:
config = {'side_effect': URLError('foo')}
with patch.object(six.moves.urllib.request, 'urlopen', **config) as urlopen:
self.assertRaises(EmbedNotFoundException, wagtail_oembed,
"http://www.youtube.com/watch/")
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_oembed_photo_request(self, loads, urlopen) :
urlopen.return_value = self.dummy_response
@ -233,7 +236,7 @@ class TestOembed(TestCase):
self.assertEqual(result['html'], '<img src="http://www.example.com" />')
loads.assert_called_with("foo")
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_oembed_return_values(self, loads, urlopen):
urlopen.return_value = self.dummy_response
@ -268,7 +271,7 @@ class TestEmbedFilter(TestCase):
return "foo"
self.dummy_response = DummyResponse()
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_valid_embed(self, loads, urlopen):
urlopen.return_value = self.dummy_response
@ -277,7 +280,7 @@ class TestEmbedFilter(TestCase):
result = embed_filter('http://www.youtube.com/watch/')
self.assertEqual(result, '<img src="http://www.example.com" />')
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_render_filter(self, loads, urlopen):
urlopen.return_value = self.dummy_response
@ -288,7 +291,7 @@ class TestEmbedFilter(TestCase):
result = temp.render(context)
self.assertEqual(result, '<img src="http://www.example.com" />')
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_render_filter_nonexistent_type(self, loads, urlopen):
urlopen.return_value = self.dummy_response
@ -307,7 +310,7 @@ class TestEmbedlyFilter(TestEmbedFilter):
return "foo"
self.dummy_response = DummyResponse()
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_valid_embed(self, loads, urlopen):
urlopen.return_value = self.dummy_response
@ -316,7 +319,7 @@ class TestEmbedlyFilter(TestEmbedFilter):
result = embedly_filter('http://www.youtube.com/watch/')
self.assertEqual(result, '<img src="http://www.example.com" />')
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_render_filter(self, loads, urlopen):
urlopen.return_value = self.dummy_response
@ -327,7 +330,7 @@ class TestEmbedlyFilter(TestEmbedFilter):
result = temp.render(context)
self.assertEqual(result, '<img src="http://www.example.com" />')
@patch('urllib2.urlopen')
@patch('six.moves.urllib.request.urlopen')
@patch('json.loads')
def test_render_filter_nonexistent_type(self, loads, urlopen):
urlopen.return_value = self.dummy_response

View File

@ -1,11 +1,15 @@
import json
import re
from six import text_type
from unidecode import unidecode
from django.db import models
from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _
from django.utils.text import slugify
from unidecode import unidecode
import json
import re
from django.utils.encoding import python_2_unicode_compatible
from wagtail.wagtailcore.models import Page, Orderable, UserPagePermissionsProxy, get_page_types
from wagtail.wagtailadmin.edit_handlers import FieldPanel
@ -32,6 +36,7 @@ FORM_FIELD_CHOICES = (
HTML_EXTENSION_RE = re.compile(r"(.*)\.html")
@python_2_unicode_compatible
class FormSubmission(models.Model):
"""Data for a Form submission."""
@ -43,7 +48,7 @@ class FormSubmission(models.Model):
def get_data(self):
return json.loads(self.form_data)
def __unicode__(self):
def __str__(self):
return self.form_data
@ -73,7 +78,7 @@ class AbstractFormField(Orderable):
# unidecode will return an ascii string while slugify wants a
# unicode string on the other hand, slugify returns a safe-string
# which will be converted to a normal str
return str(slugify(unicode(unidecode(self.label))))
return str(slugify(text_type(unidecode(self.label))))
panels = [
FieldPanel('label'),

View File

@ -258,5 +258,5 @@ class TestFormsSubmissions(TestCase):
# Check response
self.assertEqual(response.status_code, 200)
data_line = response.content.split("\n")[1]
data_line = response.content.decode().split("\n")[1]
self.assertTrue('new@example.com' in data_line)

View File

@ -1,5 +1,11 @@
import datetime
import unicodecsv
try:
import unicodecsv as csv
using_unicodecsv = True
except ImportError:
import csv
using_unicodecsv = False
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.exceptions import PermissionDenied
@ -65,7 +71,11 @@ def list_submissions(request, page_id):
# return a CSV instead
response = HttpResponse(content_type='text/csv; charset=utf-8')
response['Content-Disposition'] = 'attachment;filename=export.csv'
writer = unicodecsv.writer(response, encoding='utf-8')
if using_unicodecsv:
writer = csv.writer(response, encoding='utf-8')
else:
writer = csv.writer(response)
header_row = ['Submission date'] + [label for name, label in data_fields]

View File

@ -8,11 +8,15 @@ except ImportError:
# for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7)
from django.utils.importlib import import_module
from django.utils import six
import sys
from django.conf import settings
from base import InvalidImageBackendError
from django.utils import six
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
class InvalidImageBackendError(ImproperlyConfigured):
pass
# Pinched from django 1.7 source code.
# TODO: Replace this with "from django.utils.module_loading import import_string"

View File

@ -1,9 +1,4 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
class InvalidImageBackendError(ImproperlyConfigured):
pass
class BaseImageBackend(object):

View File

@ -1,7 +1,8 @@
import StringIO
import os.path
import re
from six import BytesIO
from taggit.managers import TaggableManager
from django.core.files import File
@ -13,6 +14,7 @@ from django.utils.safestring import mark_safe
from django.utils.html import escape, format_html_join
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible
from unidecode import unidecode
@ -21,6 +23,7 @@ from wagtail.wagtailimages.backends import get_image_backend
from .utils import validate_image_format
@python_2_unicode_compatible
class AbstractImage(models.Model, TagSearchable):
title = models.CharField(max_length=255, verbose_name=_('Title') )
@ -54,7 +57,7 @@ class AbstractImage(models.Model, TagSearchable):
},
}
def __unicode__(self):
def __str__(self):
return self.title
def get_rendition(self, filter):
@ -208,7 +211,7 @@ class Filter(models.Model):
image = method(image, self.method_arg)
output = StringIO.StringIO()
output = BytesIO()
backend.save_image(image, output, file_format)
# and then close the input file

View File

@ -18,11 +18,11 @@ from wagtail.wagtailimages.backends import get_image_backend
from wagtail.wagtailimages.backends.pillow import PillowBackend
def get_test_image_file():
from StringIO import StringIO
from six import BytesIO
from PIL import Image
from django.core.files.images import ImageFile
f = StringIO()
f = BytesIO()
image = Image.new('RGB', (640, 480), 'white')
image.save(f, 'PNG')
return ImageFile(f, name='test.png')

View File

@ -1,6 +1,6 @@
from django import http
import models
from wagtail.wagtailredirects import models
# Originally pinched from: https://github.com/django/django/blob/master/django/contrib/redirects/middleware.py

View File

@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, PageChooserPanel
from urlparse import urlparse
from six.moves.urllib.parse import urlparse
class Redirect(models.Model):

View File

@ -8,7 +8,7 @@ from django.views.decorators.vary import vary_on_headers
from wagtail.wagtailadmin.edit_handlers import ObjectList
from wagtail.wagtailadmin.forms import SearchForm
import models
from wagtail.wagtailredirects import models
REDIRECT_EDIT_HANDLER = ObjectList(models.Redirect.content_panels)

View File

@ -1,3 +1,3 @@
from indexed import Indexed
from signal_handlers import register_signal_handlers
from backends import get_search_backend
from wagtail.wagtailsearch.indexed import Indexed
from wagtail.wagtailsearch.signal_handlers import register_signal_handlers
from wagtail.wagtailsearch.backends import get_search_backend

View File

@ -8,10 +8,15 @@ except ImportError:
# for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7)
from django.utils.importlib import import_module
from django.utils import six
import sys
from django.utils import six
from django.conf import settings
from base import InvalidSearchBackendError
from django.core.exceptions import ImproperlyConfigured
class InvalidSearchBackendError(ImproperlyConfigured):
pass
# Pinched from django 1.7 source code.

View File

@ -1,13 +1,8 @@
from django.db import models
from django.core.exceptions import ImproperlyConfigured
from wagtail.wagtailsearch.indexed import Indexed
class InvalidSearchBackendError(ImproperlyConfigured):
pass
class BaseSearch(object):
def __init__(self, params):
pass

View File

@ -338,10 +338,11 @@ class ElasticSearch(BaseSearch):
indexed_fields = model.indexed_get_indexed_fields()
# Make field list
fields = dict({
fields = {
"pk": dict(type="string", index="not_analyzed", store="yes"),
"content_type": dict(type="string"),
}.items() + indexed_fields.items())
}
fields.update(indexed_fields)
# Put mapping
self.es.indices.put_mapping(index=self.es_index, doc_type=content_type, body={

View File

@ -1,7 +1,8 @@
from django import forms
from django.forms.models import inlineformset_factory
from django.utils.translation import ugettext_lazy as _
import models
from wagtail.wagtailsearch import models
class QueryForm(forms.Form):
@ -48,7 +49,7 @@ class EditorsPickFormSet(EditorsPickFormSetBase):
# Check there is at least one non-deleted form.
non_deleted_forms = self.total_form_count()
non_empty_forms = 0
for i in xrange(0, self.total_form_count()):
for i in range(0, self.total_form_count()):
form = self.forms[i]
if self.can_delete and self._should_delete_form(form):
non_deleted_forms -= 1

View File

@ -1,3 +1,5 @@
from six import string_types
from django.db import models
@ -35,21 +37,27 @@ class Indexed(object):
def indexed_get_indexed_fields(cls):
# Get indexed fields for this class as dictionary
indexed_fields = cls.indexed_fields
if isinstance(indexed_fields, tuple):
indexed_fields = list(indexed_fields)
if isinstance(indexed_fields, basestring):
indexed_fields = [indexed_fields]
if isinstance(indexed_fields, list):
indexed_fields = dict((field, dict(type="string")) for field in indexed_fields)
if not isinstance(indexed_fields, dict):
raise ValueError()
if isinstance(indexed_fields, dict):
# Make sure we have a copy to prevent us accidentally changing the configuration
indexed_fields = indexed_fields.copy()
else:
# Convert to dict
if isinstance(indexed_fields, tuple):
indexed_fields = list(indexed_fields)
if isinstance(indexed_fields, string_types):
indexed_fields = [indexed_fields]
if isinstance(indexed_fields, list):
indexed_fields = dict((field, dict(type="string")) for field in indexed_fields)
if not isinstance(indexed_fields, dict):
raise ValueError()
# Get indexed fields for parent class
parent = cls.indexed_get_parent(require_model=False)
if parent:
# Add parent fields into this list
parent_indexed_fields = parent.indexed_get_indexed_fields()
indexed_fields = dict(parent_indexed_fields.items() + indexed_fields.items())
parent_indexed_fields = parent.indexed_get_indexed_fields().copy()
parent_indexed_fields.update(indexed_fields)
indexed_fields = parent_indexed_fields
return indexed_fields
def indexed_get_document_id(self):

View File

@ -2,11 +2,13 @@ import datetime
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from wagtail.wagtailsearch.indexed import Indexed
from wagtail.wagtailsearch.utils import normalise_query_string, MAX_QUERY_STRING_LENGTH
@python_2_unicode_compatible
class Query(models.Model):
query_string = models.CharField(max_length=MAX_QUERY_STRING_LENGTH, unique=True)
@ -23,7 +25,7 @@ class Query(models.Model):
daily_hits.hits = models.F('hits') + 1
daily_hits.save()
def __unicode__(self):
def __str__(self):
return self.query_string
@property

View File

@ -1,12 +1,14 @@
from six import StringIO
from django.test import TestCase
from django.test.utils import override_settings
from django.conf import settings
from django.core import management
from wagtail.tests.utils import unittest
from wagtail.wagtailsearch import models, get_search_backend
from wagtail.wagtailsearch.backends.db import DBSearch
from wagtail.wagtailsearch.backends import InvalidSearchBackendError
from StringIO import StringIO
# Register wagtailsearch signal handlers
@ -19,7 +21,7 @@ class BackendTests(object):
def setUp(self):
# Search WAGTAILSEARCH_BACKENDS for an entry that uses the given backend path
for (backend_name, backend_conf) in settings.WAGTAILSEARCH_BACKENDS.iteritems():
for backend_name, backend_conf in settings.WAGTAILSEARCH_BACKENDS.items():
if backend_conf['BACKEND'] == self.backend_path:
self.backend = get_search_backend(backend_name)
break

View File

@ -1,4 +1,4 @@
from StringIO import StringIO
from six import StringIO
from django.test import TestCase
from django.core import management

View File

@ -1 +1 @@
from frontend import search
from wagtail.wagtailsearch.views.frontend import search

View File

@ -1,5 +1,7 @@
import json
from six import text_type
from django.shortcuts import get_object_or_404
from django.contrib.auth.decorators import permission_required
@ -35,7 +37,7 @@ def chosen(request, content_type_app_name, content_type_model_name, id):
snippet_json = json.dumps({
'id': item.id,
'string': unicode(item),
'string': text_type(item),
})
return render_modal_workflow(

View File

@ -1,8 +1,10 @@
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
@ -25,5 +27,5 @@ class UserProfile(models.Model):
def get_for_user(cls, user):
return cls.objects.get_or_create(user=user)[0]
def __unicode__(self):
def __str__(self):
return self.user.username