[damned-lies] Added loads of Python 3 compatibility fixes
- From: Claude Paroz <claudep src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [damned-lies] Added loads of Python 3 compatibility fixes
- Date: Fri, 13 Nov 2015 15:47:34 +0000 (UTC)
commit 643f1d79fbf0003692b1c2bf16e36524838576e3
Author: Claude Paroz <claude 2xlibre net>
Date: Fri Nov 13 15:51:28 2015 +0100
Added loads of Python 3 compatibility fixes
common/fields.py | 7 ++--
common/templatetags/list_to_columns.py | 6 ++--
common/utils.py | 16 +++++-----
damnedlies/settings.py | 2 +-
languages/views.py | 4 +-
people/forms.py | 6 ++--
people/templatetags/people.py | 2 +-
people/tests.py | 2 +-
stats/doap.py | 2 +-
stats/models.py | 54 ++++++++++---------------------
stats/tests/tests.py | 15 +++++----
stats/utils.py | 16 +++++----
stats/views.py | 4 +-
teams/tests.py | 2 +-
vertimus/tests/tests.py | 20 ++++++-----
15 files changed, 73 insertions(+), 85 deletions(-)
---
diff --git a/common/fields.py b/common/fields.py
index 80ba9bf..25077ee 100644
--- a/common/fields.py
+++ b/common/fields.py
@@ -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
diff --git a/common/templatetags/list_to_columns.py b/common/templatetags/list_to_columns.py
index 0c8f70b..75b1c9e 100644
--- a/common/templatetags/list_to_columns.py
+++ b/common/templatetags/list_to_columns.py
@@ -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)
diff --git a/common/utils.py b/common/utils.py
index 6e65bad..2a5ebc2 100644
--- a/common/utils.py
+++ b/common/utils.py
@@ -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()]
diff --git a/damnedlies/settings.py b/damnedlies/settings.py
index 73c6e95..f9a8f60 100644
--- a/damnedlies/settings.py
+++ b/damnedlies/settings.py
@@ -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/'
diff --git a/languages/views.py b/languages/views.py
index 971ccb0..a46a89e 100644
--- a/languages/views.py
+++ b/languages/views.py
@@ -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>"
diff --git a/people/forms.py b/people/forms.py
index 5ca6f8e..e95e481 100644
--- a/people/forms.py
+++ b/people/forms.py
@@ -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:
diff --git a/people/templatetags/people.py b/people/templatetags/people.py
index fac0ada..da3a5b8 100644
--- a/people/templatetags/people.py
+++ b/people/templatetags/people.py
@@ -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(
diff --git a/people/tests.py b/people/tests.py
index 71e8aa5..1de6d98 100644
--- a/people/tests.py
+++ b/people/tests.py
@@ -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")
diff --git a/stats/doap.py b/stats/doap.py
index cd24b03..00fcfbc 100644
--- a/stats/doap.py
+++ b/stats/doap.py
@@ -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
diff --git a/stats/models.py b/stats/models.py
index d47f60a..cb90c30 100644
--- a/stats/models.py
+++ b/stats/models.py
@@ -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
diff --git a/stats/tests/tests.py b/stats/tests/tests.py
index 0d0add2..06b9b84 100644
--- a/stats/tests/tests.py
+++ b/stats/tests/tests.py
@@ -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):
diff --git a/stats/utils.py b/stats/utils.py
index 2a25093..4a284a3 100644
--- a/stats/utils.py
+++ b/stats/utils.py
@@ -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),
diff --git a/stats/views.py b/stats/views.py
index 04472b0..b62cdff 100644
--- a/stats/views.py
+++ b/stats/views.py
@@ -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
diff --git a/teams/tests.py b/teams/tests.py
index 79fbed7..57de6fb 100644
--- a/teams/tests.py
+++ b/teams/tests.py
@@ -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):
diff --git a/vertimus/tests/tests.py b/vertimus/tests/tests.py
index bb5e9e5..9ddd0e0 100644
--- a/vertimus/tests/tests.py
+++ b/vertimus/tests/tests.py
@@ -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]))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]