Commit 643f1d79 authored by Claude Paroz's avatar Claude Paroz

Added loads of Python 3 compatibility fixes

parent f034d344
......@@ -6,6 +6,7 @@ from django import forms
from django.core import exceptions
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.utils import six
class DictionaryField(models.Field):
......@@ -19,7 +20,7 @@ class DictionaryField(models.Field):
return None
elif value == "":
return {}
elif isinstance(value, basestring):
elif isinstance(value, six.string_types):
try:
return dict(json.loads(value))
except (ValueError, TypeError):
......@@ -33,7 +34,7 @@ class DictionaryField(models.Field):
def get_prep_value(self, value):
if not value:
return ""
if isinstance(value, basestring):
if isinstance(value, six.string_types):
return value
else:
return json.dumps(value)
......@@ -65,7 +66,7 @@ class JSONField(models.TextField):
return None
try:
if isinstance(value, basestring):
if isinstance(value, six.string_types):
return json.loads(value)
except ValueError:
pass
......
......@@ -30,7 +30,7 @@ class SplitListNode(template.Node):
def split_seq(self, list, cols=2):
start = 0
for i in xrange(cols):
for i in range(cols):
stop = start + len(list[i::cols])
yield list[start:stop]
start = stop
......@@ -43,8 +43,8 @@ def list_to_columns(parser, token):
"""Parse template tag: {% list_to_columns list as new_list 2 %}"""
bits = token.contents.split()
if len(bits) != 5:
raise template.TemplateSyntaxError, "list_to_columns list as new_list 2"
raise template.TemplateSyntaxError("list_to_columns list as new_list 2")
if bits[2] != 'as':
raise template.TemplateSyntaxError, "second argument to the list_to_columns tag must be 'as'"
raise template.TemplateSyntaxError("second argument to the list_to_columns tag must be 'as'")
return SplitListNode(bits[1], bits[4], bits[3])
list_to_columns = register.tag(list_to_columns)
......@@ -129,41 +129,41 @@ def imerge_sorted_by_field(object_list1, object_list2, field):
# StopIteration to find the source.
try:
el1 = iter1.next()
el1 = next(iter1)
except StopIteration:
# Finish the other list
while True:
el2 = iter2.next()
el2 = next(iter2)
yield el2
try:
el2 = iter2.next()
el2 = next(iter2)
except StopIteration:
# Finish the other list
while True:
# el1 is already fetched
yield el1
el1 = iter1.next()
el1 = next(iter1)
while True:
if op(getattr(el1, field), getattr(el2, field)):
yield el1
try:
el1 = iter1.next()
el1 = next(iter1)
except StopIteration:
# Finish the other list
while True:
yield el2
el2 = iter2.next()
el2 = next(iter2)
else:
yield el2
try:
el2 = iter2.next()
el2 = next(iter2)
except StopIteration:
# Finish the other list
while True:
yield el1
el1 = iter1.next()
el1 = next(iter1)
def is_site_admin(user):
return user.is_superuser or settings.ADMIN_GROUP in [g.name for g in user.groups.all()]
......
......@@ -86,7 +86,7 @@ VCS_HOME_WARNING = gettext_noop(u"This module is not part of the GNOME Git repos
# By default, Django stores files locally, using the MEDIA_ROOT and MEDIA_URL settings
UPLOAD_DIR = 'upload'
UPLOAD_ARCHIVED_DIR = 'upload-archived'
FILE_UPLOAD_PERMISSIONS = 0600
FILE_UPLOAD_PERMISSIONS = 0o600
LOGIN_URL = '/login/'
......
......@@ -128,13 +128,13 @@ def language_release_xml(request, locale, release_name):
content += "<untranslated>%s</untranslated>" % categ['catuntrans']
# Modules
for module, mod_stats in categ['modules'].items():
branch_name, domains = mod_stats.items()[0]
branch_name, domains = list(mod_stats.items())[0]
content += "<module id=\"%s\" branch=\"%s\">" % (module.name, branch_name)
# DOC domains
if catname in stats['doc']['categs'] and stats['doc']['categs'][catname]['modules']:
for docmod, data in stats['doc']['categs'][catname]['modules'].items():
if docmod == module:
content += get_domain_stats(data.values()[0], "document")
content += get_domain_stats(list(data.values())[0], "document")
# UI stats
content += get_domain_stats(domains, "domain")
content += "</module>"
......
......@@ -70,8 +70,8 @@ class RegistrationForm(forms.Form):
if openid:
from django_openid_auth.models import UserOpenID
user_oid = UserOpenID.objects.create(user=new_user, claimed_id=openid)
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
activation_key = hashlib.sha1(salt+force_bytes(username)).hexdigest()
salt = hashlib.sha1(force_bytes(random.random())).hexdigest()[:5]
activation_key = hashlib.sha1(force_bytes(salt + username)).hexdigest()
new_user.activation_key = activation_key
new_user.is_active = False
new_user.save()
......@@ -132,7 +132,7 @@ def get_image_size(url):
if p.image:
size = p.image.size
break
except Exception, e:
except Exception as e:
raise forms.ValidationError(u"Sorry, an error occurred while trying to get image size (%s)" % str(e))
file.close()
if not size:
......
......@@ -32,7 +32,7 @@ def people_image(person):
'<img src="https://secure.gravatar.com/avatar/{hash}.jpg?{qs}" alt="gravatar icon">',
hash=digest,
# Size, default image, rating
qs=urlencode({'s': '80', 'd': 'identicon', 'r': 'g'}),
qs=urlencode([('s', '80'), ('d', 'identicon'), ('r', 'g')]),
)
elif person.image:
tag = format_html(
......
......@@ -152,7 +152,7 @@ class PeopleTestCase(TestCase):
pn.use_gravatar = True
self.assertHTMLEqual(
people.people_image(pn),
'<img alt="gravatar icon" src="https://secure.gravatar.com/avatar/618b8b6c1c973c780ec218242c49cbe7.jpg?s=80&r=g&d=identicon" />'
'<img alt="gravatar icon" src="https://secure.gravatar.com/avatar/618b8b6c1c973c780ec218242c49cbe7.jpg?s=80&d=identicon&r=g" />'
)
@skipUnless(pyicu_present, "PyICU package is required for this test")
......
......@@ -2,9 +2,9 @@
import os
import re
from xml.etree.ElementTree import parse
from urllib import unquote
from django.utils.encoding import force_text
from django.utils.six.moves.urllib.parse import unquote
from people.models import Person
......
......@@ -29,14 +29,15 @@ import threading
from datetime import datetime
from functools import total_ordering
from time import sleep
from urllib2 import URLError
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.core.validators import RegexValidator
from django.utils.encoding import python_2_unicode_compatible
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.six.moves.urllib.error import URLError
from django.utils.six.moves.urllib import request
from django.utils.translation import ungettext, ugettext as _, ugettext_noop
from django.utils import dateformat
from django.db import models
......@@ -406,21 +407,10 @@ class Branch(models.Model):
stats[domain].append(FakeLangStatistics(stats[domain][0], lang))
# Sort
for key, doms in stats.items():
doms.sort(self.compare_stats)
#Sort stats, pot first, then translated (desc), then language name
doms.sort(key=lambda st:(int(st.language_id is not None), -st.translated(), st.get_lang()))
return stats
def compare_stats(self, a, b):
""" Sort stats, pot first, then translated (desc), then language name """
if not a.language:
return -1
elif not b.language:
return 1
else:
res = -cmp(a.translated(), b.translated())
if not res:
res = cmp(a.get_lang(), b.get_lang())
return res
def get_doc_stats(self, mandatory_langs=[]):
if not self._doc_stats:
self._doc_stats = self.get_stats('doc', mandatory_langs)
......@@ -718,7 +708,7 @@ class Branch(models.Model):
stat.update_stats(dest_path)
else:
self.update_stats(force=False, checkout=False, domain=domain)
return commit_hash
return force_text(commit_hash)
def cherrypick_commit(self, commit_hash, domain):
"""
......@@ -845,14 +835,13 @@ class Domain(models.Model):
pot_command.extend(['--msgid-bugs-address', self.module.get_bugs_enter_url()])
elif self.pot_method.startswith(('http://', 'https://')):
# Get POT from URL and save file locally
import urllib2
req = urllib2.Request(self.pot_method)
req = request.Request(self.pot_method)
try:
handle = urllib2.urlopen(req)
handle = request.urlopen(req)
except URLError:
return "", (("error", ugettext_noop("Error retrieving pot file from URL.")),)
potfile = os.path.join(vcs_path, self.potbase() + ".pot")
with open(potfile, 'w') as f:
with open(potfile, 'wb') as f:
f.write(handle.read())
pot_command = ':' # noop
elif self.module.name == 'damned-lies':
......@@ -882,7 +871,7 @@ class Domain(models.Model):
return "", (("error", ugettext_noop("Error regenerating POT file for %(file)s:\n<pre>%(cmd)s\n%(output)s</pre>")
% {'file': self.potbase(),
'cmd': ' '.join(pot_command) if isinstance(pot_command, list) else pot_command,
'output': errs.decode('utf-8')}),
'output': force_text(errs)}),
)
else:
return potfile, ()
......@@ -985,7 +974,7 @@ class Release(models.Model):
def perc(x, y):
return int(x / y * 100)
for k in stats.keys():
stats[k]['stats'] = map(perc, stats[k]['stats'], totals)
stats[k]['stats'] = list(map(perc, stats[k]['stats'], totals))
stats[k]['diff'] = stats[k]['stats'][-1] - stats[k]['stats'][0]
return stats
......@@ -1137,18 +1126,11 @@ class Release(models.Model):
stats[locale]['ui_part']['fuzzy_perc'] = int(100 * row['fuzzy_p'] /total_uistrings_part[locale])
stats[locale]['ui_part']['untranslated_perc'] = int(100 * stats[locale]['ui_part']['untranslated'] / total_uistrings_part[locale])
results = stats.values()
results.sort(self.compare_stats)
results = list(stats.values())
# Sort by most translated first
results.sort(key=lambda st: (-st['ui']['translated'], -st['doc']['translated'], st['lang_name']))
return results
def compare_stats(self, a, b):
res = cmp(b['ui']['translated'], a['ui']['translated'])
if not res:
res = cmp(b['doc']['translated'], a['doc']['translated'])
if not res:
res = cmp(b['lang_name'], a['lang_name'])
return res
def get_lang_stats(self, lang):
""" Get statistics for a specific language, producing the stats data structure
Used for displaying the language-release template """
......@@ -1348,7 +1330,7 @@ class Statistics(models.Model):
return False
def is_pot_stats(self):
return self.language is None
return self.language_id is None
def tr_percentage(self, scope='full'):
if scope == 'full' and self.full_po:
......@@ -1718,17 +1700,17 @@ class Statistics(models.Model):
stats['categs'][categ_key]['catuntrans'] += stat.untranslated(scope)
if module not in stats['categs'][categ_key]['modules']:
# first element is a placeholder for a fake stat
stats['categs'][categ_key]['modules'][module] = {branchname:[[' fake', None], (domname, stat)]}
stats['categs'][categ_key]['modules'][module] = {branchname:[[' fake', None], [domname, stat]]}
elif branchname not in stats['categs'][categ_key]['modules'][module]:
# first element is a placeholder for a fake stat
stats['categs'][categ_key]['modules'][module][branchname] = [[' fake', None], (domname, stat)]
stats['categs'][categ_key]['modules'][module][branchname] = [[' fake', None], [domname, stat]]
else:
# Here we add the 2nd or more stat to the same module-branch
if len(stats['categs'][categ_key]['modules'][module][branchname]) == 2:
# Create a fake statistics object for module summary
stats['categs'][categ_key]['modules'][module][branchname][0][1] = FakeSummaryStatistics(stat.domain.module, stat.branch, dtype)
stats['categs'][categ_key]['modules'][module][branchname][0][1].trans(stats['categs'][categ_key]['modules'][module][branchname][1][1])
stats['categs'][categ_key]['modules'][module][branchname].append((domname, stat))
stats['categs'][categ_key]['modules'][module][branchname].append([domname, stat])
stats['categs'][categ_key]['modules'][module][branchname][0][1].trans(stat)
# Compute percentages and sorting
......
......@@ -32,11 +32,12 @@ from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.test import TestCase
from stats.models import Module, Domain, Branch, Release, Statistics, FakeLangStatistics
from stats.models import (
Module, Domain, Branch, Release, CategoryName, Statistics, FakeLangStatistics,
)
from stats import utils
from languages.models import Language
from fixture_factory import *
def test_scratchdir(test_func):
""" Decorator to temporarily use the scratchdir inside the test directory """
......@@ -119,7 +120,7 @@ class ModuleTestCase(TestCase):
self.assertContains(response, '<li><a href="/module/zenity/">zenity</a></li>')
# Also test JSON output
response = self.client.get(reverse('modules', args=['json']))
content = json.loads(response.content)
content = json.loads(response.content.decode('utf-8'))
self.assertEqual(len(content), Module.objects.count())
self.assertEqual(content[-1]["fields"]["vcs_root"], 'git://git.gnome.org/zenity')
......@@ -281,7 +282,7 @@ class ModuleTestCase(TestCase):
self.assertContains(response, "Language-Team: Tamil <ta@li.org>")
try:
response = self.client.get('/module/po/gnome-hello/po/master/ta-reduced.po')
except OSError, e:
except OSError as e:
if "msginit" in str(e):
self.fail(
"You are probably running an unpatched version of the translate-toolkit. "
......@@ -289,7 +290,7 @@ class ModuleTestCase(TestCase):
)
return
else:
raise e
raise
self.assertContains(response, """# Tamil translation for gnome-hello.""")
@test_scratchdir
......@@ -457,7 +458,7 @@ class StatisticsTests(TestCase):
def test_total_by_releases(self):
releases = list(Release.objects.filter(name__in=('gnome-3-8', 'gnome-dev')))
stats = Release.total_by_releases('ui', releases)
self.assertEqual(list(stats.keys()), ['fr', 'it'])
self.assertEqual(set(stats.keys()), {'fr', 'it'})
self.assertEqual(stats['fr']['diff'], 0)
self.assertEqual(stats['fr']['stats'], [100, 100])
self.assertEqual(stats['it']['diff'], 24)
......@@ -569,7 +570,7 @@ class OtherTests(TestCase):
self.assertContains(response, item)
# Also test JSON output
response = self.client.get(reverse('releases', args=['json']))
content = json.loads(response.content)
content = json.loads(response.content.decode('utf-8'))
self.assertEqual(content[0]["fields"]["name"], "gnome-3-18")
def test_bugzilla_linkify(self):
......
......@@ -38,9 +38,10 @@ from django.core.mail import send_mail
from django.template import Context
from django.template.loader import get_template
from django.utils import six
from django.utils.encoding import force_bytes, force_text
from django.utils.translation import ugettext_noop
import potdiff
from . import potdiff
STATUS_OK = 0
......@@ -139,8 +140,8 @@ def run_shell_command(cmd, input_data=None, raise_on_error=False, **popen_kwargs
pipe = Popen(cmd, shell=shell, stdin=stdin, stdout=PIPE, stderr=PIPE, **popen_kwargs)
if input_data:
try:
pipe.stdin.write(input_data)
except IOError, e:
pipe.stdin.write(force_bytes(input_data))
except IOError as e:
if e.errno != errno.EPIPE:
raise
(output, errout) = pipe.communicate()
......@@ -332,7 +333,7 @@ def po_file_stats(pofile, msgfmt_checks=True):
}
c_env = {"LC_ALL": "C", "LANG": "C", "LANGUAGE": "C"}
if isinstance(pofile, basestring):
if isinstance(pofile, six.string_types):
# pofile is a filesystem path
filename = os.path.basename(pofile)
if not os.access(pofile, os.R_OK):
......@@ -372,6 +373,7 @@ def po_file_stats(pofile, msgfmt_checks=True):
res['errors'].append(("warn", ugettext_noop("This PO file has an executable bit set.")))
# msgfmt output stats on stderr
errs = force_text(errs)
r_tr = re.search(r"([0-9]+) translated", errs)
r_un = re.search(r"([0-9]+) untranslated", errs)
r_fz = re.search(r"([0-9]+) fuzzy", errs)
......@@ -471,7 +473,7 @@ def get_fig_stats(pofile, doc_format):
if status != STATUS_OK:
# FIXME: something should be logged here
return []
lines = output.split('\n')
lines = output.decode('utf-8').split('\n')
while lines[0][0] != "#":
lines = lines[1:] # skip warning messages at the top of the output
......@@ -538,7 +540,7 @@ def copy_file(file1, file2):
def compute_md5(full_path):
m = hashlib.md5()
block_size=2**13
with open(full_path) as fh:
with open(full_path, 'rb') as fh:
while True:
data = fh.read(block_size)
if not data:
......@@ -554,7 +556,7 @@ def notify_list(branch, diff):
text = template.render(Context({
'module' : '%s.%s' % (branch.module.name, branch.name),
'ourweb' : current_site.domain,
'potdiff' : "\n ".join(diff).decode('utf-8'),
'potdiff' : force_text("\n ".join(diff)),
'commit_log': branch.get_vcs_web_log_url(),
}))
send_mail(subject="String additions to '%s.%s'" % (branch.module.name, branch.name),
......
......@@ -141,7 +141,7 @@ def module_edit_branches(request, module_name):
messages.success(request, "The new branch %s has been added" % branch_name)
updated = True
# Send message to gnome-i18n?
except Exception, e:
except Exception as e:
messages.error(request, "Error adding the branch '%s': %s" % (branch_name, str(e)))
branch = None
else:
......@@ -222,7 +222,7 @@ def dynamic_po(request, module_name, domain, branch_name, filename):
raise Http404
potfile = get_object_or_404(Statistics, branch=branch, domain=domain, language=None)
file_path = potfile.po_path(reduced=reduced).encode('ascii')
file_path = potfile.po_path(reduced=reduced)
if not os.access(file_path, os.R_OK):
raise Http404
......
......@@ -220,7 +220,7 @@ class JSONTeamsTest(TeamsAndRolesTests):
"coordinators": []
}
]"""
self.assertJSONEqual(response.content, expected_JSON)
self.assertJSONEqual(response.content.decode('utf-8'), expected_JSON)
class RoleTest(TeamsAndRolesTests):
......
......@@ -18,6 +18,7 @@
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import os
from xml.dom.minidom import parseString
from django.conf import settings
from django.core.files.base import File, ContentFile
......@@ -548,16 +549,16 @@ class VertimusTest(TeamsAndRolesTests):
def test_uploaded_file_validation(self):
# Test a non valid po file
post_content = QueryDict('action=WC&comment=Test1')
post_file = MultiValueDict({'file': [SimpleUploadedFile('filename.po', 'Not valid po file content')]})
post_file = MultiValueDict({'file': [SimpleUploadedFile('filename.po', b'Not valid po file content')]})
form = ActionForm(None, [ActionWC()], True, post_content, post_file)
self.assertTrue('file' in form.errors)
# Test a valid po file
f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "valid_po.po"), 'r')
post_file = MultiValueDict({'file': [File(f)]})
form = ActionForm(None, [ActionWC()], True, post_content, post_file)
self.assertTrue(form.is_valid())
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "valid_po.po"), 'rb') as fh:
post_file = MultiValueDict({'file': [File(fh)]})
form = ActionForm(None, [ActionWC()], True, post_content, post_file)
self.assertTrue(form.is_valid())
# Test form without file
form = ActionForm(None, [ActionWC()], True, post_content)
......@@ -571,11 +572,12 @@ class VertimusTest(TeamsAndRolesTests):
action.apply_on(state, {'send_to_ml': action.send_mail_to_ml, 'comment': "Translating"})
response = self.client.get(reverse('lang_feed', args=[self.l.locale]))
self.assertContains(response,
"""<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">""")
self.assertContains(response,
doc = parseString(response.content)
self.assertEqual(doc.childNodes[0].tagName, 'rss')
self.assertEqual(doc.childNodes[0].getAttribute('xmlns:atom'), "http://www.w3.org/2005/Atom")
self.assertEqual(doc.getElementsByTagName('title')[1].toxml(),
"""<title>po (gedit/User Interface) - gedit (gnome-2-24) - Reserve for translation\n</title>""")
self.assertContains(response,
self.assertEqual(doc.getElementsByTagName('guid')[0].toxml(),
"""<guid>http://example.com/vertimus/gedit/gnome-2-24/po/fr#%d</guid>""" % action.id)
response = self.client.get(reverse('team_feed', args=[self.l.team.name]))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment