From 391b91b99a9b2d42c5bca4c5f2b22f21bddba02f Mon Sep 17 00:00:00 2001 From: Evans Dianga Date: Tue, 7 May 2019 09:53:17 -0400 Subject: [PATCH] Modernize common/djangoapps/util- INCR-203 (#20437) --- common/djangoapps/util/admin.py | 2 ++ .../djangoapps/util/bad_request_rate_limiter.py | 2 ++ common/djangoapps/util/cache.py | 11 ++++++++--- common/djangoapps/util/config_parse.py | 6 +++++- common/djangoapps/util/course.py | 15 ++++++++++----- common/djangoapps/util/date_utils.py | 5 ++++- common/djangoapps/util/db.py | 2 ++ common/djangoapps/util/disable_rate_limit.py | 2 ++ common/djangoapps/util/file.py | 5 ++++- common/djangoapps/util/json_request.py | 2 ++ common/djangoapps/util/keyword_substitution.py | 2 ++ common/djangoapps/util/memcache.py | 10 +++++++--- common/djangoapps/util/milestones_helpers.py | 17 ++++++++++------- common/djangoapps/util/model_utils.py | 6 ++++-- common/djangoapps/util/models.py | 8 +++++--- common/djangoapps/util/organizations_helpers.py | 2 ++ .../util/password_policy_validators.py | 13 ++++++------- common/djangoapps/util/query.py | 2 ++ common/djangoapps/util/testing.py | 4 +++- common/djangoapps/util/url.py | 2 ++ common/djangoapps/util/views.py | 9 ++++++--- 21 files changed, 90 insertions(+), 37 deletions(-) diff --git a/common/djangoapps/util/admin.py b/common/djangoapps/util/admin.py index 0f4f7b2354..70352fcdef 100644 --- a/common/djangoapps/util/admin.py +++ b/common/djangoapps/util/admin.py @@ -1,5 +1,7 @@ """Admin interface for the util app. """ +from __future__ import absolute_import + from django.contrib import admin from util.models import RateLimitConfiguration diff --git a/common/djangoapps/util/bad_request_rate_limiter.py b/common/djangoapps/util/bad_request_rate_limiter.py index 381289f639..10bb67458d 100644 --- a/common/djangoapps/util/bad_request_rate_limiter.py +++ b/common/djangoapps/util/bad_request_rate_limiter.py @@ -2,6 +2,8 @@ A utility class which wraps the RateLimitMixin 3rd party class to do bad request counting which can be used for rate limiting """ +from __future__ import absolute_import + from ratelimitbackend.backends import RateLimitMixin diff --git a/common/djangoapps/util/cache.py b/common/djangoapps/util/cache.py index 26ae2a6963..5987d704e4 100644 --- a/common/djangoapps/util/cache.py +++ b/common/djangoapps/util/cache.py @@ -5,9 +5,14 @@ invalidation. Import these instead of django.core.cache. Note that 'default' is being preserved for user session caching, which we're not migrating so as not to inconvenience users by logging them all out. """ -import urllib +from __future__ import absolute_import + from functools import wraps +import six +import six.moves.urllib.error +import six.moves.urllib.parse +import six.moves.urllib.request from django.conf import settings from django.core import cache # If we can't find a 'general' CACHE defined in settings.py, we simply fall back @@ -66,8 +71,8 @@ def cache_if_anonymous(*get_parameters): if parameter_value is not None: # urlencode expects data to be of type str, and doesn't deal well with Unicode data # since it doesn't provide a way to specify an encoding. - cache_key = cache_key + '.' + urllib.urlencode({ - get_parameter: unicode(parameter_value).encode('utf-8') + cache_key = cache_key + '.' + six.moves.urllib.parse.urlencode({ + get_parameter: six.text_type(parameter_value).encode('utf-8') }) response = cache.get(cache_key) # pylint: disable=maybe-no-member diff --git a/common/djangoapps/util/config_parse.py b/common/djangoapps/util/config_parse.py index 3d76aed308..d89a16efc5 100644 --- a/common/djangoapps/util/config_parse.py +++ b/common/djangoapps/util/config_parse.py @@ -1,8 +1,12 @@ """ Helper functions for configuration parsing """ +from __future__ import absolute_import + import collections +import six + def convert_tokens(tokens): """ @@ -15,7 +19,7 @@ def convert_tokens(tokens): if tokens == 'None': return None - elif isinstance(tokens, basestring) or (not isinstance(tokens, collections.Iterable)): + elif isinstance(tokens, six.string_types) or (not isinstance(tokens, collections.Iterable)): return tokens elif isinstance(tokens, dict): return { diff --git a/common/djangoapps/util/course.py b/common/djangoapps/util/course.py index 598e703456..1ef703d424 100644 --- a/common/djangoapps/util/course.py +++ b/common/djangoapps/util/course.py @@ -1,9 +1,14 @@ """ Utility methods related to course """ -import logging -import urllib +from __future__ import absolute_import +import logging + +import six +import six.moves.urllib.error +import six.moves.urllib.parse +import six.moves.urllib.request from django.conf import settings from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers @@ -29,8 +34,8 @@ def get_encoded_course_sharing_utm_params(): Returns encoded Course Sharing UTM Parameters. """ return { - utm_source: urllib.urlencode(utm_params) - for utm_source, utm_params in COURSE_SHARING_UTM_PARAMETERS.iteritems() + utm_source: six.moves.urllib.parse.urlencode(utm_params) + for utm_source, utm_params in six.iteritems(COURSE_SHARING_UTM_PARAMETERS) } @@ -53,7 +58,7 @@ def get_link_for_about_page(course): else: course_about_url = u'{about_base_url}/courses/{course_key}/about'.format( about_base_url=configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL), - course_key=unicode(course.id), + course_key=six.text_type(course.id), ) return course_about_url diff --git a/common/djangoapps/util/date_utils.py b/common/djangoapps/util/date_utils.py index 3d9ff08674..96cbb66e08 100644 --- a/common/djangoapps/util/date_utils.py +++ b/common/djangoapps/util/date_utils.py @@ -2,9 +2,12 @@ Convenience methods for working with datetime objects """ +from __future__ import absolute_import + import re from datetime import datetime, timedelta +import six from django.utils.translation import pgettext, ugettext from pytz import UnknownTimeZoneError, timezone, utc @@ -57,7 +60,7 @@ def get_time_display(dtime, format_string=None, coerce_tz=None): if dtime is None or format_string is None: return get_default_time_display(dtime) try: - return unicode(strftime_localized(dtime, format_string)) + return six.text_type(strftime_localized(dtime, format_string)) except ValueError: return get_default_time_display(dtime) diff --git a/common/djangoapps/util/db.py b/common/djangoapps/util/db.py index 7dea56aaef..662efe5ae7 100644 --- a/common/djangoapps/util/db.py +++ b/common/djangoapps/util/db.py @@ -1,6 +1,8 @@ """ Utility functions related to databases. """ +from __future__ import absolute_import + import random # TransactionManagementError used below actually *does* derive from the standard "Exception" class. # pylint: disable=nonstandard-exception diff --git a/common/djangoapps/util/disable_rate_limit.py b/common/djangoapps/util/disable_rate_limit.py index c160d03a1e..8937198fdd 100644 --- a/common/djangoapps/util/disable_rate_limit.py +++ b/common/djangoapps/util/disable_rate_limit.py @@ -13,6 +13,8 @@ To disable rate limiting: Note: You should NEVER disable rate limiting in production. """ +from __future__ import absolute_import + import logging from functools import wraps diff --git a/common/djangoapps/util/file.py b/common/djangoapps/util/file.py index c0af52ab9a..1a41fe8910 100644 --- a/common/djangoapps/util/file.py +++ b/common/djangoapps/util/file.py @@ -2,9 +2,12 @@ Utility methods related to file handling. """ +from __future__ import absolute_import + import os from datetime import datetime +import six from django.core.exceptions import PermissionDenied from django.core.files.storage import DefaultStorage, get_valid_filename from django.utils.translation import ugettext as _ @@ -98,7 +101,7 @@ def course_filename_prefix_generator(course_id, separator='_'): str: A unicode string which can safely be inserted into a filename. """ - return get_valid_filename(unicode(separator).join([course_id.org, course_id.course, course_id.run])) + return get_valid_filename(six.text_type(separator).join([course_id.org, course_id.course, course_id.run])) # pylint: disable=invalid-name diff --git a/common/djangoapps/util/json_request.py b/common/djangoapps/util/json_request.py index 6086cd065d..a5badd3bb7 100644 --- a/common/djangoapps/util/json_request.py +++ b/common/djangoapps/util/json_request.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import decimal import json from functools import wraps diff --git a/common/djangoapps/util/keyword_substitution.py b/common/djangoapps/util/keyword_substitution.py index 9c3d4c0aff..80d998f14f 100644 --- a/common/djangoapps/util/keyword_substitution.py +++ b/common/djangoapps/util/keyword_substitution.py @@ -18,6 +18,8 @@ Usage: - CMS: Not called """ +from __future__ import absolute_import + from django.contrib.auth.models import User from student.models import anonymous_id_for_user diff --git a/common/djangoapps/util/memcache.py b/common/djangoapps/util/memcache.py index 683a124e97..bca18d51d8 100644 --- a/common/djangoapps/util/memcache.py +++ b/common/djangoapps/util/memcache.py @@ -2,9 +2,13 @@ This module provides a KEY_FUNCTION suitable for use with a memcache backend so that we can cache any keys, not just ones that memcache would ordinarily accept """ -import hashlib -import urllib +from __future__ import absolute_import +import hashlib + +import six.moves.urllib.error +import six.moves.urllib.parse +import six.moves.urllib.request from django.utils.encoding import smart_str @@ -22,7 +26,7 @@ def cleaned_string(val): Converts `val` to unicode and URL-encodes special characters (including quotes and spaces) """ - return urllib.quote_plus(smart_str(val)) + return six.moves.urllib.parse.quote_plus(smart_str(val)) def safe_key(key, key_prefix, version): diff --git a/common/djangoapps/util/milestones_helpers.py b/common/djangoapps/util/milestones_helpers.py index 907f579d6a..b4ec501a75 100644 --- a/common/djangoapps/util/milestones_helpers.py +++ b/common/djangoapps/util/milestones_helpers.py @@ -2,6 +2,9 @@ """ Utility library for working with the edx-milestones app """ +from __future__ import absolute_import + +import six from django.conf import settings from django.utils.translation import ugettext as _ from milestones import api as milestones_api @@ -54,12 +57,12 @@ def add_prerequisite_course(course_key, prerequisite_course_key): if not is_prerequisite_courses_enabled(): return None milestone_name = _('Course {course_id} requires {prerequisite_course_id}').format( - course_id=unicode(course_key), - prerequisite_course_id=unicode(prerequisite_course_key) + course_id=six.text_type(course_key), + prerequisite_course_id=six.text_type(prerequisite_course_key) ) milestone = milestones_api.add_milestone({ 'name': milestone_name, - 'namespace': unicode(prerequisite_course_key), + 'namespace': six.text_type(prerequisite_course_key), 'description': _('System defined milestone'), }) # add requirement course milestone @@ -213,7 +216,7 @@ def get_required_content(course_key, user): """ required_content = [] if settings.FEATURES.get('MILESTONES_APP'): - course_run_id = unicode(course_key) + course_run_id = six.text_type(course_key) if user.is_authenticated: # Get all of the outstanding milestones for this course, for this user @@ -276,9 +279,9 @@ def generate_milestone_namespace(namespace, course_key=None): """ Returns a specifically-formatted namespace string for the specified type """ - if namespace in NAMESPACE_CHOICES.values(): + if namespace in list(NAMESPACE_CHOICES.values()): if namespace == 'entrance_exams': - return '{}.{}'.format(unicode(course_key), NAMESPACE_CHOICES['ENTRANCE_EXAM']) + return '{}.{}'.format(six.text_type(course_key), NAMESPACE_CHOICES['ENTRANCE_EXAM']) def serialize_user(user): @@ -373,7 +376,7 @@ def get_course_content_milestones(course_id, content_id=None, relationship='requ if content_id is None: return request_cache_dict[user_id][relationship] - return [m for m in request_cache_dict[user_id][relationship] if m['content_id'] == unicode(content_id)] + return [m for m in request_cache_dict[user_id][relationship] if m['content_id'] == six.text_type(content_id)] def remove_course_content_user_milestones(course_key, content_key, user, relationship): diff --git a/common/djangoapps/util/model_utils.py b/common/djangoapps/util/model_utils.py index bc22fb0017..da885ff078 100644 --- a/common/djangoapps/util/model_utils.py +++ b/common/djangoapps/util/model_utils.py @@ -1,10 +1,12 @@ """ Utilities for django models. """ +from __future__ import absolute_import + +import six from django.conf import settings from django.dispatch import Signal from django_countries.fields import Country - from eventtracking import tracker # The setting name used for events when "settings" (account settings, preferences, profile information) change. @@ -162,7 +164,7 @@ def _get_truncated_setting_value(value, max_length=None): truncated_value (object): the possibly truncated version of the value. was_truncated (bool): returns true if the serialized value was truncated. """ - if isinstance(value, basestring) and max_length is not None and len(value) > max_length: + if isinstance(value, six.string_types) and max_length is not None and len(value) > max_length: return value[0:max_length], True else: return value, False diff --git a/common/djangoapps/util/models.py b/common/djangoapps/util/models.py index 066b708290..413640b0b6 100644 --- a/common/djangoapps/util/models.py +++ b/common/djangoapps/util/models.py @@ -1,12 +1,14 @@ """Models for the util app. """ +from __future__ import absolute_import + import cStringIO import gzip import logging +import six from config_models.models import ConfigurationModel from django.db import models from django.utils.text import compress_string - from opaque_keys.edx.django.models import CreatorMixin logger = logging.getLogger(__name__) # pylint: disable=invalid-name @@ -56,7 +58,7 @@ class CompressedTextField(CreatorMixin, models.TextField): Compress the text data. """ if value is not None: - if isinstance(value, unicode): + if isinstance(value, six.text_type): value = value.encode('utf8') value = compress_string(value) value = value.encode('base64').decode('utf8') @@ -66,7 +68,7 @@ class CompressedTextField(CreatorMixin, models.TextField): """ Decompresses the value from the database. """ - if isinstance(value, unicode): + if isinstance(value, six.text_type): value = decompress_string(value) return value diff --git a/common/djangoapps/util/organizations_helpers.py b/common/djangoapps/util/organizations_helpers.py index 124ab8f1c9..4b9ebe234d 100644 --- a/common/djangoapps/util/organizations_helpers.py +++ b/common/djangoapps/util/organizations_helpers.py @@ -2,6 +2,8 @@ Utility library for working with the edx-organizations app """ +from __future__ import absolute_import + from django.conf import settings from django.db.utils import DatabaseError diff --git a/common/djangoapps/util/password_policy_validators.py b/common/djangoapps/util/password_policy_validators.py index 58238a99d1..2fb15e55e9 100644 --- a/common/djangoapps/util/password_policy_validators.py +++ b/common/djangoapps/util/password_policy_validators.py @@ -2,18 +2,17 @@ This file exposes a number of password validators which can be optionally added to account creation """ -from __future__ import unicode_literals +from __future__ import absolute_import, unicode_literals import logging import unicodedata -from django.contrib.auth.password_validation import ( - get_default_password_validators, - validate_password as django_validate_password, - MinimumLengthValidator as DjangoMinimumLengthValidator, -) +from django.contrib.auth.password_validation import MinimumLengthValidator as DjangoMinimumLengthValidator +from django.contrib.auth.password_validation import get_default_password_validators +from django.contrib.auth.password_validation import validate_password as django_validate_password from django.core.exceptions import ValidationError -from django.utils.translation import ugettext as _, ungettext +from django.utils.translation import ugettext as _ +from django.utils.translation import ungettext from six import text_type log = logging.getLogger(__name__) diff --git a/common/djangoapps/util/query.py b/common/djangoapps/util/query.py index 6e8182e6a8..0a65f20488 100644 --- a/common/djangoapps/util/query.py +++ b/common/djangoapps/util/query.py @@ -1,4 +1,6 @@ """ Utility functions related to database queries """ +from __future__ import absolute_import + from django.conf import settings diff --git a/common/djangoapps/util/testing.py b/common/djangoapps/util/testing.py index 1cf10611ca..d55ad200e8 100644 --- a/common/djangoapps/util/testing.py +++ b/common/djangoapps/util/testing.py @@ -2,12 +2,14 @@ Utility Mixins for unit tests """ +from __future__ import absolute_import + import json import sys from django.conf import settings -from django.urls import clear_url_caches, resolve from django.test import TestCase +from django.urls import clear_url_caches, resolve from mock import patch from util.db import CommitOnSuccessManager, OuterAtomic diff --git a/common/djangoapps/util/url.py b/common/djangoapps/util/url.py index e8ece69c66..e24d6cbc12 100644 --- a/common/djangoapps/util/url.py +++ b/common/djangoapps/util/url.py @@ -2,6 +2,8 @@ Utility functions related to urls. """ +from __future__ import absolute_import + import sys from importlib import import_module diff --git a/common/djangoapps/util/views.py b/common/djangoapps/util/views.py index f1993a790c..2ea29614fe 100644 --- a/common/djangoapps/util/views.py +++ b/common/djangoapps/util/views.py @@ -1,10 +1,13 @@ +from __future__ import absolute_import + import json import logging import sys from functools import wraps from smtplib import SMTPException -import crum +import calc +import crum import zendesk from django.conf import settings from django.contrib.auth.decorators import login_required @@ -16,8 +19,8 @@ from django.views.decorators.csrf import requires_csrf_token from django.views.defaults import server_error from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey +from six.moves import map -import calc import track.views from edxmako.shortcuts import render_to_response, render_to_string from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers @@ -166,7 +169,7 @@ def calculate(request): try: result = calc.evaluator({}, {}, equation) except: - event = {'error': map(str, sys.exc_info()), + event = {'error': list(map(str, sys.exc_info())), 'equation': equation} track.views.server_track(request, 'error:calc', event, page='calc') return HttpResponse(json.dumps({'result': 'Invalid syntax'}))