From 182a177b4e30d3c427fd363489dd7a0f7c63c3eb Mon Sep 17 00:00:00 2001 From: amitvadhel Date: Thu, 11 Jul 2019 00:15:16 +0300 Subject: [PATCH] INCR-411: Updates on Python 3.x --- common/lib/capa/capa/capa_problem.py | 13 ++++--- common/lib/capa/capa/checker.py | 5 ++- common/lib/capa/capa/inputtypes.py | 23 ++++++------ common/lib/capa/capa/registry.py | 2 +- common/lib/capa/capa/responsetypes.py | 50 ++++++++++++++------------- common/lib/capa/capa/util.py | 7 ++-- common/lib/capa/setup.py | 2 ++ 7 files changed, 56 insertions(+), 46 deletions(-) diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 1708f79900..fb572e00fd 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -13,6 +13,8 @@ Main module which shows problems (of "capa" type). This is used by capa_module. """ +from __future__ import absolute_import + import logging import os.path import re @@ -21,6 +23,7 @@ from copy import deepcopy from datetime import datetime from xml.sax.saxutils import unescape +import six from lxml import etree from pytz import UTC @@ -208,7 +211,7 @@ class LoncapaProblem(object): # Run response late_transforms last (see MultipleChoiceResponse) # Sort the responses to be in *_1 *_2 ... order. - responses = self.responders.values() + responses = list(self.responders.values()) responses = sorted(responses, key=lambda resp: int(resp.id[resp.id.rindex('_') + 1:])) for response in responses: if hasattr(response, 'late_transforms'): @@ -492,7 +495,7 @@ class LoncapaProblem(object): answer_ids = [] for response in self.responders.keys(): results = self.responder_answers[response] - answer_ids.append(results.keys()) + answer_ids.append(list(results.keys())) return answer_ids def find_correct_answer_text(self, answer_id): @@ -612,7 +615,7 @@ class LoncapaProblem(object): self.find_answer_text(answer_id, answer) for answer in current_answer ) - elif isinstance(current_answer, basestring) and current_answer.startswith('choice_'): + elif isinstance(current_answer, six.string_types) and current_answer.startswith('choice_'): # Many problem (e.g. checkbox) report "choice_0" "choice_1" etc. # Here we transform it elems = self.tree.xpath('//*[@id="{answer_id}"]//*[@name="{choice_number}"]'.format( @@ -625,7 +628,7 @@ class LoncapaProblem(object): choices_map = dict(input_cls.extract_choices(choicegroup, self.capa_system.i18n, text_only=True)) answer_text = choices_map[current_answer] - elif isinstance(current_answer, basestring): + elif isinstance(current_answer, six.string_types): # Already a string with the answer answer_text = current_answer @@ -903,7 +906,7 @@ class LoncapaProblem(object): Used by get_html. """ - if not isinstance(problemtree.tag, basestring): + if not isinstance(problemtree.tag, six.string_types): # Comment and ProcessingInstruction nodes are not Elements, # and we're ok leaving those behind. # BTW: etree gives us no good way to distinguish these things diff --git a/common/lib/capa/capa/checker.py b/common/lib/capa/capa/checker.py index 25d9bee7e5..a856c1df06 100755 --- a/common/lib/capa/capa/checker.py +++ b/common/lib/capa/capa/checker.py @@ -2,18 +2,17 @@ """ Commandline tool for doing operations on Problems """ -from __future__ import unicode_literals -from __future__ import print_function +from __future__ import absolute_import, print_function, unicode_literals import argparse import logging import sys from io import BytesIO +from calc import UndefinedVariable from mako.lookup import TemplateLookup from path import Path as path -from calc import UndefinedVariable from capa.capa_problem import LoncapaProblem logging.basicConfig(format="%(levelname)s %(message)s") diff --git a/common/lib/capa/capa/inputtypes.py b/common/lib/capa/capa/inputtypes.py index 10bb572456..cdc35a447f 100644 --- a/common/lib/capa/capa/inputtypes.py +++ b/common/lib/capa/capa/inputtypes.py @@ -38,6 +38,8 @@ graded status as'status' # makes sense, but a bunch of problems have markup that assumes block. Bigger TODO: figure out a # general css and layout strategy for capa, document it, then implement it. +from __future__ import absolute_import + import json import logging import re @@ -49,16 +51,17 @@ from datetime import datetime import bleach import html5lib import pyparsing +import six +from calc.preview import latex_preview from lxml import etree from six import text_type -import xqueue_interface -from calc.preview import latex_preview from capa.xqueue_interface import XQUEUE_TIMEOUT from chem import chemcalc from openedx.core.djangolib.markup import HTML, Text from xmodule.stringify import stringify_children +from . import xqueue_interface from .registry import TagRegistry from .util import sanitize_html @@ -82,7 +85,7 @@ class Status(object): } __slots__ = ('classname', '_status', 'display_name', 'display_tooltip') - def __init__(self, status, gettext_func=unicode): + def __init__(self, status, gettext_func=six.text_type): self.classname = self.css_classes.get(status, status) _ = gettext_func names = { @@ -107,7 +110,7 @@ class Status(object): ['incomplete', 'unanswered', 'unsubmitted'], _('Not yet answered.') ) ) - self.display_name = names.get(status, unicode(status)) + self.display_name = names.get(status, six.text_type(status)) self.display_tooltip = tooltips.get(status, u'') self._status = status or '' @@ -253,7 +256,7 @@ class InputTypeBase(object): # Something went wrong: add xml to message, but keep the traceback msg = u"Error in xml '{x}': {err} ".format( x=etree.tostring(xml), err=text_type(err)) - raise Exception, msg, sys.exc_info()[2] + six.reraise(Exception, msg, sys.exc_info()[2]) @classmethod def get_attributes(cls): @@ -341,14 +344,14 @@ class InputTypeBase(object): # Every list should contain the status id status_id = 'status_' + self.input_id descriptions.append(status_id) - descriptions.extend(self.response_data.get('descriptions', {}).keys()) + descriptions.extend(list(self.response_data.get('descriptions', {}).keys())) description_ids = ' '.join(descriptions) context.update( {'describedby_html': HTML('aria-describedby="{}"').format(description_ids)} ) context.update( - (a, v) for (a, v) in self.loaded_attributes.iteritems() if a in self.to_render + (a, v) for (a, v) in six.iteritems(self.loaded_attributes) if a in self.to_render ) context.update(self._extra_context()) if self.answervariable: @@ -554,7 +557,7 @@ class ChoiceGroup(InputTypeBase): return choices def get_user_visible_answer(self, internal_answer): - if isinstance(internal_answer, basestring): + if isinstance(internal_answer, six.string_types): return self._choices_map[internal_answer] return [self._choices_map[i] for i in internal_answer] @@ -689,7 +692,7 @@ class TextLine(InputTypeBase): 'class_name': self.loaded_attributes['preprocessorClassName'], 'script_src': self.loaded_attributes['preprocessorSrc'], } - if None in self.preprocessor.values(): + if None in list(self.preprocessor.values()): self.preprocessor = None def _extra_context(self): @@ -1594,7 +1597,7 @@ class AnnotationInput(InputTypeBase): d = {} comment_value = d.get('comment', '') - if not isinstance(comment_value, basestring): + if not isinstance(comment_value, six.string_types): comment_value = '' options_value = d.get('options', []) diff --git a/common/lib/capa/capa/registry.py b/common/lib/capa/capa/registry.py index af2156a686..1f0674771f 100644 --- a/common/lib/capa/capa/registry.py +++ b/common/lib/capa/capa/registry.py @@ -50,7 +50,7 @@ class TagRegistry(object): """ Get a list of all the tags that have been registered. """ - return self._mapping.keys() + return list(self._mapping.keys()) def get_class_for_tag(self, tag): """ diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 46e4c08013..a69f382f88 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -10,6 +10,8 @@ Used by capa_problem.py # pylint: disable=attribute-defined-outside-init # standard library imports +from __future__ import absolute_import + import abc # TODO: Refactor this code and fix this issue. import cgi @@ -22,6 +24,7 @@ import re import sys import textwrap import traceback +from cmath import isnan from collections import namedtuple from datetime import datetime from sys import float_info @@ -29,18 +32,19 @@ from sys import float_info import html5lib import numpy import requests +import six +# specific library imports +from calc import UndefinedVariable, UnmatchedParenthesis, evaluator from lxml import etree from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME? from pyparsing import ParseException from pytz import UTC from shapely.geometry import MultiPoint, Point from six import text_type +from six.moves import map, range, zip import capa.safe_exec as safe_exec import capa.xqueue_interface as xqueue_interface -# specific library imports -from calc import UndefinedVariable, UnmatchedParenthesis, evaluator -from cmath import isnan from openedx.core.djangolib.markup import HTML, Text from . import correctmap @@ -103,7 +107,7 @@ class StudentInputError(Exception): # Main base class for CAPA responsetypes -class LoncapaResponse(object): +class LoncapaResponse(six.with_metaclass(abc.ABCMeta, object)): """ Base class for CAPA responsetypes. Each response type (ie a capa question, which is part of a capa problem) is represented as a subclass, @@ -140,7 +144,6 @@ class LoncapaResponse(object): - hint_tag : xhtml tag identifying hint associated with this response inside hintgroup """ - __metaclass__ = abc.ABCMeta # abc = Abstract Base Class tags = None hint_tag = None @@ -179,14 +182,14 @@ class LoncapaResponse(object): for abox in inputfields: if abox.tag not in self.allowed_inputfields: msg = "%s: cannot have input field %s" % ( - unicode(self), abox.tag) + six.text_type(self), abox.tag) msg += "\nSee XML source line %s" % getattr( xml, 'sourceline', '[unavailable]') raise LoncapaProblemError(msg) if self.max_inputfields and len(inputfields) > self.max_inputfields: msg = "%s: cannot have more than %s input fields" % ( - unicode(self), self.max_inputfields) + six.text_type(self), self.max_inputfields) msg += "\nSee XML source line %s" % getattr( xml, 'sourceline', '[unavailable]') raise LoncapaProblemError(msg) @@ -194,7 +197,7 @@ class LoncapaResponse(object): for prop in self.required_attributes: if not xml.get(prop): msg = "Error in problem specification: %s missing required attribute %s" % ( - unicode(self), prop) + six.text_type(self), prop) msg += "\nSee XML source line %s" % getattr( xml, 'sourceline', '[unavailable]') raise LoncapaProblemError(msg) @@ -1455,10 +1458,10 @@ class OptionResponse(LoncapaResponse): Return student answers variable name if exist in context else None. """ if aid in student_answers: - for key, val in self.context.iteritems(): + for key, val in six.iteritems(self.context): # convert val into unicode because student answer always be a unicode string # even it is a list, dict etc. - if unicode(val) == student_answers[aid]: + if six.text_type(val) == student_answers[aid]: return '$' + key return None @@ -2170,7 +2173,7 @@ class CustomResponse(LoncapaResponse): """ _ = self.capa_system.i18n.ugettext - log.debug('%s: student_answers=%s', unicode(self), student_answers) + log.debug('%s: student_answers=%s', six.text_type(self), student_answers) # ordered list of answer id's # sort the responses on the bases of the problem's position number @@ -2284,7 +2287,7 @@ class CustomResponse(LoncapaResponse): def execute_check_function(self, idset, submission): # exec the check function - if isinstance(self.code, basestring): + if isinstance(self.code, six.string_types): try: safe_exec.safe_exec( self.code, @@ -2897,7 +2900,7 @@ class ExternalResponse(LoncapaResponse): # no stanza; get code from