diff --git a/.pep8 b/.pep8 index 4ad0546..d8b79ea 100644 --- a/.pep8 +++ b/.pep8 @@ -1,3 +1,3 @@ [flake8] -ignore = E501 +ignore = E501,W503 exclude = south_migrations,migrations,.venv_*,docs diff --git a/.travis.yml b/.travis.yml index b357bb1..091bf66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,6 @@ services: memcached matrix: include: - - python: "2.7" - env: TOX_ENV=py27-django111 - - python: "3.6" - env: TOX_ENV=py36-django111 - python: "3.6" env: TOX_ENV=py36-django20 - python: "3.6" diff --git a/CHANGES b/CHANGES index efa42ed..f3bf884 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,8 @@ Version 0.9.5 (unreleased) * Added Kyrgyz translation (#239,thanks @Soyuzbek) * Ignore translator context hints checking unmatched variables (#238, #239, thanks @jeancochrane and @mondeja) * Uncheck fuzzy on translation keyup instead of change (#235 @mondeja) +* Allow passing a function itself to the setting ROSETTA_ACCESS_CONTROL (#227, thanks @alvra) +* Dropped support for Django 1.11 and Python 2 Version 0.9.4 diff --git a/docs/settings.rst b/docs/settings.rst index face5ba..abf055d 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -13,7 +13,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr * ``ROSETTA_REQUIRES_AUTH``: Require authentication for all Rosetta views. Defaults to ``True``. * ``ROSETTA_POFILE_WRAP_WIDTH``: Sets the line-length of the edited PO file. Set this to ``0`` to mimic ``makemessage``'s ``--no-wrap`` option. Defaults to ``78``. * ``ROSETTA_STORAGE_CLASS``: See the note below on Storages. Defaults to ``rosetta.storage.CacheRosettaStorage`` -* ``ROSETTA_ACCESS_CONTROL_FUNCTION``: An alternative function that determines if a given user can access the translation views. This function receives a ``user`` as its argument, and returns a boolean specifying whether the passed user is allowed to use Rosetta or not. +* ``ROSETTA_ACCESS_CONTROL_FUNCTION``: An alternative function (string or a callable) that determines if a given user can access the translation views. This function receives a ``user`` as its argument, and returns a boolean specifying whether the passed user is allowed to use Rosetta or not. * ``ROSETTA_LANGUAGE_GROUPS``: Set to ``True`` to enable language-specific groups, which can be used to give different translators access to different languages. Instead of creating a global ``translators`` group, create individual per-language groups, e.g. ``translators-de``, ``translators-fr``, and assign users to these. * ``ROSETTA_CACHE_NAME``: When using ``rosetta.storage.CacheRosettaStorage``, you can store the Rosetta data in a specific cache. This is particularly useful when your ``default`` cache is a ``django.core.cache.backends.dummy.DummyCache`` (which happens on pre-production environments). If unset, it will default to ``rosetta`` if a cache with this name exists, or ``default`` if not. * ``ROSETTA_POFILENAMES``: Defines which po file names are exposed in the web interface. Defaults to ``('django.po', 'djangojs.po')`` diff --git a/rosetta/tests/tests.py b/rosetta/tests/tests.py index d3c28ab..3fd2189 100644 --- a/rosetta/tests/tests.py +++ b/rosetta/tests/tests.py @@ -1,34 +1,27 @@ -# -*- coding: utf-8 -*- import filecmp import hashlib import os import shutil -import vcr -try: - # Python 3 - from urllib.parse import urlencode -except ImportError: - # Python 2 - from urllib import urlencode +from urllib.parse import urlencode -from django.conf import settings -from django.dispatch import receiver -from django.core.exceptions import ImproperlyConfigured -from django.http import Http404 -from django.test import TestCase, RequestFactory, override_settings -from django.test.client import Client from django import VERSION -from django.urls import reverse, resolve +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.dispatch import receiver +from django.http import Http404 +from django.test import RequestFactory, TestCase, override_settings +from django.test.client import Client +from django.urls import resolve, reverse from django.utils.encoding import force_bytes -import six +import six +import vcr +from rosetta import views from rosetta.signals import entry_changed, post_save from rosetta.storage import get_storage -from rosetta import views class RosettaTestCase(TestCase): - def __init__(self, *args, **kwargs): super(RosettaTestCase, self).__init__(*args, **kwargs) self.curdir = os.path.dirname(__file__) @@ -39,9 +32,15 @@ class RosettaTestCase(TestCase): def setUp(self): from django.contrib.auth.models import User - user = User.objects.create_superuser('test_admin', 'test@test.com', 'test_password') - user2 = User.objects.create_superuser('test_admin2', 'test@test2.com', 'test_password') - user3 = User.objects.create_superuser('test_admin3', 'test@test2.com', 'test_password') + user = User.objects.create_superuser( + 'test_admin', 'test@test.com', 'test_password' + ) + user2 = User.objects.create_superuser( + 'test_admin2', 'test@test2.com', 'test_password' + ) + user3 = User.objects.create_superuser( + 'test_admin3', 'test@test2.com', 'test_password' + ) user3.is_staff = False user3.save() @@ -84,15 +83,14 @@ class RosettaTestCase(TestCase): def test_1_ListLoading(self): r = self.client.get(self.third_party_file_list_url) self.assertTrue( - os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content) + os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') + in r.content.decode() ) - @override_settings(ROSETTA_LANGUAGES=( - ('xx', 'dummy language'), - )) + @override_settings(ROSETTA_LANGUAGES=(('xx', 'dummy language'),)) def test_2_PickFile(self): r = self.client.get(self.xx_form_url) - self.assertTrue('dummy language' in str(r.content)) + self.assertTrue('dummy language' in r.content.decode()) def test_3_DownloadZIP(self): kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 0} @@ -101,9 +99,7 @@ class RosettaTestCase(TestCase): self.assertTrue('content-type' in r._headers.keys()) self.assertTrue('application/x-zip' in r._headers.get('content-type')) - @override_settings(ROSETTA_LANGUAGES=( - ('xx', 'dummy language'), - )) + @override_settings(ROSETTA_LANGUAGES=(('xx', 'dummy language'),)) def test_4_DoChanges(self): self.copy_po_file_from_template('./django.po.template') untranslated_url = self.xx_form_url + '?msg_filter=untranslated' @@ -113,10 +109,10 @@ class RosettaTestCase(TestCase): r = self.client.get(untranslated_url) # make sure both strings are untranslated - self.assertTrue('dummy language' in str(r.content)) - self.assertTrue('String 1' in str(r.content)) - self.assertTrue('String 2' in str(r.content)) - self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content)) + self.assertTrue('dummy language' in r.content.decode()) + self.assertTrue('String 1' in r.content.decode()) + self.assertTrue('String 2' in r.content.decode()) + self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content.decode()) # post a translation data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'} @@ -126,20 +122,18 @@ class RosettaTestCase(TestCase): r = self.client.get(untranslated_url) # the translated string no longer is up for translation - self.assertTrue('String 1' in str(r.content)) - self.assertTrue('String 2' not in str(r.content)) + self.assertTrue('String 1' in r.content.decode()) + self.assertTrue('String 2' not in r.content.decode()) # display only translated strings r = self.client.get(translated_url) # The translation was persisted - self.assertTrue('String 1' not in str(r.content)) - self.assertTrue('String 2' in str(r.content)) - self.assertTrue('Hello, world' in str(r.content)) + self.assertTrue('String 1' not in r.content.decode()) + self.assertTrue('String 2' in r.content.decode()) + self.assertTrue('Hello, world' in r.content.decode()) - @override_settings(ROSETTA_LANGUAGES=( - ('xx', 'dummy language'), - )) + @override_settings(ROSETTA_LANGUAGES=(('xx', 'dummy language'),)) def test_5_TestIssue67(self): # issue 67: http://code.google.com/p/django-rosetta/issues/detail?id=67 self.copy_po_file_from_template('./django.po.issue67.template') @@ -149,15 +143,15 @@ class RosettaTestCase(TestCase): content = f_.read() self.assertTrue('Hello, world' not in six.text_type(content)) self.assertTrue('|| n%100>=20) ? 1 : 2)' in six.text_type(content)) - del(content) + del content r = self.client.get(self.xx_form_url + '?msg_filter=untranslated') # make sure all strings are untranslated - self.assertTrue('dummy language' in str(r.content)) - self.assertTrue('String 1' in str(r.content)) - self.assertTrue('String 2' in str(r.content)) - self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content)) + self.assertTrue('dummy language' in r.content.decode()) + self.assertTrue('String 1' in r.content.decode()) + self.assertTrue('String 2' in r.content.decode()) + self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content.decode()) # post a translation data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'} @@ -169,13 +163,11 @@ class RosettaTestCase(TestCase): self.assertTrue('Hello, world' in str(content)) self.assertTrue('|| n%100>=20) ? 1 : 2)' in str(content)) self.assertTrue('or n%100>=20) ? 1 : 2)' not in str(content)) - del(content) + del content - @override_settings(ROSETTA_LANGUAGES=( - ('xx', 'dummy language'), - )) + @override_settings(ROSETTA_LANGUAGES=(('xx', 'dummy language'),)) def test_6_ExcludedApps(self): - with self.settings(ROSETTA_EXCLUDED_APPLICATIONS=('rosetta', )): + with self.settings(ROSETTA_EXCLUDED_APPLICATIONS=('rosetta',)): r = self.client.get(self.third_party_file_list_url) self.assertNotContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po') @@ -190,17 +182,15 @@ class RosettaTestCase(TestCase): r = self.client.get(self.project_file_list_url) self.assertNotContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po') - @override_settings(ROSETTA_LANGUAGES=( - ('xx', 'dummy language'), - )) + @override_settings(ROSETTA_LANGUAGES=(('xx', 'dummy language'),)) def test_8_hideObsoletes(self): r = self.client.get(self.xx_form_url) # not in listing for p in range(1, 5): r = self.client.get(self.xx_form_url + '?page=%d' % p) - self.assertTrue('dummy language' in str(r.content)) - self.assertTrue('Les deux' not in str(r.content)) + self.assertTrue('dummy language' in r.content.decode()) + self.assertTrue('Les deux' not in r.content.decode()) r = self.client.get(self.xx_form_url + '?query=Les%20Deux') self.assertContains(r, 'dummy language') @@ -278,7 +268,7 @@ class RosettaTestCase(TestCase): def test_11_issue_80_tab_indexes(self): r = self.client.get(self.xx_form_url) - self.assertTrue('tabindex="3"' in str(r.content)) + self.assertTrue('tabindex="3"' in r.content.decode()) def test_12_issue_82_staff_user(self): self.client3 = Client() @@ -297,37 +287,37 @@ class RosettaTestCase(TestCase): self.assertTrue(r.content) self.assertEqual(r.status_code, 200) - @override_settings(ROSETTA_LANGUAGES=(('fr', 'French'), ('xx', 'Dummy Language'),)) + @override_settings(ROSETTA_LANGUAGES=(('fr', 'French'), ('xx', 'Dummy Language'))) def test_13_catalog_filters(self): r = self.client.get(self.third_party_file_list_url) self.assertTrue( - os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content) + os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') + in r.content.decode() ) - self.assertTrue(('contrib') not in str(r.content)) url = reverse('rosetta-file-list', kwargs={'po_filter': 'django'}) r = self.client.get(url) self.assertTrue( - os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in str(r.content) + os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') + not in r.content.decode() ) - self.assertTrue(('contrib') in str(r.content)) r = self.client.get(self.all_file_list_url) self.assertTrue( - os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content) + os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') + in r.content.decode() ) - self.assertTrue(('contrib') in str(r.content)) r = self.client.get(self.project_file_list_url) self.assertTrue( - os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') not in str(r.content) + os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') + not in r.content.decode() ) - self.assertTrue(('contrib') not in str(r.content)) def test_14_issue_99_context_and_comments(self): r = self.client.get(self.xx_form_url) - self.assertTrue('This is a text of the base template' in str(r.content)) - self.assertTrue('Context hint' in str(r.content)) + self.assertTrue('This is a text of the base template' in r.content.decode()) + self.assertTrue('Context hint' in r.content.decode()) def test_15_issue_87_entry_changed_signal(self): self.copy_po_file_from_template('./django.po.template') @@ -338,7 +328,8 @@ class RosettaTestCase(TestCase): self.test_old_msgstr = kwargs.get('old_msgstr') self.test_new_msgstr = sender.msgstr self.test_msg_id = sender.msgid - self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content)) + + self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content.decode()) # post a translation data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'} @@ -348,7 +339,7 @@ class RosettaTestCase(TestCase): self.assertTrue(self.test_new_msgstr == 'Hello, world') self.assertTrue(self.test_msg_id == 'String 2') - del(self.test_old_msgstr, self.test_new_msgstr, self.test_msg_id) + del (self.test_old_msgstr, self.test_new_msgstr, self.test_msg_id) def test_16_issue_101_post_save_signal(self): self.copy_po_file_from_template('./django.po.template') @@ -358,14 +349,14 @@ class RosettaTestCase(TestCase): def test_receiver(sender, **kwargs): self.test_sig_lang = kwargs.get('language_code') - self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content)) + self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content.decode()) # post a translation data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'} self.client.post(self.xx_form_url, data) self.assertTrue(self.test_sig_lang == 'xx') - del(self.test_sig_lang) + del self.test_sig_lang def test_17_issue_103_post_save_signal_has_request(self): self.copy_po_file_from_template('./django.po.template') @@ -375,31 +366,32 @@ class RosettaTestCase(TestCase): def test_receiver(sender, **kwargs): self.test_16_has_request = 'request' in kwargs - self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content)) + self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content.decode()) # post a translation data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'} r = self.client.post(self.xx_form_url, data) self.assertTrue(self.test_16_has_request) - del(self.test_16_has_request) + del self.test_16_has_request def test_18_Test_Issue_gh24(self): self.copy_po_file_from_template('./django.po.issue24gh.template') r = self.client.get(self.xx_form_url) - self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in str(r.content)) + self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in r.content.decode()) # Post a translation, it should have properly wrapped lines - data = {'m_bb9d8fe6159187b9ea494c1b313d23d4': - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean ' - 'commodo ligula eget dolor. Aenean massa. Cum sociis natoque ' - 'penatibus et magnis dis parturient montes, nascetur ridiculus ' - 'mus. Donec quam felis, ultricies nec, pellentesque eu, pretium ' - 'quis, sem. Nulla consequat massa quis enim. Donec pede justo, ' - 'fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, ' - 'rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum ' - 'felis eu pede mollis pretium.'} + data = { + 'm_bb9d8fe6159187b9ea494c1b313d23d4': 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean ' + 'commodo ligula eget dolor. Aenean massa. Cum sociis natoque ' + 'penatibus et magnis dis parturient montes, nascetur ridiculus ' + 'mus. Donec quam felis, ultricies nec, pellentesque eu, pretium ' + 'quis, sem. Nulla consequat massa quis enim. Donec pede justo, ' + 'fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, ' + 'rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum ' + 'felis eu pede mollis pretium.' + } r = self.client.post(self.xx_form_url, data) with open(self.dest_file, 'r') as po_file: pofile_content = po_file.read() @@ -410,7 +402,7 @@ class RosettaTestCase(TestCase): self.copy_po_file_from_template('./django.po.issue24gh.template') with self.settings(ROSETTA_POFILE_WRAP_WIDTH=0): r = self.client.get(self.xx_form_url) - self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in str(r.content)) + self.assertTrue('m_bb9d8fe6159187b9ea494c1b313d23d4' in r.content.decode()) r = self.client.post(self.xx_form_url, data) with open(self.dest_file, 'r') as po_file: pofile_content = po_file.read() @@ -419,9 +411,9 @@ class RosettaTestCase(TestCase): def test_19_Test_Issue_gh34(self): self.copy_po_file_from_template('./django.po.issue34gh.template') r = self.client.get(self.xx_form_url) - self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_1' in str(r.content)) - self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_0' in str(r.content)) - self.assertTrue('m_09f7e02f1290be211da707a266f153b3' in str(r.content)) + self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_1' in r.content.decode()) + self.assertTrue('m_ff7060c1a9aae9c42af4d54ac8551f67_0' in r.content.decode()) + self.assertTrue('m_09f7e02f1290be211da707a266f153b3' in r.content.decode()) # post a translation, it should have properly wrapped lines data = { @@ -443,15 +435,16 @@ class RosettaTestCase(TestCase): def test_20_Test_Issue_gh38(self): # (Have to log in again, since our session engine changed) self.client.login(username='test_admin', password='test_password') - self.assertTrue('django.contrib.sessions.middleware.SessionMiddleware' - in settings.MIDDLEWARE) + self.assertTrue( + 'django.contrib.sessions.middleware.SessionMiddleware' in settings.MIDDLEWARE + ) # Only one backend to test: cache backend self.copy_po_file_from_template('./django.po.issue38gh.template') r = self.client.get(self.xx_form_url) self.assertFalse(len(str(self.client.cookies.get('sessionid'))) > 4096) - self.assertTrue('m_9efd113f7919952523f06e0d88da9c54' in str(r.content)) + self.assertTrue('m_9efd113f7919952523f06e0d88da9c54' in r.content.decode()) data = {'m_9efd113f7919952523f06e0d88da9c54': 'Testing cookie length'} r = self.client.post(self.xx_form_url, data) @@ -460,8 +453,8 @@ class RosettaTestCase(TestCase): self.assertTrue('Testing cookie length' in pofile_content) r = self.client.get(self.xx_form_url + '?filter=translated') - self.assertTrue('Testing cookie length' in str(r.content)) - self.assertTrue('m_9f6c442c6d579707440ba9dada0fb373' in str(r.content)) + self.assertTrue('Testing cookie length' in r.content.decode()) + self.assertTrue('m_9f6c442c6d579707440ba9dada0fb373' in r.content.decode()) @override_settings(ROSETTA_STORAGE_CLASS='rosetta.storage.CacheRosettaStorage') def test_21_concurrency_of_cache_backend(self): @@ -474,7 +467,7 @@ class RosettaTestCase(TestCase): self.client2.get(self.xx_form_url) self.assertNotEqual( self.client.session.get('rosetta_cache_storage_key_prefix'), - self.client2.session.get('rosetta_cache_storage_key_prefix') + self.client2.session.get('rosetta_cache_storage_key_prefix'), ) # Clean up (restore perms) @@ -485,12 +478,10 @@ class RosettaTestCase(TestCase): r = self.client.get(self.xx_form_url) # We have distinct hashes, even though the msgid and msgstr are identical - self.assertTrue('m_4765f7de94996d3de5975fa797c3451f' in str(r.content)) - self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in str(r.content)) + self.assertTrue('m_4765f7de94996d3de5975fa797c3451f' in r.content.decode()) + self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in r.content.decode()) - @override_settings(ROSETTA_LANGUAGES=( - ('xx', 'dummy language'), - )) + @override_settings(ROSETTA_LANGUAGES=(('xx', 'dummy language'),)) def test_23_save_header_data(self): from django.contrib.auth.models import User @@ -510,10 +501,10 @@ class RosettaTestCase(TestCase): r = self.client.get(self.xx_form_url + '?filter=untranslated') # make sure both strings are untranslated - self.assertTrue('dummy language' in str(r.content)) - self.assertTrue('String 1' in str(r.content)) - self.assertTrue('String 2' in str(r.content)) - self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in str(r.content)) + self.assertTrue('dummy language' in r.content.decode()) + self.assertTrue('String 1' in r.content.decode()) + self.assertTrue('String 2' in r.content.decode()) + self.assertTrue('m_e48f149a8b2e8baa81b816c0edf93890' in r.content.decode()) # post a translation data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'} @@ -533,10 +524,10 @@ class RosettaTestCase(TestCase): # Load the template file r = self.client.get(self.xx_form_url) - self.assertTrue('Progress: 0%' in str(r.content)) + self.assertTrue('Progress: 0%' in r.content.decode()) data = {'m_e48f149a8b2e8baa81b816c0edf93890': 'Hello, world'} r = self.client.post(self.xx_form_url, data, follow=True) - self.assertTrue('Progress: 25%' in str(r.content)) + self.assertTrue('Progress: 25%' in r.content.decode()) def test_25_replace_access_control(self): # Test default access control allows access @@ -562,17 +553,18 @@ class RosettaTestCase(TestCase): def test_27_extended_urlconf_language_code_loads_file(self): url = reverse( - 'rosetta-form', - kwargs={'po_filter': 'all', 'lang_id': 'fr_FR.utf8', 'idx': 0} + 'rosetta-form', kwargs={'po_filter': 'all', 'lang_id': 'fr_FR.utf8', 'idx': 0} ) r = self.client.get(url) - self.assertTrue('French (France), UTF8' in str(r.content)) - self.assertTrue('m_03a603523bd75b00414a413657acdeb2' in str(r.content)) + self.assertTrue('French (France), UTF8' in r.content.decode()) + self.assertTrue('m_03a603523bd75b00414a413657acdeb2' in r.content.decode()) def test_28_issue_gh87(self): """Make sure that rosetta_i18n_catalog_filter is passed into the context.""" r = self.client.get(self.third_party_file_list_url) - self.assertContains(r, '
  • ') + self.assertContains( + r, '
  • ' + ) @override_settings( SESSION_ENGINE='django.contrib.sessions.backends.signed_cookies', @@ -580,8 +572,10 @@ class RosettaTestCase(TestCase): ) def test_29_unsupported_p3_django_16_storage(self): if VERSION[0:2] < (2, 0): - self.assertTrue('django.contrib.sessions.middleware.SessionMiddleware' - in settings.MIDDLEWARE) + self.assertTrue( + 'django.contrib.sessions.middleware.SessionMiddleware' + in settings.MIDDLEWARE + ) # Force caching to be used by making the pofile read-only os.chmod(self.dest_file, 292) # 0444 @@ -596,8 +590,7 @@ class RosettaTestCase(TestCase): os.chmod(self.dest_file, 420) # 0644 @override_settings( - ROSETTA_POFILENAMES=('pr44.po', ), - ROSETTA_LANGUAGES=(('xx', 'dummy language'),) + ROSETTA_POFILENAMES=('pr44.po',), ROSETTA_LANGUAGES=(('xx', 'dummy language'),) ) def test_30_pofile_names(self): os.unlink(self.dest_file) @@ -605,15 +598,14 @@ class RosettaTestCase(TestCase): os.path.join(self.curdir, '../locale/xx/LC_MESSAGES/pr44.po') ) shutil.copy( - os.path.normpath(os.path.join(self.curdir, './pr44.po.template')), - destfile + os.path.normpath(os.path.join(self.curdir, './pr44.po.template')), destfile ) r = self.client.get(self.third_party_file_list_url) - self.assertTrue('xx/LC_MESSAGES/pr44.po' in str(r.content)) + self.assertTrue('xx/LC_MESSAGES/pr44.po' in r.content.decode()) r = self.client.get(self.xx_form_url) - self.assertTrue('dummy language' in str(r.content)) + self.assertTrue('dummy language' in r.content.decode()) # (Clean up) os.unlink(destfile) @@ -654,32 +646,35 @@ class RosettaTestCase(TestCase): self.assertContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po') @override_settings( - ROSETTA_ENABLE_REFLANG=True, - ROSETTA_LANGUAGES=(('xx', 'dummy language'),) + ROSETTA_ENABLE_REFLANG=True, ROSETTA_LANGUAGES=(('xx', 'dummy language'),) ) def test_33_reflang(self): self.copy_po_file_from_template('./django.po.issue60.template') r = self.client.get(self.xx_form_url) # Verify that there's an option to select a reflang - self.assertTrue('' in str(r.content)) + self.assertTrue( + '' in r.content.decode() + ) r = self.client.get(self.xx_form_url + '?ref_lang=xx') # The translated string in the test PO file ends up in the "Reference" column - self.assertTrue('translated-string1' in str(r.content)) + self.assertTrue( + 'translated-string1' in r.content.decode() + ) def test_show_occurrences(self): r = self.client.get(self.xx_form_url) # Verify that occurrences in view - self.assertTrue('' in str(r.content)) + self.assertTrue('' in r.content.decode()) with self.settings(ROSETTA_SHOW_OCCURRENCES=False): r = self.client.get(self.xx_form_url) # Verify that occurrences not in view - self.assertFalse('' in str(r.content)) + self.assertFalse('' in r.content.decode()) def test_34_issue_113_app_configs(self): r = self.client.get(self.all_file_list_url) - self.assertTrue('rosetta/files/all/xx/1/">Test_App' in str(r.content)) + self.assertTrue('rosetta/files/all/xx/1/">Test_App' in r.content.decode()) @override_settings(ROSETTA_STORAGE_CLASS='rosetta.storage.CacheRosettaStorage') def test_35_issue_135_display_exception_messages(self): @@ -733,7 +728,7 @@ class RosettaTestCase(TestCase): def test_39_invalid_get_page(self): url = self.xx_form_url + '?filter=untranslated' - r = self.client.get(url) # Page not specified + r = self.client.get(url) # Page not specified self.assertEqual(r.context['page'], 1) r = self.client.get(url + '&page=') # No number given @@ -746,7 +741,6 @@ class RosettaTestCase(TestCase): self.assertEqual(r.context['page'], 1) def test_40_issue_155_auto_compile(self): - def file_hash(file_string): if six.PY3: with open(file_string, encoding="latin-1") as file: @@ -786,32 +780,50 @@ class RosettaTestCase(TestCase): # Disable auto-compilation of the MO when the PO is saved with self.settings(ROSETTA_AUTO_COMPILE=False): # Make a change to the translations - po_file_hash_before, mo_file_hash_before = po_file_hash_after, mo_file_hash_after + po_file_hash_before, mo_file_hash_before = ( + po_file_hash_after, + mo_file_hash_after, + ) msg_hashes = message_hashes() data = {msg_hashes['String 1']: "Translation 3"} self.client.post(self.xx_form_url, data) - po_file_hash_after, mo_file_hash_after = file_hash(po_file), file_hash(mo_file) + po_file_hash_after, mo_file_hash_after = ( + file_hash(po_file), + file_hash(mo_file), + ) # Only the PO should have changed, the MO should be unchanged self.assertNotEqual(po_file_hash_before, po_file_hash_after) self.assertEqual(mo_file_hash_before, mo_file_hash_after) # Verify that translating another string also leaves the MO unchanged - po_file_hash_before, mo_file_hash_before = po_file_hash_after, mo_file_hash_after + po_file_hash_before, mo_file_hash_before = ( + po_file_hash_after, + mo_file_hash_after, + ) msg_hashes = message_hashes() data = {msg_hashes['String 2']: "Translation 4"} self.client.post(self.xx_form_url, data) - po_file_hash_after, mo_file_hash_after = file_hash(po_file), file_hash(mo_file) + po_file_hash_after, mo_file_hash_after = ( + file_hash(po_file), + file_hash(mo_file), + ) self.assertNotEqual(po_file_hash_before, po_file_hash_after) self.assertEqual(mo_file_hash_before, mo_file_hash_after) with self.settings(ROSETTA_AUTO_COMPILE=True): - po_file_hash_before, mo_file_hash_before = po_file_hash_after, mo_file_hash_after + po_file_hash_before, mo_file_hash_before = ( + po_file_hash_after, + mo_file_hash_after, + ) msg_hashes = message_hashes() data = {msg_hashes['String 2']: "Translation 5"} self.client.post(self.xx_form_url, data) - po_file_hash_after, mo_file_hash_after = file_hash(po_file), file_hash(mo_file) + po_file_hash_after, mo_file_hash_after = ( + file_hash(po_file), + file_hash(mo_file), + ) self.assertNotEqual(po_file_hash_before, po_file_hash_after) self.assertNotEqual(mo_file_hash_before, mo_file_hash_after) @@ -842,9 +854,7 @@ class RosettaTestCase(TestCase): request.user = self.user kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 0} view = self._setup_view( - view=views.TranslationFormView(), - request=request, - **kwargs + view=views.TranslationFormView(), request=request, **kwargs ) self.assertTrue(view.po_file_is_writable) @@ -853,9 +863,7 @@ class RosettaTestCase(TestCase): # make the pofile read-only os.chmod(self.dest_file, 292) # 0444 view = self._setup_view( - view=views.TranslationFormView(), - request=request, - **kwargs + view=views.TranslationFormView(), request=request, **kwargs ) self.assertFalse(view.po_file_is_writable) @@ -873,18 +881,16 @@ class RosettaTestCase(TestCase): request.user = self.user kwargs = {'po_filter': 'third-party', 'lang_id': 'xx', 'idx': 0} view = self._setup_view( - view=views.TranslationFormView(), - request=request, - **kwargs + view=views.TranslationFormView(), request=request, **kwargs ) self.assertEqual(view.po_file_path, self.dest_file) # But if the language isn't an option, we get a 404 - with self.settings(ROSETTA_LANGUAGES=[l for l in settings.LANGUAGES if l[0] != 'xx']): + with self.settings( + ROSETTA_LANGUAGES=[l for l in settings.LANGUAGES if l[0] != 'xx'] + ): view = self._setup_view( - view=views.TranslationFormView(), - request=request, - **kwargs + view=views.TranslationFormView(), request=request, **kwargs ) with self.assertRaises(Http404): view.po_file_path @@ -975,16 +981,26 @@ class RosettaTestCase(TestCase): r = self.client.get(url) self.assertEqual(r.status_code, 200) - @vcr.use_cassette('fixtures/vcr_cassettes/test_47_azure_ajax_translation.yaml', match_on=['method', 'scheme', 'host', 'port', 'path', 'query', 'raw_body'], record_mode='new_episodes') + @vcr.use_cassette( + 'fixtures/vcr_cassettes/test_47_azure_ajax_translation.yaml', + match_on=['method', 'scheme', 'host', 'port', 'path', 'query', 'raw_body'], + record_mode='new_episodes', + ) def test_47_azure_ajax_translation(self): - r = self.client.get(reverse('rosetta.translate_text') + '?from=en&to=fr&text=hello%20world') + r = self.client.get( + reverse('rosetta.translate_text') + '?from=en&to=fr&text=hello%20world' + ) self.assertContains(r, '"Salut tout le monde"') @override_settings(ROSETTA_REQUIRES_AUTH=True) def test_48_requires_auth_not_respected_issue_203(self): self.client.logout() r = self.client.get(self.all_file_list_url) - self.assertRedirects(r, '{}?next=/rosetta/files/all/'.format(settings.LOGIN_URL), fetch_redirect_response=False) + self.assertRedirects( + r, + '{}?next=/rosetta/files/all/'.format(settings.LOGIN_URL), + fetch_redirect_response=False, + ) self.assertEqual(302, r.status_code) @override_settings(ROSETTA_REQUIRES_AUTH=False) @@ -996,22 +1012,26 @@ class RosettaTestCase(TestCase): def test_50_custom_login_url(self): self.client.logout() r = self.client.get(self.all_file_list_url) - self.assertRedirects(r, '/custom-url/?next=/rosetta/files/all/', fetch_redirect_response=False) + self.assertRedirects( + r, '/custom-url/?next=/rosetta/files/all/', fetch_redirect_response=False + ) self.assertEqual(302, r.status_code) def test_51_rosetta_languages(self): self.assertTrue('xx' in dict(settings.LANGUAGES)) self.assertFalse('yy' in dict(settings.LANGUAGES)) - with self.settings(ROSETTA_LANGUAGES=(('xx', 'foo language'), )): + with self.settings(ROSETTA_LANGUAGES=(('xx', 'foo language'),)): r = self.client.get(self.project_file_list_url) - self.assertTrue('foo language' in str(r.content)) - self.assertFalse('bar language' in str(r.content)) + self.assertTrue('foo language' in r.content.decode()) + self.assertFalse('bar language' in r.content.decode()) - with self.settings(ROSETTA_LANGUAGES=(('xx', 'foo language'), ('yy', 'bar language'), )): + with self.settings( + ROSETTA_LANGUAGES=(('xx', 'foo language'), ('yy', 'bar language')) + ): r = self.client.get(self.project_file_list_url) - self.assertTrue('foo language' in str(r.content)) - self.assertTrue('bar language' in str(r.content)) + self.assertTrue('foo language' in r.content.decode()) + self.assertTrue('bar language' in r.content.decode()) # Stubbed access control function diff --git a/rosetta/views.py b/rosetta/views.py index 27ddb6e..c8e6c4b 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -6,6 +6,7 @@ import re import unicodedata import uuid import zipfile +from urllib.parse import urlencode from django.conf import settings from django.contrib import messages @@ -21,7 +22,7 @@ from django.urls import reverse from django.utils.decorators import method_decorator from django.utils.encoding import force_bytes from django.utils.functional import Promise, cached_property -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from django.views.generic import TemplateView, View @@ -37,14 +38,6 @@ from .signals import entry_changed, post_save from .storage import get_storage -try: - # Python 3 - from urllib.parse import urlencode -except ImportError: - # Python 2 - from urllib import urlencode - - def get_app_name(path): return path.split('/locale')[0].split('/')[-1] @@ -53,12 +46,15 @@ class LoginURL(Promise): """ Tests friendly login URL, url is resolved at runtime. """ + def __str__(self): return rosetta_settings.LOGIN_URL @method_decorator(never_cache, 'dispatch') -@method_decorator(user_passes_test(lambda user: can_translate(user), LoginURL()), 'dispatch') +@method_decorator( + user_passes_test(lambda user: can_translate(user), LoginURL()), 'dispatch' +) class RosettaBaseMixin(object): """A mixin class for Rosetta's class-based views. It provides: * security (see class decorators) @@ -91,6 +87,7 @@ class RosettaFileLevelMixin(RosettaBaseMixin): * po_file (pofile object) * po_file_is_writable (bool: do we have filesystem write perms to file) """ + def _request_request(self, key, default=None): if key in self.request.GET: return self.request.GET.get(key) @@ -130,11 +127,12 @@ class RosettaFileLevelMixin(RosettaBaseMixin): django_apps = self.po_filter in ('all', 'django') project_apps = self.po_filter in ('all', 'project') - po_paths = find_pos(self.language_id, - project_apps=project_apps, - django_apps=django_apps, - third_party_apps=third_party_apps, - ) + po_paths = find_pos( + self.language_id, + project_apps=project_apps, + django_apps=django_apps, + third_party_apps=third_party_apps, + ) po_paths.sort(key=get_app_name) try: @@ -154,17 +152,18 @@ class RosettaFileLevelMixin(RosettaBaseMixin): # If we can write changes to file, then we pull it up fresh with # each request. # XXX: brittle; what if this path doesn't exist? Isn't a .po file? - po_file = pofile(self.po_file_path, - wrapwidth=rosetta_settings.POFILE_WRAP_WIDTH) + po_file = pofile( + self.po_file_path, wrapwidth=rosetta_settings.POFILE_WRAP_WIDTH + ) for entry in po_file: # Entry is an object representing a single entry in the catalog. # We iterate through the *entire catalog*, pasting a hashed # value of the meat of each entry on its side in an attribute # called "md5hash". str_to_hash = ( - six.text_type(entry.msgid) + - six.text_type(entry.msgstr) + - six.text_type(entry.msgctxt or '') + six.text_type(entry.msgid) + + six.text_type(entry.msgstr) + + six.text_type(entry.msgctxt or '') ).encode('utf8') entry.md5hash = hashlib.md5(str_to_hash).hexdigest() else: @@ -178,9 +177,9 @@ class RosettaFileLevelMixin(RosettaBaseMixin): # a hashed value of the meat of each entry on its side in # an attribute called "md5hash". str_to_hash = ( - six.text_type(entry.msgid) + - six.text_type(entry.msgstr) + - six.text_type(entry.msgctxt or '') + six.text_type(entry.msgid) + + six.text_type(entry.msgstr) + + six.text_type(entry.msgctxt or '') ).encode('utf8') entry.md5hash = hashlib.new('md5', str_to_hash).hexdigest() storage.set(self.po_file_cache_key, po_file) @@ -206,6 +205,7 @@ class TranslationFileListView(RosettaBaseMixin, TemplateView): """Lists the languages, the gettext catalog files that can be translated, and their translation progress for a filtered list of apps/projects. """ + http_method_names = ['get'] template_name = 'rosetta/file-list.html' @@ -222,12 +222,15 @@ class TranslationFileListView(RosettaBaseMixin, TemplateView): if not can_translate_language(self.request.user, language[0]): continue - po_paths = find_pos(language[0], - project_apps=project_apps, - django_apps=django_apps, - third_party_apps=third_party_apps, - ) - po_files = [(get_app_name(l), os.path.realpath(l), pofile(l)) for l in po_paths] + po_paths = find_pos( + language[0], + project_apps=project_apps, + django_apps=django_apps, + third_party_apps=third_party_apps, + ) + po_files = [ + (get_app_name(l), os.path.realpath(l), pofile(l)) for l in po_paths + ] po_files.sort(key=lambda app: app[0]) languages.append((language[0], _(language[1]), po_files)) has_pos = has_pos or bool(po_paths) @@ -255,6 +258,7 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): searched include: source, translated text, "occurence" file path, or context hints. """ + # Note: due to the unorthodox nature of the form itself, we're not using # Django's generic FormView as our base class. http_method_names = ['get', 'post'] @@ -345,32 +349,38 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): file_change = True if old_msgstr != new_msgstr or old_fuzzy != is_fuzzy: - entry_changed.send(sender=entry, - user=request.user, - old_msgstr=old_msgstr, - old_fuzzy=old_fuzzy, - pofile=self.po_file_path, - language_code=self.language_id, - ) + entry_changed.send( + sender=entry, + user=request.user, + old_msgstr=old_msgstr, + old_fuzzy=old_fuzzy, + pofile=self.po_file_path, + language_code=self.language_id, + ) else: messages.error( self.request, - _("Some items in your last translation block couldn't " - "be saved: this usually happens when the catalog file " - "changes on disk after you last loaded it."), + _( + "Some items in your last translation block couldn't " + "be saved: this usually happens when the catalog file " + "changes on disk after you last loaded it." + ), ) if file_change and self.po_file_is_writable: try: self.po_file.metadata['Last-Translator'] = unicodedata.normalize( - 'NFKD', u"%s %s <%s>" % ( + 'NFKD', + u"%s %s <%s>" + % ( getattr(self.request.user, 'first_name', 'Anonymous'), getattr(self.request.user, 'last_name', 'User'), - getattr(self.request.user, 'email', 'anonymous@user.tld') - ) + getattr(self.request.user, 'email', 'anonymous@user.tld'), + ), ).encode('ascii', 'ignore') self.po_file.metadata['X-Translated-Using'] = u"django-rosetta %s" % ( - get_rosetta_version()) + get_rosetta_version() + ) self.po_file.metadata['PO-Revision-Date'] = timestamp_with_timezone() except UnicodeDecodeError: pass @@ -382,16 +392,16 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): if rosetta_settings.AUTO_COMPILE: self.po_file.save_as_mofile(po_filepath + '.mo') - post_save.send(sender=None, language_code=self.language_id, - request=self.request - ) + post_save.send( + sender=None, language_code=self.language_id, request=self.request + ) # Try auto-reloading via the WSGI daemon mode reload mechanism should_try_wsgi_reload = ( - rosetta_settings.WSGI_AUTO_RELOAD and - 'mod_wsgi.process_group' in self.request.environ and - self.request.environ.get('mod_wsgi.process_group', None) and - 'SCRIPT_FILENAME' in self.request.environ and - int(self.request.environ.get('mod_wsgi.script_reloading', 0)) + rosetta_settings.WSGI_AUTO_RELOAD + and 'mod_wsgi.process_group' in self.request.environ + and self.request.environ.get('mod_wsgi.process_group', None) + and 'SCRIPT_FILENAME' in self.request.environ + and int(self.request.environ.get('mod_wsgi.script_reloading', 0)) ) if should_try_wsgi_reload: try: @@ -402,6 +412,7 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): if rosetta_settings.UWSGI_AUTO_RELOAD: try: import uwsgi + uwsgi.reload() # pretty easy right? except: pass # we may not be running under uwsgi :P @@ -433,10 +444,12 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): } # Winnow down the query string args to non-blank ones query_string_args = {k: v for k, v in query_string_args.items() if v} - return HttpResponseRedirect("{url}?{qs}".format( - url=reverse('rosetta-form', kwargs=self.kwargs), - qs=urlencode_safe(query_string_args) - )) + return HttpResponseRedirect( + "{url}?{qs}".format( + url=reverse('rosetta-form', kwargs=self.kwargs), + qs=urlencode_safe(query_string_args), + ) + ) def get_context_data(self, **kwargs): context = super(TranslationFormView, self).get_context_data(**kwargs) @@ -491,8 +504,7 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): break if main_language: main_lang_po_path = self.po_file_path.replace( - '/%s/' % self.language_id, - '/%s/' % main_language_id, + '/%s/' % self.language_id, '/%s/' % main_language_id ) # XXX: brittle; what if this path doesn't exist? Isn't a .po file? main_lang_po = pofile(main_lang_po_path) @@ -521,29 +533,31 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): {k: v for k, v in query_string_args.items() if k == 'ref_lang'} ) - context.update({ - 'version': get_rosetta_version(), - 'LANGUAGES': LANGUAGES, - 'rosetta_settings': rosetta_settings, - 'rosetta_i18n_lang_name': rosetta_i18n_lang_name, - 'rosetta_i18n_lang_code': self.language_id, - 'rosetta_i18n_lang_code_normalized': self.language_id.replace('_', '-'), - 'rosetta_i18n_lang_bidi': rosetta_i18n_lang_bidi, - 'rosetta_i18n_filter': self.msg_filter, - 'rosetta_i18n_write': self.po_file_is_writable, - 'rosetta_messages': rosetta_messages, - 'page_range': needs_pagination and page_range, - 'needs_pagination': needs_pagination, - 'main_language': main_language, - 'rosetta_i18n_app': get_app_name(self.po_file_path), - 'page': page, - 'query': self.query, - 'pagination_query_string_base': pagination_query_string_base, - 'filter_query_string_base': filter_query_string_base, - 'paginator': paginator, - 'rosetta_i18n_pofile': self.po_file, - 'ref_lang': self.ref_lang, - }) + context.update( + { + 'version': get_rosetta_version(), + 'LANGUAGES': LANGUAGES, + 'rosetta_settings': rosetta_settings, + 'rosetta_i18n_lang_name': rosetta_i18n_lang_name, + 'rosetta_i18n_lang_code': self.language_id, + 'rosetta_i18n_lang_code_normalized': self.language_id.replace('_', '-'), + 'rosetta_i18n_lang_bidi': rosetta_i18n_lang_bidi, + 'rosetta_i18n_filter': self.msg_filter, + 'rosetta_i18n_write': self.po_file_is_writable, + 'rosetta_messages': rosetta_messages, + 'page_range': needs_pagination and page_range, + 'needs_pagination': needs_pagination, + 'main_language': main_language, + 'rosetta_i18n_app': get_app_name(self.po_file_path), + 'page': page, + 'query': self.query, + 'pagination_query_string_base': pagination_query_string_base, + 'filter_query_string_base': filter_query_string_base, + 'paginator': paginator, + 'rosetta_i18n_pofile': self.po_file, + 'ref_lang': self.ref_lang, + } + ) return context @@ -569,11 +583,10 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): ref_pofile = None if rosetta_settings.ENABLE_REFLANG and self.ref_lang != 'msgid': replacement = '{separator}locale{separator}{ref_lang}'.format( - separator=os.sep, - ref_lang=self.ref_lang + separator=os.sep, ref_lang=self.ref_lang ) pattern = r'\{separator}locale\{separator}[a-z]{{2}}'.format(separator=os.sep) - ref_fn = re.sub(pattern, replacement, self.po_file_path,) + ref_fn = re.sub(pattern, replacement, self.po_file_path) try: ref_pofile = pofile(ref_fn) except IOError: @@ -616,17 +629,21 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): rx = re.compile(re.escape(self.query), re.IGNORECASE) def concat_entry(e): - return (six.text_type(e.msgstr) + - six.text_type(e.msgid) + - six.text_type(e.msgctxt) + - six.text_type(e.comment) + - u''.join([o[0] for o in e.occurrences]) + - six.text_type(e.msgid_plural) + - u''.join(e.msgstr_plural.values()) - ) + return ( + six.text_type(e.msgstr) + + six.text_type(e.msgid) + + six.text_type(e.msgctxt) + + six.text_type(e.comment) + + u''.join([o[0] for o in e.occurrences]) + + six.text_type(e.msgid_plural) + + u''.join(e.msgstr_plural.values()) + ) - entries = [e_ for e_ in self.po_file - if not e_.obsolete and rx.search(concat_entry(e_))] + entries = [ + e_ + for e_ in self.po_file + if not e_.obsolete and rx.search(concat_entry(e_)) + ] else: # Scenario #2: filtered list of messages if self.msg_filter == 'untranslated': @@ -634,8 +651,7 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): elif self.msg_filter == 'translated': entries = self.po_file.translated_entries() elif self.msg_filter == 'fuzzy': - entries = [e_ for e_ in self.po_file.fuzzy_entries() - if not e_.obsolete] + entries = [e_ for e_ in self.po_file.fuzzy_entries() if not e_.obsolete] else: # ("all") entries = [e_ for e_ in self.po_file if not e_.obsolete] @@ -647,6 +663,7 @@ class TranslationFileDownload(RosettaFileLevelMixin, View): and compiled (.mo) files, either as they exist on disk, or, if what's on disk is unwritable (permissions-wise), return what's in the cache. """ + http_method_names = [u'get'] def get(self, request, *args, **kwargs): @@ -677,7 +694,6 @@ class TranslationFileDownload(RosettaFileLevelMixin, View): @user_passes_test(lambda user: can_translate(user), LoginURL()) def translate_text(request): - def translate(text, from_language, to_language, subscription_key): """ This method does the heavy lifting of connecting to the translator API and fetching a response @@ -696,20 +712,20 @@ def translate_text(request): headers = { 'Ocp-Apim-Subscription-Key': subscription_key, 'Content-type': 'application/json', - 'X-ClientTraceId': str(uuid.uuid4()) + 'X-ClientTraceId': str(uuid.uuid4()), } - url_parameters = { - "from": from_language, - "to": to_language - } + url_parameters = {"from": from_language, "to": to_language} - request_data = [ - {"text": text} - ] + request_data = [{"text": text}] api_hostname = AZURE_TRANSLATOR_HOST + AZURE_TRANSLATOR_PATH - r = requests.post(api_hostname, headers=headers, params=url_parameters, data=json.dumps(request_data)) + r = requests.post( + api_hostname, + headers=headers, + params=url_parameters, + data=json.dumps(request_data), + ) return json.loads(r.text) language_from = request.GET.get('from', None) @@ -723,7 +739,9 @@ def translate_text(request): AZURE_CLIENT_SECRET = getattr(settings, 'AZURE_CLIENT_SECRET', None) try: - api_response = translate(text, language_from, language_to, AZURE_CLIENT_SECRET) + api_response = translate( + text, language_from, language_to, AZURE_CLIENT_SECRET + ) # result will be a dict if there is an error, e.g. # { @@ -737,7 +755,9 @@ def translate_text(request): error_message = api_error.get("message") data = { 'success': False, - 'error': "Microsoft Translation API error: Error code {}, {}".format(error_code, error_message), + 'error': "Microsoft Translation API error: Error code {}, {}".format( + error_code, error_message + ), } else: # response body will be of the form: @@ -753,15 +773,14 @@ def translate_text(request): translations = api_response[0].get("translations") translated_text = translations[0].get("text") - data = { - 'success': True, - 'translation': translated_text - } + data = {'success': True, 'translation': translated_text} # catch general connection exception in the requests framework except requests.exceptions.RequestException as err: data = { 'success': False, - 'error': "Error connecting to Microsoft Translation Service: {0}".format(err), + 'error': "Error connecting to Microsoft Translation Service: {0}".format( + err + ), } return JsonResponse(data) diff --git a/setup.py b/setup.py index 80e6360..8185914 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,8 @@ -from setuptools import setup, find_packages -from setuptools.command.test import test as test_command import sys +from setuptools import find_packages, setup +from setuptools.command.test import test as test_command + class Tox(test_command): user_options = [('tox-args=', 'a', "Arguments to pass to tox")] @@ -19,12 +20,14 @@ class Tox(test_command): # import here, cause outside the eggs aren't loaded import tox import shlex + args = self.tox_args if args: args = shlex.split(self.tox_args) errno = tox.cmdline(args=args) sys.exit(errno) + with open('README.rst') as readme: long_description = readme.read() @@ -57,9 +60,9 @@ setup( zip_safe=False, install_requires=[ 'six >=1.2.0', - 'Django >= 1.11', + 'Django >= 2.0', 'requests >= 2.1.0', - 'polib >= 1.1.0' + 'polib >= 1.1.0', ], tests_require=['tox', 'vcrpy'], cmdclass={'test': Tox}, diff --git a/testproject/settings.py b/testproject/settings.py index 694b66d..b33d1a4 100644 --- a/testproject/settings.py +++ b/testproject/settings.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- -# from __future__ import unicode_literals -import django import os import sys +import django + SITE_ID = 1 @@ -15,7 +14,7 @@ DJANGO_VERSION = django.get_version() DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(PROJECT_PATH, 'rosetta.db') + 'NAME': os.path.join(PROJECT_PATH, 'rosetta.db'), } } @@ -23,7 +22,7 @@ CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', - 'KEY_PREFIX': 'ROSETTA_TEST' + 'KEY_PREFIX': 'ROSETTA_TEST', } } @@ -58,7 +57,7 @@ MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware' + 'django.contrib.messages.middleware.MessageMiddleware', ) # Note: languages are overridden in the test runner @@ -75,15 +74,11 @@ LANGUAGES = ( SILENCED_SYSTEM_CHECKS = ["translation.E002"] -LOCALE_PATHS = [ - os.path.join(PROJECT_PATH, 'locale'), -] +LOCALE_PATHS = [os.path.join(PROJECT_PATH, 'locale')] SOUTH_TESTS_MIGRATE = False -FIXTURE_DIRS = ( - os.path.join(PROJECT_PATH, 'fixtures'), -) +FIXTURE_DIRS = (os.path.join(PROJECT_PATH, 'fixtures'),) STATIC_URL = '/static/' ROOT_URLCONF = 'testproject.urls' @@ -102,10 +97,10 @@ TEMPLATES = [ "django.template.context_processors.media", "django.template.context_processors.static", "django.template.context_processors.tz", - "django.contrib.messages.context_processors.messages" - ) - } - }, + "django.contrib.messages.context_processors.messages", + ), + }, + } ] STATIC_URL = '/static/' diff --git a/tox.ini b/tox.ini index e635ce1..328ad16 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist = flake8, - py{27,36}-django111, py{36,37}-django{20,21}, py{36,37,38}-django{22,30}, gettext, @@ -19,16 +18,13 @@ setenv = PYTHONDONTWRITEBYTECODE=1 deps = - django111: Django==1.11.* django20: Django==2.0.* django21: Django>=2.1a1,<=2.1.99 django22: Django>=2.2.8,<=2.2.99 django30: Django>=3.0,<=3.0.99 - py27-django111: python-memcached - py{36,37,38}-django{111,20,21,22,30}: python3-memcached + py{36,37,38}-django{20,21,22,30}: python3-memcached - # py27-django111: pudb requests polib>=1.1.0 six @@ -62,9 +58,9 @@ changedir = docs commands= sphinx-build -W -b html . _build/html + [testenv:flake8] basepython = python3 deps = flake8==2.4.1 commands= flake8 {toxinidir}/rosetta -