Merge pull request #19072 from shadinaif/fix_lazy_text_with_json_dumps

Fix exceptions raised when a lazy text is used in json dump
This commit is contained in:
Ned Batchelder
2018-11-05 06:20:05 -05:00
committed by GitHub
21 changed files with 107 additions and 48 deletions

View File

@@ -1476,7 +1476,7 @@ class ContentStoreTest(ContentStoreTestCase):
resp = self.client.get_html('/home/')
self.assertContains(
resp,
'<h1 class="page-header">Studio Home</h1>',
u'<h1 class="page-header">{} Home</h1>'.format(settings.STUDIO_SHORT_NAME),
status_code=200,
html=True
)

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
Tests for validate Internationalization and Module i18n service.
"""
@@ -207,7 +208,7 @@ class InternationalizationTest(ModuleStoreTestCase):
resp = self.client.get_html('/home/')
self.assertContains(resp,
'<h1 class="page-header">Studio Home</h1>',
u'<h1 class="page-header">𝓢𝓽𝓾𝓭𝓲𝓸 Home</h1>',
status_code=200,
html=True)
@@ -223,7 +224,7 @@ class InternationalizationTest(ModuleStoreTestCase):
)
self.assertContains(resp,
'<h1 class="page-header">Studio Home</h1>',
u'<h1 class="page-header">𝓢𝓽𝓾𝓭𝓲𝓸 Home</h1>',
status_code=200,
html=True)

View File

@@ -91,7 +91,6 @@
"LOG_DIR": "** OVERRIDDEN **",
"MEDIA_URL": "/media/",
"MKTG_URL_LINK_MAP": {},
"PLATFORM_NAME": "édX",
"SERVER_EMAIL": "devops@example.com",
"SESSION_COOKIE_DOMAIN": null,
"SITE_NAME": "localhost",

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
Settings for Bok Choy tests that are used when running Studio.
@@ -13,6 +14,7 @@ from the same directory.
import os
from path import Path as path
from django.utils.translation import ugettext_lazy
from openedx.core.release import RELEASE_LINE
########################## Prod-like settings ###################################
@@ -52,6 +54,11 @@ XBLOCK_SETTINGS.update({'VideoDescriptor': {'licensing_enabled': True}})
# Capture the console log via template includes, until webdriver supports log capture again
CAPTURE_CONSOLE_LOG = True
PLATFORM_NAME = ugettext_lazy(u"édX")
PLATFORM_DESCRIPTION = ugettext_lazy(u"Open édX Platform")
STUDIO_NAME = ugettext_lazy(u"Your Platform 𝓢𝓽𝓾𝓭𝓲𝓸")
STUDIO_SHORT_NAME = ugettext_lazy(u"𝓢𝓽𝓾𝓭𝓲𝓸")
############################ STATIC FILES #############################
# Enable debug so that static assets are served by Django

View File

@@ -91,7 +91,6 @@
"LOG_DIR": "** OVERRIDDEN **",
"MEDIA_URL": "/media/",
"MKTG_URL_LINK_MAP": {},
"PLATFORM_NAME": "édX",
"SERVER_EMAIL": "devops@example.com",
"SESSION_COOKIE_DOMAIN": null,
"SITE_NAME": "localhost",

View File

@@ -254,10 +254,14 @@ LOGGING = get_logger_config(LOG_DIR,
service_variant=SERVICE_VARIANT)
#theming start:
PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME', PLATFORM_NAME)
PLATFORM_DESCRIPTION = ENV_TOKENS.get('PLATFORM_DESCRIPTION', PLATFORM_DESCRIPTION)
STUDIO_NAME = ENV_TOKENS.get('STUDIO_NAME', STUDIO_NAME)
STUDIO_SHORT_NAME = ENV_TOKENS.get('STUDIO_SHORT_NAME', STUDIO_SHORT_NAME)
# The following variables use (or) instead of the default value inside (get). This is to enforce using the Lazy Text
# values when the varibale is an empty string. Therefore, setting these variable as empty text in related
# json files will make the system reads thier values from django translation files
PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME') or PLATFORM_NAME
PLATFORM_DESCRIPTION = ENV_TOKENS.get('PLATFORM_DESCRIPTION') or PLATFORM_DESCRIPTION
STUDIO_NAME = ENV_TOKENS.get('STUDIO_NAME') or STUDIO_NAME
STUDIO_SHORT_NAME = ENV_TOKENS.get('STUDIO_SHORT_NAME') or STUDIO_SHORT_NAME
# Event Tracking
if "TRACKING_IGNORE_URL_PATTERNS" in ENV_TOKENS:

View File

@@ -13,6 +13,8 @@ sessions. Assumes structure:
# want to import all variables from base settings files
# pylint: disable=wildcard-import, unused-wildcard-import
from django.utils.translation import ugettext_lazy
from .common import *
import os
from path import Path as path
@@ -25,6 +27,7 @@ from openedx.core.lib.derived import derive_settings
from lms.envs.test import (
WIKI_ENABLED,
PLATFORM_NAME,
PLATFORM_DESCRIPTION,
SITE_NAME,
DEFAULT_FILE_STORAGE,
MEDIA_ROOT,
@@ -35,6 +38,12 @@ from lms.envs.test import (
ECOMMERCE_API_URL,
)
# Include a non-ascii character in STUDIO_NAME and STUDIO_SHORT_NAME to uncover possible
# UnicodeEncodeErrors in tests. Also use lazy text to reveal possible json dumps errors
STUDIO_NAME = ugettext_lazy(u"Your Platform 𝓢𝓽𝓾𝓭𝓲𝓸")
STUDIO_SHORT_NAME = ugettext_lazy(u"𝓢𝓽𝓾𝓭𝓲𝓸")
# Allow all hosts during tests, we use a lot of different ones all over the codebase.
ALLOWED_HOSTS = [
'*'

View File

@@ -5,7 +5,6 @@ that are stored in a database an accessible using their Location as an identifie
import logging
import re
import json
import datetime
from pytz import UTC
@@ -22,11 +21,15 @@ from xblock.plugin import default_select
from .exceptions import InvalidLocationError, InsufficientSpecificationError
from xmodule.errortracker import make_error_tracker
from xmodule.assetstore import AssetMetadata
from opaque_keys.edx.keys import CourseKey, UsageKey, AssetKey
from opaque_keys.edx.keys import CourseKey, AssetKey
from opaque_keys.edx.locations import Location # For import backwards compatibility
from xblock.runtime import Mixologist
from xblock.core import XBlock
# The below import is not used within this module, but ir is still needed becuase
# other modules are imorting EdxJSONEncoder from here
from openedx.core.lib.json_utils import EdxJSONEncoder # pylint: disable=unused-import
log = logging.getLogger('edx.modulestore')
new_contract('CourseKey', CourseKey)
@@ -1430,25 +1433,3 @@ def prefer_xmodules(identifier, entry_points):
return default_select(identifier, from_xmodule)
else:
return default_select(identifier, entry_points)
class EdxJSONEncoder(json.JSONEncoder):
"""
Custom JSONEncoder that handles `Location` and `datetime.datetime` objects.
`Location`s are encoded as their url string form, and `datetime`s as
ISO date strings
"""
def default(self, obj):
if isinstance(obj, (CourseKey, UsageKey)):
return unicode(obj)
elif isinstance(obj, datetime.datetime):
if obj.tzinfo is not None:
if obj.utcoffset() is None:
return obj.isoformat() + 'Z'
else:
return obj.isoformat()
else:
return obj.isoformat()
else:
return super(EdxJSONEncoder, self).default(obj)

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
Tests of XML export
"""
@@ -10,6 +11,7 @@ import shutil
import unittest
from datetime import datetime, timedelta, tzinfo
from django.utils.translation import ugettext_lazy
from fs.osfs import OSFS
from path import Path as path
from six import text_type
@@ -212,3 +214,17 @@ class TestEdxJsonEncoder(unittest.TestCase):
with self.assertRaises(TypeError):
self.encoder.default({})
def test_encode_unicode_lazy_text(self):
"""
Verify that the encoding is functioning fine with lazy text
"""
# Initializing a lazy text object with Unicode
unicode_text = u"Your 𝓟𝓵𝓪𝓽𝓯𝓸𝓻𝓶 Name Here"
lazy_text = ugettext_lazy(unicode_text)
self.assertEquals(
unicode_text,
self.encoder.default(lazy_text)
)

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
Test the Studio help links.
"""
@@ -189,7 +190,7 @@ class HomeHelpTest(StudioCourseTest):
test=self,
page=self.home_page,
href=expected_url,
help_text='Getting Started with Your Platform Studio',
help_text=u'Getting Started with Your Platform 𝓢𝓽𝓾𝓭𝓲𝓸',
as_list_item=True
)
@@ -242,7 +243,7 @@ class NewCourseHelpTest(AcceptanceTest):
test=self,
page=self.dashboard_page,
href=expected_url,
help_text='Getting Started with Your Platform Studio',
help_text=u'Getting Started with Your Platform 𝓢𝓽𝓾𝓭𝓲𝓸',
as_list_item=True
)
@@ -295,7 +296,7 @@ class NewLibraryHelpTest(AcceptanceTest):
test=self,
page=self.dashboard_page,
href=expected_url,
help_text='Getting Started with Your Platform Studio',
help_text=u'Getting Started with Your Platform 𝓢𝓽𝓾𝓭𝓲𝓸',
as_list_item=True
)

View File

@@ -115,7 +115,6 @@
"ROOT": "root",
"SITEMAP.XML": "sitemap_xml"
},
"PLATFORM_NAME": "édX",
"REGISTRATION_EXTENSION_FORM": "openedx.core.djangoapps.user_api.tests.test_helpers.TestCaseForm",
"REGISTRATION_EXTRA_FIELDS": {
"level_of_education": "optional",

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
Settings for Bok Choy tests that are used when running LMS.
@@ -14,6 +15,7 @@ import os
from path import Path as path
from tempfile import mkdtemp
from django.utils.translation import ugettext_lazy
from openedx.core.release import RELEASE_LINE
CONFIG_ROOT = path(__file__).abspath().dirname()
@@ -52,6 +54,9 @@ update_module_store_settings(
# Capture the console log via template includes, until webdriver supports log capture again
CAPTURE_CONSOLE_LOG = True
PLATFORM_NAME = ugettext_lazy(u"édX")
PLATFORM_DESCRIPTION = ugettext_lazy(u"Open édX Platform")
############################ STATIC FILES #############################
# Enable debug so that static assets are served by Django

View File

@@ -115,7 +115,6 @@
"ROOT": "root",
"SITEMAP.XML": "sitemap_xml"
},
"PLATFORM_NAME": "édX",
"REGISTRATION_EXTENSION_FORM": "openedx.core.djangoapps.user_api.tests.test_helpers.TestCaseForm",
"REGISTRATION_EXTRA_FIELDS": {
"level_of_education": "optional",

View File

@@ -131,8 +131,12 @@ COURSE_MODE_DEFAULTS = ENV_TOKENS.get('COURSE_MODE_DEFAULTS', COURSE_MODE_DEFAUL
MEDIA_ROOT = ENV_TOKENS.get('MEDIA_ROOT', MEDIA_ROOT)
MEDIA_URL = ENV_TOKENS.get('MEDIA_URL', MEDIA_URL)
PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME', PLATFORM_NAME)
PLATFORM_DESCRIPTION = ENV_TOKENS.get('PLATFORM_DESCRIPTION', PLATFORM_DESCRIPTION)
# The following variables use (or) instead of the default value inside (get). This is to enforce using the Lazy Text
# values when the varibale is an empty string. Therefore, setting these variable as empty text in related
# json files will make the system reads thier values from django translation files
PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME') or PLATFORM_NAME
PLATFORM_DESCRIPTION = ENV_TOKENS.get('PLATFORM_DESCRIPTION') or PLATFORM_DESCRIPTION
# For displaying on the receipt. At Stanford PLATFORM_NAME != MERCHANT_NAME, but PLATFORM_NAME is a fine default
PLATFORM_TWITTER_ACCOUNT = ENV_TOKENS.get('PLATFORM_TWITTER_ACCOUNT', PLATFORM_TWITTER_ACCOUNT)
PLATFORM_FACEBOOK_ACCOUNT = ENV_TOKENS.get('PLATFORM_FACEBOOK_ACCOUNT', PLATFORM_FACEBOOK_ACCOUNT)

View File

@@ -13,6 +13,8 @@ sessions. Assumes structure:
# want to import all variables from base settings files
# pylint: disable=wildcard-import, unused-wildcard-import
from django.utils.translation import ugettext_lazy
from .common import *
import os
from path import Path as path
@@ -397,8 +399,12 @@ FEATURES['CLASS_DASHBOARD'] = True
import openid.oidutil
openid.oidutil.log = lambda message, level=0: None
# Include a non-ascii character in PLATFORM_NAME to uncover possible UnicodeEncodeErrors in tests.
PLATFORM_NAME = u"édX"
# Include a non-ascii character in PLATFORM_NAME and PLATFORM_DESCRIPTION to uncover possible
# UnicodeEncodeErrors in tests. Also use lazy text to reveal possible json dumps errors
PLATFORM_NAME = ugettext_lazy(u"édX")
PLATFORM_DESCRIPTION = ugettext_lazy(u"Open édX Platform")
SITE_NAME = "edx.org"
# set up some testing for microsites

View File

@@ -4,11 +4,11 @@ Message Types for user_api emails
from django.conf import settings
from edx_ace import message
from openedx.core.djangoapps.ace_common.message import BaseMessageType
from openedx.core.djangoapps.site_configuration import helpers
class DeletionNotificationMessage(message.MessageType):
class DeletionNotificationMessage(BaseMessageType):
"""
Message to notify learners that their account is queued for deletion.
"""

View File

@@ -0,0 +1,29 @@
"""
Helpers for json serialization
"""
import datetime
from django.core.serializers.json import DjangoJSONEncoder
from opaque_keys.edx.keys import CourseKey, UsageKey
class EdxJSONEncoder(DjangoJSONEncoder):
"""
Custom JSONEncoder that handles `Location` and `datetime.datetime` objects.
`Location`s are encoded as their url string form, and `datetime`s as
ISO date strings
"""
def default(self, o): # pylint: disable=method-hidden
if isinstance(o, (CourseKey, UsageKey)):
return unicode(o)
elif isinstance(o, datetime.datetime):
if o.tzinfo is not None:
if o.utcoffset() is None:
return o.isoformat() + 'Z'
else:
return o.isoformat()
else:
return o.isoformat()
else:
return super(EdxJSONEncoder, self).default(o)

View File

@@ -64,7 +64,7 @@ django-webpack-loader # Used to wire webpack bundles into the djan
djangorestframework-jwt
django-xforwardedfor-middleware==2.0 # Middleware to use the X-Forwarded-For header as the request IP
dogapi==1.2.1 # Python bindings to Datadog's API, for metrics gathering
edx-ace==0.1.9
edx-ace==0.1.10
edx-analytics-data-api-client
edx-ccx-keys
edx-celeryutils

View File

@@ -109,7 +109,7 @@ dm.xmlsec.binding==1.3.3 # via python-saml
docopt==0.6.2
docutils==0.14 # via botocore
dogapi==1.2.1
edx-ace==0.1.9
edx-ace==0.1.10
edx-analytics-data-api-client==0.14.4
edx-ccx-keys==0.2.1
edx-celeryutils==0.2.7

View File

@@ -128,7 +128,7 @@ dm.xmlsec.binding==1.3.3
docopt==0.6.2
docutils==0.14
dogapi==1.2.1
edx-ace==0.1.9
edx-ace==0.1.10
edx-analytics-data-api-client==0.14.4
edx-ccx-keys==0.2.1
edx-celeryutils==0.2.7

View File

@@ -123,7 +123,7 @@ dm.xmlsec.binding==1.3.3
docopt==0.6.2
docutils==0.14
dogapi==1.2.1
edx-ace==0.1.9
edx-ace==0.1.10
edx-analytics-data-api-client==0.14.4
edx-ccx-keys==0.2.1
edx-celeryutils==0.2.7