diff --git a/common/lib/xmodule/xmodule/annotatable_module.py b/common/lib/xmodule/xmodule/annotatable_module.py index 3d376eb22055b571ae32b678910bcbb1b3e48671..1c7a80122bdf2c9b5b380e9182ff7d47a5359f00 100644 --- a/common/lib/xmodule/xmodule/annotatable_module.py +++ b/common/lib/xmodule/xmodule/annotatable_module.py @@ -46,7 +46,7 @@ class AnnotatableBlock( data = String( help=_("XML data for the annotation"), scope=Scope.content, - default=textwrap.dedent(HTML(u""" + default=textwrap.dedent(HTML(""" <annotatable> <instructions> <p>Enter your (optional) instructions for the exercise in HTML format.</p> diff --git a/common/lib/xmodule/xmodule/annotator_mixin.py b/common/lib/xmodule/xmodule/annotator_mixin.py index 6ed36c54cc104475834b7790555a73aa1b77fc10..8e7617e1cabda1e0107eb5bb19f93c319e929992 100644 --- a/common/lib/xmodule/xmodule/annotator_mixin.py +++ b/common/lib/xmodule/xmodule/annotator_mixin.py @@ -3,12 +3,11 @@ Annotations Tool Mixin This file contains global variables and functions used in the various Annotation Tools. """ - +from html.parser import HTMLParser from os.path import basename, splitext +from urllib.parse import urlparse from lxml import etree -from six.moves.html_parser import HTMLParser -from six.moves.urllib.parse import urlparse def get_instructions(xmltree): diff --git a/common/lib/xmodule/xmodule/assetstore/__init__.py b/common/lib/xmodule/xmodule/assetstore/__init__.py index b4ecb63ea0361c6c716c8d521de5840b47675daf..d1c432b45688d7d5b4188c160a7f02835193da4e 100644 --- a/common/lib/xmodule/xmodule/assetstore/__init__.py +++ b/common/lib/xmodule/xmodule/assetstore/__init__.py @@ -8,7 +8,6 @@ from datetime import datetime import dateutil.parser import pytz -import six from contracts import contract, new_contract from lxml import etree from opaque_keys.edx.keys import AssetKey, CourseKey @@ -16,16 +15,13 @@ from opaque_keys.edx.keys import AssetKey, CourseKey new_contract('AssetKey', AssetKey) new_contract('CourseKey', CourseKey) new_contract('datetime', datetime) -new_contract('basestring', six.string_types[0]) -if six.PY2: - new_contract('long', long) # lint-amnesty, pylint: disable=undefined-variable -else: - new_contract('long', int) +new_contract('basestring', (str,)[0]) +new_contract('long', int) new_contract('AssetElement', lambda x: isinstance(x, etree._Element) and x.tag == "asset") # pylint: disable=protected-access new_contract('AssetsElement', lambda x: isinstance(x, etree._Element) and x.tag == "assets") # pylint: disable=protected-access -class AssetMetadata(object): +class AssetMetadata: """ Stores the metadata associated with a particular course asset. The asset metadata gets stored in the modulestore. @@ -50,10 +46,10 @@ class AssetMetadata(object): ASSET_XML_TAG = 'asset' # Top-level directory name in exported course XML which holds asset metadata. - EXPORTED_ASSET_DIR = u'assets' + EXPORTED_ASSET_DIR = 'assets' # Filename of all asset metadata exported as XML. - EXPORTED_ASSET_FILENAME = u'assets.xml' + EXPORTED_ASSET_FILENAME = 'assets.xml' @contract(asset_id='AssetKey', pathname='str|None', internal_name='str|None', @@ -128,7 +124,7 @@ class AssetMetadata(object): Arguments: attr_dict: Prop, val dictionary of all attributes to set. """ - for attr, val in six.iteritems(attr_dict): + for attr, val in attr_dict.items(): if attr in self.ATTRS_ALLOWED_TO_UPDATE: setattr(self, attr, val) else: @@ -238,7 +234,7 @@ class AssetMetadata(object): elif isinstance(value, dict): value = json.dumps(value) else: - value = six.text_type(value) + value = str(value) child.text = value @staticmethod @@ -253,7 +249,7 @@ class AssetMetadata(object): asset.to_xml(asset_node) -class CourseAssetsFromStorage(object): +class CourseAssetsFromStorage: """ Wrapper class for asset metadata lists returned from modulestore storage. """ @@ -304,7 +300,7 @@ class CourseAssetsFromStorage(object): """ Iterates over the items of the asset dict. """ - return six.iteritems(self.asset_md) + return self.asset_md.items() def items(self): """ diff --git a/common/lib/xmodule/xmodule/assetstore/assetmgr.py b/common/lib/xmodule/xmodule/assetstore/assetmgr.py index 442f2dcca033dc38002c35ccf7ba11e105648db6..8f579989df2743a391617e50edbf2387cfc18627 100644 --- a/common/lib/xmodule/xmodule/assetstore/assetmgr.py +++ b/common/lib/xmodule/xmodule/assetstore/assetmgr.py @@ -43,7 +43,7 @@ class AssetMetadataFoundTemporary(AssetException): pass # lint-amnesty, pylint: disable=unnecessary-pass -class AssetManager(object): +class AssetManager: """ Manager for saving/loading course assets. """ diff --git a/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py b/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py index c647e952af3c9607a7984549c970ab8d64dcde1c..eeaa762718cf6063003cf2e02e7126d84b3be870 100644 --- a/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py +++ b/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py @@ -10,7 +10,6 @@ from contracts import ContractNotRespected from lxml import etree from opaque_keys.edx.locator import CourseLocator from path import Path as path -from six.moves import zip from xmodule.assetstore import AssetMetadata from xmodule.modulestore.tests.test_assetstore import AssetStoreTestData @@ -22,7 +21,7 @@ class TestAssetXml(unittest.TestCase): """ def setUp(self): - super(TestAssetXml, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() xsd_filename = "assets.xsd" diff --git a/common/lib/xmodule/xmodule/capa_base.py b/common/lib/xmodule/xmodule/capa_base.py index f0e0b6703c2cae6fbcf1fa5c35dfb9b4807042e4..614bf1f51a2afae47b5b29e1399b49326a31e298 100644 --- a/common/lib/xmodule/xmodule/capa_base.py +++ b/common/lib/xmodule/xmodule/capa_base.py @@ -12,13 +12,11 @@ import struct import sys import traceback -import six from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.utils.encoding import smart_text from django.utils.functional import cached_property from pytz import utc -from six import text_type from xblock.fields import Boolean, Dict, Float, Integer, Scope, String, XMLString from xblock.scorable import ScorableXBlockMixin, Score @@ -51,7 +49,7 @@ except ImproperlyConfigured: FEATURES = {} -class SHOWANSWER(object): +class SHOWANSWER: """ Constants for when to show answer """ @@ -69,7 +67,7 @@ class SHOWANSWER(object): ATTEMPTED_NO_PAST_DUE = "attempted_no_past_due" -class RANDOMIZATION(object): +class RANDOMIZATION: """ Constants for problem randomization """ @@ -88,8 +86,8 @@ def randomization_bin(seed, problem_id): we'll combine the system's per-student seed with the problem id in picking the bin. """ r_hash = hashlib.sha1() - r_hash.update(six.b(str(seed))) - r_hash.update(six.b(str(problem_id))) + r_hash.update(str(seed).encode()) + r_hash.update(str(problem_id).encode()) # get the first few digits of the hash, convert to an int, then mod. return int(r_hash.hexdigest()[:7], 16) % NUM_RANDOMIZATION_BINS @@ -117,11 +115,11 @@ class ComplexEncoder(json.JSONEncoder): Print a nicely formatted complex number, or default to the JSON encoder """ if isinstance(obj, complex): - return u"{real:.7g}{imag:+.7g}*j".format(real=obj.real, imag=obj.imag) + return f"{obj.real:.7g}{obj.imag:+.7g}*j" return json.JSONEncoder.default(self, obj) -class CapaFields(object): +class CapaFields: """ Define the possible fields for a Capa problem """ @@ -298,9 +296,9 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): try: lcp = self.new_lcp(self.get_state_for_lcp()) except Exception as err: # pylint: disable=broad-except - msg = u'cannot create LoncapaProblem {loc}: {err}'.format( - loc=text_type(self.location), err=err) - six.reraise(Exception, Exception(msg), sys.exc_info()[2]) + msg = 'cannot create LoncapaProblem {loc}: {err}'.format( + loc=str(self.location), err=err) + raise Exception(msg).with_traceback(sys.exc_info()[2]) if self.score is None: self.set_score(self.score_from_lcp(lcp)) @@ -316,7 +314,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): self.seed = 1 elif self.rerandomize == RANDOMIZATION.PER_STUDENT and hasattr(self.runtime, 'seed'): # see comment on randomization_bin - self.seed = randomization_bin(self.runtime.seed, six.text_type(self.location).encode('utf-8')) + self.seed = randomization_bin(self.runtime.seed, str(self.location).encode('utf-8')) else: self.seed = struct.unpack('i', os.urandom(4))[0] @@ -437,7 +435,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): return self.runtime.render_template('problem_ajax.html', { 'element_id': self.location.html_id(), - 'id': text_type(self.location), + 'id': str(self.location), 'ajax_url': self.ajax_url, 'current_score': curr_score, 'total_possible': total_possible, @@ -447,16 +445,16 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): }) def handle_fatal_lcp_error(self, error): # lint-amnesty, pylint: disable=missing-function-docstring - log.exception(u"LcpFatalError Encountered for {block}".format(block=str(self.location))) + log.exception("LcpFatalError Encountered for {block}".format(block=str(self.location))) if error: return( - HTML(u'<p>Error formatting HTML for problem:</p><p><pre style="color:red">{msg}</pre></p>').format( - msg=text_type(error)) + HTML('<p>Error formatting HTML for problem:</p><p><pre style="color:red">{msg}</pre></p>').format( + msg=str(error)) ) else: return HTML( - u'<p>Could not format HTML for problem. ' - u'Contact course staff in the discussion forum for assistance.</p>' + '<p>Could not format HTML for problem. ' + 'Contact course staff in the discussion forum for assistance.</p>' ) def submit_button_name(self): @@ -565,24 +563,24 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): `err` is the Exception encountered while rendering the problem HTML. """ problem_display_name = self.display_name_with_default - problem_location = text_type(self.location) + problem_location = str(self.location) log.exception( - u"ProblemGetHtmlError: %r, %r, %s", + "ProblemGetHtmlError: %r, %r, %s", problem_display_name, problem_location, - text_type(err) + str(err) ) # TODO (vshnayder): another switch on DEBUG. if self.runtime.DEBUG: msg = HTML( - u'[courseware.capa.capa_module] ' - u'Failed to generate HTML for problem {url}' + '[courseware.capa.capa_module] ' + 'Failed to generate HTML for problem {url}' ).format( - url=text_type(self.location) + url=str(self.location) ) - msg += HTML(u'<p>Error:</p><p><pre>{msg}</pre></p>').format(msg=text_type(err)) - msg += HTML(u'<p><pre>{tb}</pre></p>').format(tb=traceback.format_exc()) + msg += HTML('<p>Error:</p><p><pre>{msg}</pre></p>').format(msg=str(err)) + msg += HTML('<p><pre>{tb}</pre></p>').format(tb=traceback.format_exc()) html = msg else: @@ -630,10 +628,10 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): except Exception as error: # Couldn't do it. Give up. log.exception( - u"ProblemGetHtmlError: Unable to generate html from LoncapaProblem: %r, %r, %s", + "ProblemGetHtmlError: Unable to generate html from LoncapaProblem: %r, %r, %s", problem_display_name, problem_location, - text_type(error) + str(error) ) raise @@ -696,7 +694,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): # Log this demand-hint request. Note that this only logs the last hint requested (although now # all previously shown hints are still displayed). event_info = dict() - event_info['module_id'] = text_type(self.location) + event_info['module_id'] = str(self.location) event_info['hint_index'] = hint_index event_info['hint_len'] = len(demand_hints) event_info['hint_text'] = get_inner_html_from_xpath(demand_hints[hint_index]) @@ -759,12 +757,12 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): save_message = None if self.has_saved_answers: save_message = _( - u"Your answers were previously saved. Click '{button_name}' to grade them." + "Your answers were previously saved. Click '{button_name}' to grade them." ).format(button_name=self.submit_button_name()) context = { 'problem': content, - 'id': text_type(self.location), + 'id': str(self.location), 'short_id': self.location.html_id(), 'submit_button': submit_button, 'submit_button_submitting': submit_button_submitting, @@ -786,7 +784,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): html = self.runtime.render_template('problem.html', context) if encapsulate: - html = HTML(u'<div id="problem_{id}" class="problem" data-url="{ajax_url}">{html}</div>').format( + html = HTML('<div id="problem_{id}" class="problem" data-url="{ajax_url}">{html}</div>').format( id=self.location.html_id(), ajax_url=self.ajax_url, html=HTML(html) ) @@ -876,7 +874,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): 'correcthint', 'regexphint', 'additional_answer', 'stringequalhint', 'compoundhint', 'stringequalhint'] for tag in tags: - html = re.sub(r'<%s.*?>.*?</%s>' % (tag, tag), '', html, flags=re.DOTALL) # xss-lint: disable=python-interpolate-html # lint-amnesty, pylint: disable=line-too-long + html = re.sub(fr'<{tag}.*?>.*?</{tag}>', '', html, flags=re.DOTALL) # xss-lint: disable=python-interpolate-html # lint-amnesty, pylint: disable=line-too-long # Some of these tags span multiple lines # Note: could probably speed this up by calling sub() once with a big regex # vs. simply calling sub() many times as we have here. @@ -1064,7 +1062,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): (and also screen reader text). """ event_info = dict() - event_info['problem_id'] = text_type(self.location) + event_info['problem_id'] = str(self.location) self.track_function_unmask('showanswer', event_info) if not self.answer_available(): # lint-amnesty, pylint: disable=no-else-raise raise NotFoundError('Answer is not available') @@ -1084,7 +1082,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): answer_content = self.runtime.replace_jump_to_id_urls(answer_content) new_answer = {answer_id: answer_content} except TypeError: - log.debug(u'Unable to perform URL substitution on answers[%s]: %s', + log.debug('Unable to perform URL substitution on answers[%s]: %s', answer_id, answers[answer_id]) new_answer = {answer_id: answers[answer_id]} new_answers.update(new_answer) @@ -1156,7 +1154,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): # will return (key, '', '') # We detect this and raise an error if not name: # lint-amnesty, pylint: disable=no-else-raise - raise ValueError(u"{key} must contain at least one underscore".format(key=key)) + raise ValueError(f"{key} must contain at least one underscore") else: # This allows for answers which require more than one value for @@ -1177,7 +1175,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): # If the submission wasn't deserializable, raise an error. except(KeyError, ValueError): raise ValueError( # lint-amnesty, pylint: disable=raise-missing-from - u"Invalid submission: {val} for {key}".format(val=data[key], key=key) + "Invalid submission: {val} for {key}".format(val=data[key], key=key) ) else: val = data[key] @@ -1185,7 +1183,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): # If the name already exists, then we don't want # to override it. Raise an error instead if name in answers: # lint-amnesty, pylint: disable=no-else-raise - raise ValueError(u"Key {name} already exists in answers dict".format(name=name)) + raise ValueError(f"Key {name} already exists in answers dict") else: answers[name] = val @@ -1220,14 +1218,14 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): """ event_info = dict() event_info['state'] = self.lcp.get_state() - event_info['problem_id'] = text_type(self.location) + event_info['problem_id'] = str(self.location) self.lcp.has_saved_answers = False answers = self.make_dict_of_responses(data) answers_without_files = convert_files_to_filenames(answers) event_info['answers'] = answers_without_files - metric_name = u'capa.check_problem.{}'.format # lint-amnesty, pylint: disable=unused-variable + metric_name = 'capa.check_problem.{}'.format # lint-amnesty, pylint: disable=unused-variable # Can override current time current_time = datetime.datetime.now(utc) if override_time is not False: @@ -1239,7 +1237,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): if self.closed(): log.error( 'ProblemClosedError: Problem %s, close date: %s, due:%s, is_past_due: %s, attempts: %s/%s,', - text_type(self.location), + str(self.location), self.close_date, self.due, self.is_past_due(), @@ -1263,7 +1261,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): waittime_between_requests = self.runtime.xqueue['waittime'] if (current_time - prev_submit_time).total_seconds() < waittime_between_requests: - msg = _(u"You must wait at least {wait} seconds between submissions.").format( + msg = _("You must wait at least {wait} seconds between submissions.").format( wait=waittime_between_requests) return {'success': msg, 'html': ''} @@ -1272,7 +1270,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): seconds_since_submission = (current_time - self.last_submission_time).total_seconds() if seconds_since_submission < self.submission_wait_seconds: remaining_secs = int(self.submission_wait_seconds - seconds_since_submission) - msg = _(u'You must wait at least {wait_secs} between submissions. {remaining_secs} remaining.').format( + msg = _('You must wait at least {wait_secs} between submissions. {remaining_secs} remaining.').format( wait_secs=self.pretty_print_seconds(self.submission_wait_seconds), remaining_secs=self.pretty_print_seconds(remaining_secs)) return { @@ -1308,7 +1306,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): # the full exception, including traceback, # in the response if self.runtime.user_is_staff: - msg = u"Staff debug info: {tb}".format(tb=traceback.format_exc()) + msg = f"Staff debug info: {traceback.format_exc()}" # Otherwise, display just an error message, # without a stack trace @@ -1328,8 +1326,8 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): self.set_score(self.score_from_lcp(self.lcp)) if self.runtime.DEBUG: - msg = u"Error checking problem: {}".format(text_type(err)) - msg += u'\nTraceback:\n{}'.format(traceback.format_exc()) + msg = "Error checking problem: {}".format(str(err)) + msg += f'\nTraceback:\n{traceback.format_exc()}' return {'success': msg} raise published_grade = self.publish_grade() @@ -1481,14 +1479,14 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): strings ''. """ input_metadata = {} - for input_id, internal_answer in six.iteritems(answers): + for input_id, internal_answer in answers.items(): answer_input = self.lcp.inputs.get(input_id) if answer_input is None: log.warning('Input id %s is not mapped to an input type.', input_id) answer_response = None - for responder in six.itervalues(self.lcp.responders): + for responder in self.lcp.responders.values(): if input_id in responder.answer_ids: answer_response = responder @@ -1533,7 +1531,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): """ event_info = dict() event_info['state'] = self.lcp.get_state() - event_info['problem_id'] = text_type(self.location) + event_info['problem_id'] = str(self.location) answers = self.make_dict_of_responses(data) event_info['answers'] = answers @@ -1593,7 +1591,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): """ event_info = dict() event_info['old_state'] = self.lcp.get_state() - event_info['problem_id'] = text_type(self.location) + event_info['problem_id'] = str(self.location) _ = self.runtime.service(self, "i18n").ugettext if self.closed(): @@ -1658,7 +1656,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): Returns the error messages for exceptions occurring while performing the rescoring, rather than throwing them. """ - event_info = {'state': self.lcp.get_state(), 'problem_id': text_type(self.location)} + event_info = {'state': self.lcp.get_state(), 'problem_id': str(self.location)} _ = self.runtime.service(self, "i18n").ugettext diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 7b339df708c8b5a6f36ca6fd76de02a3d45f5305..172128d907dbb920d496341ef6d5772c20c37168 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -6,7 +6,6 @@ import logging import re import sys -import six from bleach.sanitizer import Cleaner from lxml import etree from pkg_resources import resource_string @@ -96,7 +95,7 @@ class ProblemBlock( } def bind_for_student(self, *args, **kwargs): # lint-amnesty, pylint: disable=signature-differs - super(ProblemBlock, self).bind_for_student(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().bind_for_student(*args, **kwargs) # Capa was an XModule. When bind_for_student() was called on it with a new runtime, a new CapaModule object # was initialized when XModuleDescriptor._xmodule() was called next. self.lcp was constructed in CapaModule @@ -132,7 +131,7 @@ class ProblemBlock( return self.student_view(context) else: # Show a message that this content requires users to login/enroll. - return super(ProblemBlock, self).public_view(context) # lint-amnesty, pylint: disable=super-with-arguments + return super().public_view(context) def author_view(self, context): """ @@ -189,7 +188,7 @@ class ProblemBlock( ) if dispatch not in handlers: - return 'Error: {} is not a known capa action'.format(dispatch) + return f'Error: {dispatch} is not a known capa action' before = self.get_progress() before_attempts = self.attempts @@ -197,7 +196,7 @@ class ProblemBlock( try: result = handlers[dispatch](data) - except NotFoundError: + except NotFoundError as ex: log.info( "Unable to find data when dispatching %s to %s for user %s", dispatch, @@ -205,9 +204,9 @@ class ProblemBlock( self.scope_ids.user_id ) _, _, traceback_obj = sys.exc_info() - six.reraise(ProcessingError, ProcessingError(not_found_error_message), traceback_obj) + raise ProcessingError(not_found_error_message).with_traceback(traceback_obj) from ex - except Exception: # lint-amnesty, pylint: disable=broad-except + except Exception as ex: # lint-amnesty, pylint: disable=broad-except log.exception( "Unknown error when dispatching %s to %s for user %s", dispatch, @@ -215,7 +214,7 @@ class ProblemBlock( self.scope_ids.user_id ) _, _, traceback_obj = sys.exc_info() - six.reraise(ProcessingError, ProcessingError(generic_error_message), traceback_obj) + raise ProcessingError(generic_error_message).with_traceback(traceback_obj) from ex after = self.get_progress() after_attempts = self.attempts @@ -275,7 +274,7 @@ class ProblemBlock( @property def non_editable_metadata_fields(self): - non_editable_fields = super(ProblemBlock, self).non_editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments + non_editable_fields = super().non_editable_metadata_fields non_editable_fields.extend([ ProblemBlock.due, ProblemBlock.graceperiod, @@ -292,7 +291,7 @@ class ProblemBlock( try: tree = etree.XML(self.data) except etree.XMLSyntaxError: - log.error('Error parsing problem types from xml for capa module {}'.format(self.display_name)) + log.error(f'Error parsing problem types from xml for capa module {self.display_name}') return None # short-term fix to prevent errors (TNL-5057). Will be more properly addressed in TNL-4525. registered_tags = responsetypes.registry.registered_tags() return {node.tag for node in tree.iter() if node.tag in registered_tags} @@ -301,7 +300,7 @@ class ProblemBlock( """ Return dictionary prepared with module content and type for indexing. """ - xblock_body = super(ProblemBlock, self).index_dictionary() # lint-amnesty, pylint: disable=super-with-arguments + xblock_body = super().index_dictionary() # Make optioninput's options index friendly by replacing the actual tag with the values capa_content = re.sub(r'<optioninput options="\(([^"]+)\)".*?>\s*|\S*<\/optioninput>', r'\1', self.data) @@ -384,7 +383,7 @@ class ProblemBlock( minimal_init=True, ) except responsetypes.LoncapaProblemError: - log.exception(u"LcpFatalError for block {} while getting max score".format(str(self.location))) + log.exception("LcpFatalError for block {} while getting max score".format(str(self.location))) maximum_score = 0 else: maximum_score = lcp.get_max_score() diff --git a/common/lib/xmodule/xmodule/conditional_module.py b/common/lib/xmodule/xmodule/conditional_module.py index 2ecfad726a0990ea648ef4176f29cefff9812b7b..8a2d50c63bc18b8f471f1cae965f52d5d38d3a71 100644 --- a/common/lib/xmodule/xmodule/conditional_module.py +++ b/common/lib/xmodule/xmodule/conditional_module.py @@ -6,12 +6,10 @@ ConditionalBlock is an XBlock which you can use for disabling some XBlocks by co import json import logging -import six from lazy import lazy from lxml import etree from opaque_keys.edx.locator import BlockUsageLocator from pkg_resources import resource_string -from six import text_type from web_fragments.fragment import Fragment from xblock.fields import ReferenceList, Scope, String @@ -194,11 +192,11 @@ class ConditionalBlock( """ Create an instance of the Conditional XBlock. """ - super(ConditionalBlock, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) # Convert sources xml_attribute to a ReferenceList field type so Location/Locator # substitution can be done. if not self.sources_list: - if 'sources' in self.xml_attributes and isinstance(self.xml_attributes['sources'], six.string_types): + if 'sources' in self.xml_attributes and isinstance(self.xml_attributes['sources'], str): self.sources_list = [ # TODO: it is not clear why we are replacing the run here (which actually is a no-op # for old-style course locators. However, this is the implementation of @@ -343,7 +341,7 @@ class ConditionalBlock( children = [] show_tag_list = [] definition = {} - for conditional_attr in six.iterkeys(cls.conditions_map): + for conditional_attr in cls.conditions_map: conditional_value = xml_object.get(conditional_attr) if conditional_value is not None: definition.update({ @@ -377,28 +375,28 @@ class ConditionalBlock( self.runtime.add_block_as_child_node(child, xml_object) if self.show_tag_list: - show_str = HTML(u'<show sources="{sources}" />').format( - sources=Text(';'.join(text_type(location) for location in self.show_tag_list))) + show_str = HTML('<show sources="{sources}" />').format( + sources=Text(';'.join(str(location) for location in self.show_tag_list))) xml_object.append(etree.fromstring(show_str)) # Overwrite the original sources attribute with the value from sources_list, as # Locations may have been changed to Locators. - stringified_sources_list = [text_type(loc) for loc in self.sources_list] + stringified_sources_list = [str(loc) for loc in self.sources_list] self.xml_attributes['sources'] = ';'.join(stringified_sources_list) self.xml_attributes[self.conditional_attr] = self.conditional_value self.xml_attributes['message'] = self.conditional_message return xml_object def validate(self): - validation = super(ConditionalBlock, self).validate() # lint-amnesty, pylint: disable=super-with-arguments + validation = super().validate() if not self.sources_list: conditional_validation = StudioValidation(self.location) conditional_validation.add( StudioValidationMessage( StudioValidationMessage.NOT_CONFIGURED, - _(u"This component has no source components configured yet."), + _("This component has no source components configured yet."), action_class='edit-button', - action_label=_(u"Configure list of sources") + action_label=_("Configure list of sources") ) ) validation = StudioValidation.copy(validation) @@ -407,7 +405,7 @@ class ConditionalBlock( @property def non_editable_metadata_fields(self): - non_editable_fields = super(ConditionalBlock, self).non_editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments + non_editable_fields = super().non_editable_metadata_fields non_editable_fields.extend([ ConditionalBlock.due, ConditionalBlock.show_tag_list, diff --git a/common/lib/xmodule/xmodule/contentstore/content.py b/common/lib/xmodule/xmodule/contentstore/content.py index f5cdc1a6315b04c7214e892cf21f6ea988e2dc1a..45884adb4f9e882f9d3a6c9cc3a1116c93aabbc4 100644 --- a/common/lib/xmodule/xmodule/contentstore/content.py +++ b/common/lib/xmodule/xmodule/contentstore/content.py @@ -5,13 +5,12 @@ import os import re import uuid from io import BytesIO +from urllib.parse import parse_qsl, quote_plus, urlencode, urlparse, urlunparse -import six from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import AssetKey, CourseKey from opaque_keys.edx.locator import AssetLocator from PIL import Image -from six.moves.urllib.parse import parse_qsl, quote_plus, urlencode, urlparse, urlunparse from xmodule.assetstore.assetmgr import AssetManager from xmodule.exceptions import NotFoundError @@ -26,7 +25,7 @@ VERSIONED_ASSETS_PREFIX = '/assets/courseware' VERSIONED_ASSETS_PATTERN = r'/assets/courseware/(v[\d]/)?([a-f0-9]{32})' -class StaticContent(object): # lint-amnesty, pylint: disable=missing-class-docstring +class StaticContent: # lint-amnesty, pylint: disable=missing-class-docstring def __init__(self, loc, name, content_type, data, last_modified_at=None, thumbnail_location=None, import_path=None, length=None, locked=False, content_digest=None): self.location = loc @@ -58,13 +57,13 @@ class StaticContent(object): # lint-amnesty, pylint: disable=missing-class-docs name_root, ext = os.path.splitext(original_name) if not ext == extension: - name_root = name_root + ext.replace(u'.', u'-') + name_root = name_root + ext.replace('.', '-') if dimensions: width, height = dimensions - name_root += "-{}x{}".format(width, height) + name_root += f"-{width}x{height}" - return u"{name_root}{extension}".format( + return "{name_root}{extension}".format( name_root=name_root, extension=extension, ) @@ -118,7 +117,7 @@ class StaticContent(object): # lint-amnesty, pylint: disable=missing-class-docs the actual /c4x/... path which the client needs to reference static content """ if location is not None: - return u"/static/{name}".format(name=location.block_id) + return f"/static/{location.block_id}" else: return None @@ -180,9 +179,9 @@ class StaticContent(object): # lint-amnesty, pylint: disable=missing-class-docs if StaticContent.is_versioned_asset_path(path): return path - structure_version = 'v{}'.format(STATIC_CONTENT_VERSION) + structure_version = f'v{STATIC_CONTENT_VERSION}' - return u'{}/{}/{}{}'.format(VERSIONED_ASSETS_PREFIX, structure_version, version, path) + return f'{VERSIONED_ASSETS_PREFIX}/{structure_version}/{version}{path}' @staticmethod def get_asset_key_from_path(course_key, path): @@ -301,7 +300,7 @@ class StaticContent(object): # lint-amnesty, pylint: disable=missing-class-docs Legacy code expects the serialized asset key to start w/ a slash; so, do that in one place :param asset_key: """ - url = six.text_type(asset_key) + url = str(asset_key) if not url.startswith('/'): url = '/' + url # TODO - re-address this once LMS-11198 is tackled. return url @@ -310,9 +309,9 @@ class StaticContent(object): # lint-amnesty, pylint: disable=missing-class-docs class StaticContentStream(StaticContent): # lint-amnesty, pylint: disable=missing-class-docstring def __init__(self, loc, name, content_type, stream, last_modified_at=None, thumbnail_location=None, import_path=None, # lint-amnesty, pylint: disable=line-too-long length=None, locked=False, content_digest=None): - super(StaticContentStream, self).__init__(loc, name, content_type, None, last_modified_at=last_modified_at, # lint-amnesty, pylint: disable=super-with-arguments - thumbnail_location=thumbnail_location, import_path=import_path, - length=length, locked=locked, content_digest=content_digest) + super().__init__(loc, name, content_type, None, last_modified_at=last_modified_at, + thumbnail_location=thumbnail_location, import_path=import_path, + length=length, locked=locked, content_digest=content_digest) self._stream = stream def stream_data(self): @@ -349,7 +348,7 @@ class StaticContentStream(StaticContent): # lint-amnesty, pylint: disable=missi return content -class ContentStore(object): +class ContentStore: ''' Abstraction for all ContentStore providers (e.g. MongoDB) ''' @@ -459,7 +458,7 @@ class ContentStore(object): except Exception as exc: # pylint: disable=broad-except # log and continue as thumbnails are generally considered as optional logging.exception( - u"Failed to generate thumbnail for {0}. Exception: {1}".format(content.location, str(exc)) + "Failed to generate thumbnail for {}. Exception: {}".format(content.location, str(exc)) ) return thumbnail_content, thumbnail_file_location diff --git a/common/lib/xmodule/xmodule/contentstore/mongo.py b/common/lib/xmodule/xmodule/contentstore/mongo.py index 4888a31ce6cf8ab1436f7082ecfefeb8639b0ebf..2a9848bc2a620e01e4ea5b717102bc9cb8916b0c 100644 --- a/common/lib/xmodule/xmodule/contentstore/mongo.py +++ b/common/lib/xmodule/xmodule/contentstore/mongo.py @@ -8,7 +8,6 @@ import os import gridfs import pymongo -import six from bson.son import SON from fs.osfs import OSFS from gridfs.errors import NoFile, FileExists @@ -93,7 +92,7 @@ class MongoContentStore(ContentStore): self.delete(content_id) # delete is a noop if the entry doesn't exist; so, don't waste time checking thumbnail_location = content.thumbnail_location.to_deprecated_list_repr() if content.thumbnail_location else None # lint-amnesty, pylint: disable=line-too-long - with self.fs.new_file(_id=content_id, filename=six.text_type(content.location), content_type=content.content_type, # lint-amnesty, pylint: disable=line-too-long + with self.fs.new_file(_id=content_id, filename=str(content.location), content_type=content.content_type, # lint-amnesty, pylint: disable=line-too-long displayname=content.name, content_son=content_son, thumbnail_location=thumbnail_location, import_path=content.import_path, @@ -103,14 +102,14 @@ class MongoContentStore(ContentStore): # It seems that this code thought that only some specific object would have the `__iter__` attribute # but many more objects have this in python3 and shouldn't be using the chunking logic. For string and # byte streams we write them directly to gridfs and convert them to byetarrys if necessary. - if hasattr(content.data, '__iter__') and not isinstance(content.data, (six.binary_type, six.string_types)): + if hasattr(content.data, '__iter__') and not isinstance(content.data, (bytes, (str,))): for chunk in content.data: fp.write(chunk) else: # Ideally we could just ensure that we don't get strings in here and only byte streams # but being confident of that wolud be a lot more work than we have time for so we just # handle both cases here. - if isinstance(content.data, six.text_type): + if isinstance(content.data, str): fp.write(content.data.encode('utf-8')) else: fp.write(content.data) @@ -217,7 +216,7 @@ class MongoContentStore(ContentStore): # When debugging course exports, this might be a good place # to look. -- pmitros self.export(asset['asset_key'], output_directory) - for attr, value in six.iteritems(asset): + for attr, value in asset.items(): if attr not in ['_id', 'md5', 'uploadDate', 'length', 'chunkSize', 'asset_key']: policy.setdefault(asset['asset_key'].block_id, {})[attr] = value @@ -240,9 +239,9 @@ class MongoContentStore(ContentStore): assets_to_delete = 0 for prefix in ['_id', 'content_son']: query = SON([ - ('{}.tag'.format(prefix), XASSET_LOCATION_TAG), - ('{}.category'.format(prefix), 'asset'), - ('{}.name'.format(prefix), {'$regex': ASSET_IGNORE_REGEX}), + (f'{prefix}.tag', XASSET_LOCATION_TAG), + (f'{prefix}.category', 'asset'), + (f'{prefix}.name', {'$regex': ASSET_IGNORE_REGEX}), ]) items = self.fs_files.find(query) for asset in items: @@ -375,9 +374,9 @@ class MongoContentStore(ContentStore): :param location: a c4x asset location """ - for attr in six.iterkeys(attr_dict): + for attr in attr_dict.keys(): if attr in ['_id', 'md5', 'uploadDate', 'length']: - raise AttributeError("{} is a protected attribute.".format(attr)) + raise AttributeError(f"{attr} is a protected attribute.") asset_db_key, __ = self.asset_db_key(location) # catch upsert error and raise NotFoundError if asset doesn't exist result = self.fs_files.update_one({'_id': asset_db_key}, {"$set": attr_dict}, upsert=False) @@ -413,7 +412,7 @@ class MongoContentStore(ContentStore): asset_key = self.make_id_son(asset) # don't convert from string until fs access source_content = self.fs.get(asset_key) - if isinstance(asset_key, six.string_types): + if isinstance(asset_key, str): asset_key = AssetKey.from_string(asset_key) __, asset_key = self.asset_db_key(asset_key) # Need to replace dict IDs with SON for chunk lookup to work under Python 3 @@ -428,7 +427,7 @@ class MongoContentStore(ContentStore): asset_id = asset_key else: # add the run, since it's the last field, we're golden asset_key['run'] = dest_course_key.run - asset_id = six.text_type( + asset_id = str( dest_course_key.make_asset_key(asset_key['category'], asset_key['name']).for_branch(None) ) try: @@ -496,7 +495,7 @@ class MongoContentStore(ContentStore): # NOTE, there's no need to state that run doesn't exist in the negative case b/c access via # SON requires equivalence (same keys and values in exact same order) dbkey['run'] = location.run - content_id = six.text_type(location.for_branch(None)) + content_id = str(location.for_branch(None)) return content_id, dbkey def make_id_son(self, fs_entry): @@ -506,7 +505,7 @@ class MongoContentStore(ContentStore): fs_entry: the element returned by self.fs_files.find """ _id_field = fs_entry.get('_id', fs_entry) - if isinstance(_id_field, six.string_types): + if isinstance(_id_field, str): return _id_field dbkey = SON((field_name, _id_field.get(field_name)) for field_name in self.ordered_key_fields) if 'run' in _id_field: @@ -613,14 +612,14 @@ def query_for_course(course_key, category=None): else: prefix = 'content_son' dbkey = SON([ - ('{}.tag'.format(prefix), XASSET_LOCATION_TAG), - ('{}.org'.format(prefix), course_key.org), - ('{}.course'.format(prefix), course_key.course), + (f'{prefix}.tag', XASSET_LOCATION_TAG), + (f'{prefix}.org', course_key.org), + (f'{prefix}.course', course_key.course), ]) if category: - dbkey['{}.category'.format(prefix)] = category + dbkey[f'{prefix}.category'] = category if getattr(course_key, 'deprecated', False): - dbkey['{}.run'.format(prefix)] = {'$exists': False} + dbkey[f'{prefix}.run'] = {'$exists': False} else: - dbkey['{}.run'.format(prefix)] = course_key.run + dbkey[f'{prefix}.run'] = course_key.run return dbkey diff --git a/common/lib/xmodule/xmodule/contentstore/utils.py b/common/lib/xmodule/xmodule/contentstore/utils.py index 1a9b91a951931cd0e7845d3384efe2c80d3d310d..002e83969d9380439eda0d6424992956e3907729 100644 --- a/common/lib/xmodule/xmodule/contentstore/utils.py +++ b/common/lib/xmodule/xmodule/contentstore/utils.py @@ -15,13 +15,13 @@ def empty_asset_trashcan(course_locs): # first delete all of the thumbnails thumbs = store.get_all_content_thumbnails_for_course(course_loc) for thumb in thumbs: - print("Deleting {0}...".format(thumb)) + print(f"Deleting {thumb}...") store.delete(thumb['asset_key']) # then delete all of the assets assets, __ = store.get_all_content_for_course(course_loc) for asset in assets: - print("Deleting {0}...".format(asset)) + print(f"Deleting {asset}...") store.delete(asset['asset_key']) diff --git a/common/lib/xmodule/xmodule/course_metadata_utils.py b/common/lib/xmodule/xmodule/course_metadata_utils.py index 11592ded24c356e6a72b7df8efb80d372687a5de..b724f7c2295447511d5b57e65e86e2ebb27b95f6 100644 --- a/common/lib/xmodule/xmodule/course_metadata_utils.py +++ b/common/lib/xmodule/xmodule/course_metadata_utils.py @@ -12,7 +12,6 @@ from datetime import datetime, timedelta from math import exp import dateutil.parser -import six from pytz import utc DEFAULT_START_DATE = datetime(2030, 1, 1, tzinfo=utc) @@ -66,7 +65,7 @@ def clean_course_key(course_key, padding_char): padding_char (str): Character used for padding at end of the encoded string. The standard value for this is '='. """ - encoded = b32encode(six.text_type(course_key).encode('utf8')).decode('utf8') + encoded = b32encode(str(course_key).encode('utf8')).decode('utf8') return "course_{}".format( encoded.replace('=', padding_char) ) diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index 320a8093ca2e61afca40ce2b3a5a27916f02ef6d..54af7e72977daf694553455383d5eca9b15aaf29 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -10,14 +10,12 @@ from io import BytesIO import dateutil.parser import requests -import six from django.conf import settings from django.core.validators import validate_email from lazy import lazy from lxml import etree from path import Path as path from pytz import utc -from six import text_type from xblock.fields import Boolean, Dict, Float, Integer, List, Scope, String from openedx.core.djangoapps.video_pipeline.models import VideoUploadsEnabledByDefault from openedx.core.lib.license import LicenseMixin @@ -63,7 +61,7 @@ class StringOrDate(Date): # lint-amnesty, pylint: disable=missing-class-docstri if present, assume it's a string if it doesn't parse. """ try: - result = super(StringOrDate, self).from_json(value) # lint-amnesty, pylint: disable=super-with-arguments + result = super().from_json(value) except ValueError: return value if result is None: @@ -76,7 +74,7 @@ class StringOrDate(Date): # lint-amnesty, pylint: disable=missing-class-docstri Convert a time struct or string to a string. """ try: - result = super(StringOrDate, self).to_json(value) # lint-amnesty, pylint: disable=super-with-arguments + result = super().to_json(value) except: # lint-amnesty, pylint: disable=bare-except return value if result is None: @@ -103,7 +101,7 @@ edx_xml_parser = etree.XMLParser(dtd_validation=False, load_dtd=False, _cached_toc = {} -class Textbook(object): # lint-amnesty, pylint: disable=missing-class-docstring +class Textbook: # lint-amnesty, pylint: disable=missing-class-docstring,eq-without-hash def __init__(self, title, book_url): self.title = title self.book_url = book_url @@ -154,7 +152,7 @@ class Textbook(object): # lint-amnesty, pylint: disable=missing-class-docstring try: r = requests.get(toc_url) except Exception as err: - msg = 'Error %s: Unable to retrieve textbook table of contents at %s' % (err, toc_url) + msg = f'Error {err}: Unable to retrieve textbook table of contents at {toc_url}' log.error(msg) raise Exception(msg) # lint-amnesty, pylint: disable=raise-missing-from @@ -162,7 +160,7 @@ class Textbook(object): # lint-amnesty, pylint: disable=missing-class-docstring try: table_of_contents = etree.fromstring(r.text) except Exception as err: - msg = 'Error %s: Unable to parse XML for textbook table of contents at %s' % (err, toc_url) + msg = f'Error {err}: Unable to parse XML for textbook table of contents at {toc_url}' log.error(msg) raise Exception(msg) # lint-amnesty, pylint: disable=raise-missing-from @@ -185,7 +183,7 @@ class TextbookList(List): # lint-amnesty, pylint: disable=missing-class-docstri except: # lint-amnesty, pylint: disable=bare-except # If we can't get to S3 (e.g. on a train with no internet), don't break # the rest of the courseware. - log.exception("Couldn't load textbook ({0}, {1})".format(title, book_url)) + log.exception(f"Couldn't load textbook ({title}, {book_url})") continue return textbooks @@ -213,7 +211,7 @@ class ProctoringProvider(String): and include any inherited values from the platform default. """ errors = [] - value = super(ProctoringProvider, self).from_json(value) # lint-amnesty, pylint: disable=super-with-arguments + value = super().from_json(value) provider_errors = self._validate_proctoring_provider(value) errors.extend(provider_errors) @@ -263,7 +261,7 @@ class ProctoringProvider(String): """ Return default value for ProctoringProvider. """ - default = super(ProctoringProvider, self).default # lint-amnesty, pylint: disable=super-with-arguments + default = super().default proctoring_backend_settings = getattr(settings, 'PROCTORING_BACKENDS', None) @@ -312,7 +310,7 @@ class TeamsConfigField(Dict): return value.cleaned_data -class CourseFields(object): # lint-amnesty, pylint: disable=missing-class-docstring +class CourseFields: # lint-amnesty, pylint: disable=missing-class-docstring lti_passports = List( display_name=_("LTI Passports"), help=_('Enter the passports for course LTI tools in the following format: "id:client_key:client_secret".'), @@ -1040,6 +1038,9 @@ class CourseBlock( resources_dir = None def __init__(self, *args, **kwargs): + """ + Expects the same arguments as XModuleDescriptor.__init__ + """ super().__init__(*args, **kwargs) _ = self.runtime.service(self, "i18n").ugettext @@ -1076,7 +1077,7 @@ class CourseBlock( if not getattr(self, "tabs", []): CourseTabList.initialize_default(self) except InvalidTabsException as err: - raise type(err)('{msg} For course: {course_id}'.format(msg=text_type(err), course_id=six.text_type(self.id))) # lint-amnesty, pylint: disable=line-too-long + raise type(err)('{msg} For course: {course_id}'.format(msg=str(err), course_id=str(self.id))) # lint-amnesty, pylint: disable=line-too-long self.set_default_certificate_available_date() @@ -1118,14 +1119,14 @@ class CourseBlock( for policy_path in paths: if not system.resources_fs.exists(policy_path): continue - log.debug("Loading grading policy from {0}".format(policy_path)) + log.debug(f"Loading grading policy from {policy_path}") try: with system.resources_fs.open(policy_path) as grading_policy_file: policy_str = grading_policy_file.read() # if we successfully read the file, stop looking at backups break - except IOError: - msg = "Unable to load course settings file from '{0}'".format(policy_path) + except OSError: + msg = f"Unable to load course settings file from '{policy_path}'" log.warning(msg) return policy_str @@ -1136,7 +1137,7 @@ class CourseBlock( # bleh, have to parse the XML here to just pull out the url_name attribute # I don't think it's stored anywhere in the instance. - if isinstance(xml_data, six.text_type): + if isinstance(xml_data, str): xml_data = xml_data.encode('ascii', 'ignore') course_file = BytesIO(xml_data) xml_obj = etree.parse(course_file, parser=edx_xml_parser).getroot() @@ -1144,12 +1145,12 @@ class CourseBlock( policy_dir = None url_name = xml_obj.get('url_name', xml_obj.get('slug')) if url_name: - policy_dir = u'policies/' + url_name + policy_dir = 'policies/' + url_name # Try to load grading policy - paths = [u'grading_policy.json'] + paths = ['grading_policy.json'] if policy_dir: - paths = [policy_dir + u'/grading_policy.json'] + paths + paths = [policy_dir + '/grading_policy.json'] + paths try: policy = json.loads(cls.read_grading_policy(paths, system)) @@ -1366,7 +1367,7 @@ class CourseBlock( return True else: return False - elif isinstance(flag, six.string_types): + elif isinstance(flag, str): return flag.lower() in ['true', 'yes', 'y'] else: return bool(flag) @@ -1557,14 +1558,14 @@ class CourseBlock( return datetime.now(utc) <= self.start -class CourseSummary(object): +class CourseSummary: """ A lightweight course summary class, which constructs split/mongo course summary without loading the course. It is used at cms for listing courses to global staff user. """ course_info_fields = ['display_name', 'display_coursenumber', 'display_organization', 'end'] - def __init__(self, course_locator, display_name=u"Empty", display_coursenumber=None, display_organization=None, + def __init__(self, course_locator, display_name="Empty", display_coursenumber=None, display_organization=None, end=None): """ Initialize and construct course summary @@ -1624,7 +1625,7 @@ class CourseSummary(object): except TypeError as e: log.warning( "Course '{course_id}' has an improperly formatted end date '{end_date}'. Error: '{err}'.".format( - course_id=six.text_type(self.id), end_date=self.end, err=e + course_id=str(self.id), end_date=self.end, err=e ) ) modified_end = self.end.replace(tzinfo=utc) diff --git a/common/lib/xmodule/xmodule/editing_module.py b/common/lib/xmodule/xmodule/editing_module.py index 17b40009e9aab5484ed1673f1a54d748c970aa6c..b8bf994f642f9a489741c4554cac372dc03c9450 100644 --- a/common/lib/xmodule/xmodule/editing_module.py +++ b/common/lib/xmodule/xmodule/editing_module.py @@ -11,7 +11,7 @@ from xmodule.mako_module import MakoModuleDescriptor, MakoTemplateBlockBase log = logging.getLogger(__name__) -class EditingFields(object): +class EditingFields: """Contains specific template information (the raw data body)""" data = String(scope=Scope.content, default='') @@ -32,7 +32,7 @@ class EditingMixin(EditingFields, MakoTemplateBlockBase): """ `data` should not be editable in the Studio settings editor. """ - non_editable_fields = super(EditingMixin, self).non_editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments + non_editable_fields = super().non_editable_metadata_fields non_editable_fields.append(self.fields['data']) return non_editable_fields diff --git a/common/lib/xmodule/xmodule/error_module.py b/common/lib/xmodule/xmodule/error_module.py index 8e3db679bde913483316f02f6500634178c8661f..2cefcc9da9a9182f433437203035d0867b4ad991 100644 --- a/common/lib/xmodule/xmodule/error_module.py +++ b/common/lib/xmodule/xmodule/error_module.py @@ -9,7 +9,6 @@ import json import logging import sys -import six from lxml import etree from web_fragments.fragment import Fragment from xblock.field_data import DictFieldData @@ -35,7 +34,7 @@ log = logging.getLogger(__name__) # decides whether to create a staff or not-staff module. -class ErrorFields(object): +class ErrorFields: """ XBlock fields used by the ErrorBlocks """ @@ -107,7 +106,7 @@ class ErrorBlock( # real metadata stays in the content, but add a display name field_data = DictFieldData({ - 'error_msg': six.text_type(error_msg), + 'error_msg': str(error_msg), 'contents': contents, 'location': location, 'category': 'error' diff --git a/common/lib/xmodule/xmodule/exceptions.py b/common/lib/xmodule/xmodule/exceptions.py index c813dabaf90d9d3105f8e0195d72d8a80e58fc80..3fbc731c488d95b685b72c336f8fb21aad748b22 100644 --- a/common/lib/xmodule/xmodule/exceptions.py +++ b/common/lib/xmodule/xmodule/exceptions.py @@ -21,7 +21,7 @@ class InvalidVersionError(Exception): for a non-leaf node) """ def __init__(self, location): - super(InvalidVersionError, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.location = location @@ -30,7 +30,7 @@ class SerializationError(Exception): Thrown when a module cannot be exported to XML """ def __init__(self, location, msg): - super(SerializationError, self).__init__(msg) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(msg) self.location = location @@ -51,4 +51,4 @@ class HeartbeatFailure(Exception): In addition to a msg, provide the name of the service. """ self.service = service - super(HeartbeatFailure, self).__init__(msg) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(msg) diff --git a/common/lib/xmodule/xmodule/fields.py b/common/lib/xmodule/xmodule/fields.py index 49504bdf2c7734008ce205751b4110f2e65f4b34..ef8b0f84d01615c27df64a45797c03676723a113 100644 --- a/common/lib/xmodule/xmodule/fields.py +++ b/common/lib/xmodule/xmodule/fields.py @@ -6,9 +6,7 @@ import re import time import dateutil.parser -import six from pytz import UTC -from six import text_type from xblock.fields import JSONField from xblock.scorable import Score @@ -37,7 +35,7 @@ class Date(JSONField): result = dateutil.parser.parse(field, default=self.PREVENT_DEFAULT_DAY_MON_SEED1) result_other = dateutil.parser.parse(field, default=self.PREVENT_DEFAULT_DAY_MON_SEED2) if result != result_other: - log.warning("Field {0} is missing month or day".format(self.name)) + log.warning(f"Field {self.name} is missing month or day") return None if result.tzinfo is None: result = result.replace(tzinfo=UTC) @@ -53,16 +51,16 @@ class Date(JSONField): return field elif field == "": return None - elif isinstance(field, six.string_types): + elif isinstance(field, str): return self._parse_date_wo_default_month_day(field) - elif isinstance(field, six.integer_types) or isinstance(field, float): # lint-amnesty, pylint: disable=consider-merging-isinstance + elif isinstance(field, int) or isinstance(field, float): # lint-amnesty, pylint: disable=consider-merging-isinstance return datetime.datetime.fromtimestamp(field / 1000, UTC) elif isinstance(field, time.struct_time): return datetime.datetime.fromtimestamp(time.mktime(field), UTC) elif isinstance(field, datetime.datetime): return field else: - msg = "Field {0} has bad value '{1}'".format( + msg = "Field {} has bad value '{}'".format( self.name, field) raise TypeError(msg) @@ -86,7 +84,7 @@ class Date(JSONField): else: return value.isoformat() else: - raise TypeError("Cannot convert {!r} to json".format(value)) + raise TypeError(f"Cannot convert {value!r} to json") enforce_type = from_json @@ -118,7 +116,7 @@ class Timedelta(JSONField): # lint-amnesty, pylint: disable=missing-class-docst return parts = parts.groupdict() time_params = {} - for (name, param) in six.iteritems(parts): + for (name, param) in parts.items(): if param: time_params[name] = int(param) return datetime.timedelta(**time_params) @@ -179,7 +177,7 @@ class RelativeTime(JSONField): except ValueError as e: raise ValueError( # lint-amnesty, pylint: disable=raise-missing-from "Incorrect RelativeTime value {!r} was set in XML or serialized. " - "Original parse message is {}".format(value, text_type(e)) + "Original parse message is {}".format(value, str(e)) ) return datetime.timedelta( hours=obj_time.tm_hour, @@ -204,10 +202,10 @@ class RelativeTime(JSONField): if isinstance(value, float): return datetime.timedelta(seconds=value) - if isinstance(value, six.string_types): + if isinstance(value, str): return self.isotime_to_timedelta(value) - msg = "RelativeTime Field {0} has bad value '{1!r}'".format(self.name, value) + msg = f"RelativeTime Field {self.name} has bad value '{value!r}'" raise TypeError(msg) def to_json(self, value): @@ -235,7 +233,7 @@ class RelativeTime(JSONField): ) return self.timedelta_to_string(value) - raise TypeError("RelativeTime: cannot convert {!r} to json".format(value)) + raise TypeError(f"RelativeTime: cannot convert {value!r} to json") def timedelta_to_string(self, value): """ @@ -284,7 +282,7 @@ class ScoreField(JSONField): if raw_possible < 0: raise ValueError( - 'Error deserializing field of type {0}: Expected a positive number for raw_possible, got {1}.'.format( + 'Error deserializing field of type {}: Expected a positive number for raw_possible, got {}.'.format( self.display_name, raw_possible, ) @@ -292,7 +290,7 @@ class ScoreField(JSONField): if not (0 <= raw_earned <= raw_possible): # lint-amnesty, pylint: disable=superfluous-parens raise ValueError( - 'Error deserializing field of type {0}: Expected raw_earned between 0 and {1}, got {2}.'.format( + 'Error deserializing field of type {}: Expected raw_earned between 0 and {}, got {}.'.format( self.display_name, raw_possible, raw_earned diff --git a/common/lib/xmodule/xmodule/graders.py b/common/lib/xmodule/xmodule/graders.py index fb47e14699cf128267aaf799113edd21f1ee11a0..1dfead90d1168dd082ce9d84e6e5c9d84b82df69 100644 --- a/common/lib/xmodule/xmodule/graders.py +++ b/common/lib/xmodule/xmodule/graders.py @@ -11,11 +11,9 @@ import sys from collections import OrderedDict from datetime import datetime -import six from contracts import contract from pytz import UTC from django.utils.translation import ugettext_lazy as _ -from six.moves import range from xmodule.util.misc import get_short_labeler @@ -23,7 +21,7 @@ from xmodule.util.misc import get_short_labeler log = logging.getLogger("edx.courseware") -class ScoreBase(six.with_metaclass(abc.ABCMeta, object)): +class ScoreBase(metaclass=abc.ABCMeta): # pylint: disable=eq-without-hash """ Abstract base class for encapsulating fields of values scores. """ @@ -51,7 +49,7 @@ class ScoreBase(six.with_metaclass(abc.ABCMeta, object)): return not self.__eq__(other) def __repr__(self): - return u"{class_name}({fields})".format(class_name=self.__class__.__name__, fields=self.__dict__) + return f"{self.__class__.__name__}({self.__dict__})" class ProblemScore(ScoreBase): @@ -78,7 +76,7 @@ class ProblemScore(ScoreBase): :param weight: Weight of this problem :type weight: int|float|None """ - super(ProblemScore, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.raw_earned = float(raw_earned) if raw_earned is not None else None self.raw_possible = float(raw_possible) if raw_possible is not None else None self.earned = float(weighted_earned) if weighted_earned is not None else None @@ -101,7 +99,7 @@ class AggregatedScore(ScoreBase): :param tw_possible: Total aggregated sum of all weighted possible values :type tw_possible: int|float|None """ - super(AggregatedScore, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.earned = float(tw_earned) if tw_earned is not None else None self.possible = float(tw_possible) if tw_possible is not None else None @@ -180,7 +178,7 @@ def grader_from_conf(conf): bad_args = invalid_args(subgrader_class.__init__, subgraderconf) if bad_args: - log.warning(u"Invalid arguments for a subgrader: %s", bad_args) + log.warning("Invalid arguments for a subgrader: %s", bad_args) for key in bad_args: del subgraderconf[key] @@ -192,12 +190,12 @@ def grader_from_conf(conf): msg = ("Unable to parse grader configuration:\n " + str(subgraderconf) + "\n Error was:\n " + str(error)) - six.reraise(ValueError, ValueError(msg), sys.exc_info()[2]) + raise ValueError(msg).with_traceback(sys.exc_info()[2]) return WeightedSubsectionsGrader(subgraders) -class CourseGrader(six.with_metaclass(abc.ABCMeta, object)): +class CourseGrader(metaclass=abc.ABCMeta): """ A course grader takes the totaled scores for each graded section (that a student has started) in the course. From these scores, the grader calculates an overall percentage @@ -279,7 +277,7 @@ class WeightedSubsectionsGrader(CourseGrader): subgrade_result = subgrader.grade(grade_sheet, generate_random_scores) weighted_percent = subgrade_result['percent'] * weight - section_detail = _(u"{assignment_type} = {weighted_percent:.2%} of a possible {weight:.2%}").format( + section_detail = _("{assignment_type} = {weighted_percent:.2%} of a possible {weight:.2%}").format( assignment_type=assignment_type, weighted_percent=weighted_percent, weight=weight) @@ -393,7 +391,7 @@ class AssignmentFormatGrader(CourseGrader): section_name = scores[i].display_name percentage = scores[i].percent_graded - summary_format = u"{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})" + summary_format = "{section_type} {index} - {name} - {percent:.0%} ({earned:.3n}/{possible:.3n})" summary = summary_format.format( index=i + self.starting_index, section_type=self.section_type, @@ -405,7 +403,7 @@ class AssignmentFormatGrader(CourseGrader): else: percentage = 0.0 # Translators: "Homework 1 - Unreleased - 0% (?/?)" The section has not been released for viewing. - summary = _(u"{section_type} {index} Unreleased - 0% (?/?)").format( + summary = _("{section_type} {index} Unreleased - 0% (?/?)").format( index=i + self.starting_index, section_type=self.section_type ) @@ -418,7 +416,7 @@ class AssignmentFormatGrader(CourseGrader): for dropped_index in dropped_indices: breakdown[dropped_index]['mark'] = { - 'detail': _(u"The lowest {drop_count} {section_type} scores are dropped.").format( + 'detail': _("The lowest {drop_count} {section_type} scores are dropped.").format( drop_count=self.drop_count, section_type=self.section_type ) @@ -427,21 +425,21 @@ class AssignmentFormatGrader(CourseGrader): if len(breakdown) == 1: # if there is only one entry in a section, suppress the existing individual entry and the average, # and just display a single entry for the section. - total_detail = u"{section_type} = {percent:.0%}".format( + total_detail = "{section_type} = {percent:.0%}".format( percent=total_percent, section_type=self.section_type, ) - total_label = u"{short_label}".format(short_label=self.short_label) + total_label = f"{self.short_label}" breakdown = [{'percent': total_percent, 'label': total_label, 'detail': total_detail, 'category': self.category, 'prominent': True}, ] else: # Translators: "Homework Average = 0%" - total_detail = _(u"{section_type} Average = {percent:.0%}").format( + total_detail = _("{section_type} Average = {percent:.0%}").format( percent=total_percent, section_type=self.section_type ) # Translators: Avg is short for Average - total_label = _(u"{short_label} Avg").format(short_label=self.short_label) + total_label = _("{short_label} Avg").format(short_label=self.short_label) if self.show_only_average: breakdown = [] @@ -476,7 +474,7 @@ def _min_or_none(itr): return None -class ShowCorrectness(object): +class ShowCorrectness: """ Helper class for determining whether correctness is currently hidden for a block. diff --git a/common/lib/xmodule/xmodule/html_module.py b/common/lib/xmodule/xmodule/html_module.py index 4489c793305b1b84a0db9ce4ca4c0628929bebd1..b72c61bc02c873aece058b20edfbff63457c1fb8 100644 --- a/common/lib/xmodule/xmodule/html_module.py +++ b/common/lib/xmodule/xmodule/html_module.py @@ -10,7 +10,6 @@ from datetime import datetime from pkg_resources import resource_string -import six from django.conf import settings from fs.errors import ResourceNotFound from lxml import etree @@ -59,7 +58,7 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method # use display_name_with_default for those default=_("Text") ) - data = String(help=_("Html contents to display for this module"), default=u"", scope=Scope.content) + data = String(help=_("Html contents to display for this module"), default="", scope=Scope.content) source_code = String( help=_("Source code for LaTeX documents. This feature is not well-supported."), scope=Scope.settings @@ -111,7 +110,7 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method else: return { 'enabled': False, - 'message': 'To enable, set FEATURES["{}"]'.format(self.ENABLE_HTML_XBLOCK_STUDENT_VIEW_DATA) + 'message': f'To enable, set FEATURES["{self.ENABLE_HTML_XBLOCK_STUDENT_VIEW_DATA}"]' } def get_html(self): @@ -256,7 +255,7 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method ) base = path(pointer_path).dirname() # log.debug("base = {0}, base.dirname={1}, filename={2}".format(base, base.dirname(), filename)) - filepath = u"{base}/{name}.html".format(base=base, name=filename) + filepath = f"{base}/{filename}.html" # log.debug("looking for html file for {0} at {1}".format(location, filepath)) # VS[compat] @@ -278,7 +277,7 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method html = infile.read() # Log a warning if we can't parse the file, but don't error if not check_html(html) and len(html) > 0: - msg = "Couldn't parse html in {0}, content = {1}".format(filepath, html) + msg = f"Couldn't parse html in {filepath}, content = {html}" log.warning(msg) system.error_tracker("Warning: " + msg) @@ -291,10 +290,10 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method return definition, [] except ResourceNotFound as err: - msg = 'Unable to load file contents at path {0}: {1} '.format( + msg = 'Unable to load file contents at path {}: {} '.format( filepath, err) # add more info and re-raise - six.reraise(Exception, Exception(msg), sys.exc_info()[2]) + raise Exception(msg).with_traceback(sys.exc_info()[2]) @classmethod def parse_xml_new_runtime(cls, node, runtime, keys): @@ -319,7 +318,7 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method # Write html to file, return an empty tag pathname = name_to_pathname(self.url_name) - filepath = u'{category}/{pathname}.html'.format( + filepath = '{category}/{pathname}.html'.format( category=self.category, pathname=pathname ) @@ -341,12 +340,12 @@ class HtmlBlockMixin( # lint-amnesty, pylint: disable=abstract-method """ `use_latex_compiler` should not be editable in the Studio settings editor. """ - non_editable_fields = super(HtmlBlockMixin, self).non_editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments + non_editable_fields = super().non_editable_metadata_fields non_editable_fields.append(HtmlBlockMixin.use_latex_compiler) return non_editable_fields def index_dictionary(self): - xblock_body = super(HtmlBlockMixin, self).index_dictionary() # lint-amnesty, pylint: disable=super-with-arguments + xblock_body = super().index_dictionary() # Removing script and style html_content = re.sub( re.compile( @@ -380,7 +379,7 @@ class HtmlBlock(HtmlBlockMixin): # lint-amnesty, pylint: disable=abstract-metho """ -class AboutFields(object): # lint-amnesty, pylint: disable=missing-class-docstring +class AboutFields: # lint-amnesty, pylint: disable=missing-class-docstring display_name = String( help=_("The display name for this component."), scope=Scope.settings, @@ -388,7 +387,7 @@ class AboutFields(object): # lint-amnesty, pylint: disable=missing-class-docstr ) data = String( help=_("Html contents to display for this module"), - default=u"", + default="", scope=Scope.content ) @@ -402,7 +401,7 @@ class AboutBlock(AboutFields, HtmlBlockMixin): # lint-amnesty, pylint: disable= template_dir_name = "about" -class StaticTabFields(object): +class StaticTabFields: """ The overrides for Static Tabs """ @@ -420,7 +419,7 @@ class StaticTabFields(object): scope=Scope.settings ) data = String( - default=textwrap.dedent(u"""\ + default=textwrap.dedent("""\ <p>Add the content you want students to see on this page.</p> """), scope=Scope.content, @@ -437,7 +436,7 @@ class StaticTabBlock(StaticTabFields, HtmlBlockMixin): # lint-amnesty, pylint: template_dir_name = None -class CourseInfoFields(object): +class CourseInfoFields: """ Field overrides """ @@ -448,7 +447,7 @@ class CourseInfoFields(object): ) data = String( help=_("Html contents to display for this module"), - default=u"<ol></ol>", + default="<ol></ol>", scope=Scope.content ) @@ -482,7 +481,7 @@ class CourseInfoBlock(CourseInfoFields, HtmlBlockMixin): # lint-amnesty, pylint 'visible_updates': course_updates[:3], 'hidden_updates': course_updates[3:], } - return self.system.render_template("{0}/course_updates.html".format(self.TEMPLATE_DIR), context) + return self.system.render_template(f"{self.TEMPLATE_DIR}/course_updates.html", context) @classmethod def order_updates(self, updates): # lint-amnesty, pylint: disable=bad-classmethod-argument diff --git a/common/lib/xmodule/xmodule/library_content_module.py b/common/lib/xmodule/xmodule/library_content_module.py index 171fe78d31f49d1dd82ce7faae4b138d0da15ccf..c303f022c73acba7bb94ab311c48c2844e742fd3 100644 --- a/common/lib/xmodule/xmodule/library_content_module.py +++ b/common/lib/xmodule/xmodule/library_content_module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ LibraryContent: The XBlock used to include blocks from a library in a course. """ @@ -10,14 +9,11 @@ import random from copy import copy from gettext import ngettext -import six import bleach from lazy import lazy from lxml import etree from opaque_keys.edx.locator import LibraryLocator from pkg_resources import resource_string -from six import text_type -from six.moves import zip from web_fragments.fragment import Fragment from webob import Response from xblock.completable import XBlockCompletionMode @@ -196,10 +192,10 @@ class LibraryContentBlock( """ rand = random.Random() - selected_keys = set(tuple(k) for k in selected) # set of (block_type, block_id) tuples assigned to this student + selected_keys = {tuple(k) for k in selected} # set of (block_type, block_id) tuples assigned to this student # Determine which of our children we will show: - valid_block_keys = set((c.block_type, c.block_id) for c in children) + valid_block_keys = {(c.block_type, c.block_id) for c in children} # Remove any selected blocks that are no longer valid: invalid_block_keys = (selected_keys - valid_block_keys) @@ -244,13 +240,13 @@ class LibraryContentBlock( Helper method to publish an event for analytics purposes """ event_data = { - "location": six.text_type(self.location), + "location": str(self.location), "result": result, "previous_count": getattr(self, "_last_event_result_count", len(self.selected)), "max_count": self.max_count, } event_data.update(kwargs) - self.runtime.publish(self, "edx.librarycontentblock.content.{}".format(event_name), event_data) + self.runtime.publish(self, f"edx.librarycontentblock.content.{event_name}", event_data) self._last_event_result_count = len(result) # pylint: disable=attribute-defined-outside-init @classmethod @@ -364,7 +360,7 @@ class LibraryContentBlock( rendered_child = displayable.render(STUDENT_VIEW, child_context) fragment.add_fragment_resources(rendered_child) contents.append({ - 'id': text_type(displayable.location), + 'id': str(displayable.location), 'content': rendered_child.content, }) @@ -478,7 +474,7 @@ class LibraryContentBlock( """ Copy any overrides the user has made on blocks in this library. """ - for field in six.itervalues(source.fields): + for field in source.fields.values(): if field.scope == Scope.settings and field.is_set_on(source): setattr(dest, field.name, field.read_from(source)) if source.has_children: @@ -515,16 +511,16 @@ class LibraryContentBlock( """ latest_version = lib_tools.get_library_version(library_key) if latest_version is not None: - if version is None or version != six.text_type(latest_version): + if version is None or version != str(latest_version): validation.set_summary( StudioValidationMessage( StudioValidationMessage.WARNING, - _(u'This component is out of date. The library has new content.'), + _('This component is out of date. The library has new content.'), # TODO: change this to action_runtime_event='...' once the unit page supports that feature. # See https://openedx.atlassian.net/browse/TNL-993 action_class='library-update-btn', # Translators: {refresh_icon} placeholder is substituted to "↻" (without double quotes) - action_label=_(u"{refresh_icon} Update now.").format(refresh_icon=u"↻") + action_label=_("{refresh_icon} Update now.").format(refresh_icon="↻") ) ) return False @@ -532,9 +528,9 @@ class LibraryContentBlock( validation.set_summary( StudioValidationMessage( StudioValidationMessage.ERROR, - _(u'Library is invalid, corrupt, or has been deleted.'), + _('Library is invalid, corrupt, or has been deleted.'), action_class='edit-button', - action_label=_(u"Edit Library List.") + action_label=_("Edit Library List.") ) ) return False @@ -560,8 +556,8 @@ class LibraryContentBlock( StudioValidationMessage( StudioValidationMessage.ERROR, _( - u"This course does not support content libraries. " - u"Contact your system administrator for more information." + "This course does not support content libraries. " + "Contact your system administrator for more information." ) ) ) @@ -570,9 +566,9 @@ class LibraryContentBlock( validation.set_summary( StudioValidationMessage( StudioValidationMessage.NOT_CONFIGURED, - _(u"A library has not yet been selected."), + _("A library has not yet been selected."), action_class='edit-button', - action_label=_(u"Select a Library.") + action_label=_("Select a Library.") ) ) return validation @@ -587,9 +583,9 @@ class LibraryContentBlock( validation, StudioValidationMessage( StudioValidationMessage.WARNING, - _(u'There are no matching problem types in the specified libraries.'), + _('There are no matching problem types in the specified libraries.'), action_class='edit-button', - action_label=_(u"Select another problem type.") + action_label=_("Select another problem type.") ) ) @@ -600,18 +596,18 @@ class LibraryContentBlock( StudioValidationMessage.WARNING, ( ngettext( - u'The specified library is configured to fetch {count} problem, ', - u'The specified library is configured to fetch {count} problems, ', + 'The specified library is configured to fetch {count} problem, ', + 'The specified library is configured to fetch {count} problems, ', self.max_count ) + ngettext( - u'but there is only {actual} matching problem.', - u'but there are only {actual} matching problems.', + 'but there is only {actual} matching problem.', + 'but there are only {actual} matching problems.', matching_children_count ) ).format(count=self.max_count, actual=matching_children_count), action_class='edit-button', - action_label=_(u"Edit the library configuration.") + action_label=_("Edit the library configuration.") ) ) @@ -625,13 +621,13 @@ class LibraryContentBlock( user_perms = self.runtime.service(self, 'studio_user_permissions') all_libraries = [ (key, bleach.clean(name)) for key, name in lib_tools.list_available_libraries() - if user_perms.can_read(key) or self.source_library_id == six.text_type(key) + if user_perms.can_read(key) or self.source_library_id == str(key) ] all_libraries.sort(key=lambda entry: entry[1]) # Sort by name if self.source_library_id and self.source_library_key not in [entry[0] for entry in all_libraries]: - all_libraries.append((self.source_library_id, _(u"Invalid Library"))) - all_libraries = [(u"", _("No Library Selected"))] + all_libraries - values = [{"display_name": name, "value": six.text_type(key)} for key, name in all_libraries] + all_libraries.append((self.source_library_id, _("Invalid Library"))) + all_libraries = [("", _("No Library Selected"))] + all_libraries + values = [{"display_name": name, "value": str(key)} for key, name in all_libraries] return values def editor_saved(self, user, old_metadata, old_content): # lint-amnesty, pylint: disable=unused-argument @@ -684,15 +680,15 @@ class LibraryContentBlock( for child in self.get_children(): self.runtime.add_block_as_child_node(child, xml_object) # Set node attributes based on our fields. - for field_name, field in six.iteritems(self.fields): + for field_name, field in self.fields.items(): # pylint: disable=no-member if field_name in ('children', 'parent', 'content'): continue if field.is_set_on(self): - xml_object.set(field_name, six.text_type(field.read_from(self))) + xml_object.set(field_name, str(field.read_from(self))) return xml_object -class LibrarySummary(object): +class LibrarySummary: """ A library summary object which contains the fields required for library listing on studio. """ @@ -706,7 +702,7 @@ class LibrarySummary(object): display_name (unicode): display name of the library. """ - self.display_name = display_name if display_name else _(u"Empty") + self.display_name = display_name if display_name else _("Empty") self.id = library_locator # pylint: disable=invalid-name self.location = library_locator.make_usage_key('library', 'library') diff --git a/common/lib/xmodule/xmodule/library_root_xblock.py b/common/lib/xmodule/xmodule/library_root_xblock.py index 01a4948bb5bd67f4c8c56f1abca4867dcaefa2a1..5d296e66b96d41ab481bb75e19eb73a3c0a07f53 100644 --- a/common/lib/xmodule/xmodule/library_root_xblock.py +++ b/common/lib/xmodule/xmodule/library_root_xblock.py @@ -4,7 +4,6 @@ import logging -import six from django.utils.encoding import python_2_unicode_compatible from web_fragments.fragment import Fragment @@ -50,7 +49,7 @@ class LibraryRoot(XBlock): has_author_view = True def __str__(self): - return u"Library: {}".format(self.display_name) + return f"Library: {self.display_name}" def author_view(self, context): """ @@ -91,7 +90,7 @@ class LibraryRoot(XBlock): child = self.runtime.get_block(child_key) child_view_name = StudioEditableModule.get_preview_view_name(child) - if six.text_type(child.location) == force_render: + if str(child.location) == force_render: child_context['show_preview'] = True if child_context['show_preview']: @@ -101,7 +100,7 @@ class LibraryRoot(XBlock): fragment.add_fragment_resources(rendered_child) contents.append({ - 'id': six.text_type(child.location), + 'id': str(child.location), 'content': rendered_child.content, }) diff --git a/common/lib/xmodule/xmodule/library_sourced_block.py b/common/lib/xmodule/xmodule/library_sourced_block.py index 4005ffabb1449b2ea6b634bab2403199fc1e0af3..fa3d845cd0e1d4563c97237aa6508e14dbaf52ef 100644 --- a/common/lib/xmodule/xmodule/library_sourced_block.py +++ b/common/lib/xmodule/xmodule/library_sourced_block.py @@ -55,7 +55,7 @@ class LibrarySourcedBlock(StudioEditableXBlockMixin, EditableChildrenMixin, XBlo MAX_BLOCKS_ALLOWED = 10 def __str__(self): - return "LibrarySourcedBlock: {}".format(self.display_name) + return f"LibrarySourcedBlock: {self.display_name}" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -121,7 +121,7 @@ class LibrarySourcedBlock(StudioEditableXBlockMixin, EditableChildrenMixin, XBlo validation.add( ValidationMessage( ValidationMessage.ERROR, - _(u"A maximum of {0} components may be added.").format(self.MAX_BLOCKS_ALLOWED) + _("A maximum of {0} components may be added.").format(self.MAX_BLOCKS_ALLOWED) ) ) @@ -137,9 +137,9 @@ class LibrarySourcedBlock(StudioEditableXBlockMixin, EditableChildrenMixin, XBlo validation.set_summary( StudioValidationMessage( StudioValidationMessage.NOT_CONFIGURED, - _(u"No XBlock has been configured for this component. Use the editor to select the target blocks."), + _("No XBlock has been configured for this component. Use the editor to select the target blocks."), action_class='edit-button', - action_label=_(u"Open Editor") + action_label=_("Open Editor") ) ) return validation @@ -156,5 +156,5 @@ class LibrarySourcedBlock(StudioEditableXBlockMixin, EditableChildrenMixin, XBlo lib_tools.import_from_blockstore(self, self.source_block_ids) except Exception as err: # pylint: disable=broad-except log.exception(err) - return Response(_(u"Importing Library Block failed - are the IDs valid and readable?"), status=400) + return Response(_("Importing Library Block failed - are the IDs valid and readable?"), status=400) return response diff --git a/common/lib/xmodule/xmodule/library_tools.py b/common/lib/xmodule/xmodule/library_tools.py index e4bd89771e2dd29da6ebf8b1ec35056382fd1867..fd99f41dbd498727da143ff7db422cb81a6e09b4 100644 --- a/common/lib/xmodule/xmodule/library_tools.py +++ b/common/lib/xmodule/xmodule/library_tools.py @@ -3,7 +3,6 @@ XBlock runtime services for LibraryContentBlock """ import hashlib -import six from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.core.exceptions import PermissionDenied from opaque_keys.edx.keys import UsageKey @@ -26,7 +25,7 @@ def normalize_key_for_search(library_key): return library_key.replace(version_guid=None, branch=None) -class LibraryToolsService(object): +class LibraryToolsService: """ Service that allows LibraryContentBlock to interact with libraries in the modulestore. @@ -81,9 +80,9 @@ class LibraryToolsService(object): """ Basic information about the given block """ orig_key, orig_version = self.store.get_block_original_usage(usage_key) return { - "usage_key": six.text_type(usage_key), - "original_usage_key": six.text_type(orig_key) if orig_key else None, - "original_usage_version": six.text_type(orig_version) if orig_version else None, + "usage_key": str(usage_key), + "original_usage_key": str(orig_key) if orig_key else None, + "original_usage_version": str(orig_version) if orig_version else None, } result_json = [] @@ -109,7 +108,7 @@ class LibraryToolsService(object): search_engine = SearchEngine.get_search_engine(index="library_index") if search_engine: filter_clause = { - "library": six.text_type(normalize_key_for_search(library.location.library_key)), + "library": str(normalize_key_for_search(library.location.library_key)), "content_type": ProblemBlock.INDEX_CONTENT_TYPE, "problem_types": capa_type } @@ -161,7 +160,7 @@ class LibraryToolsService(object): library_key = library_key.replace(branch=ModuleStoreEnum.BranchName.library, version_guid=version) library = self._get_library(library_key) if library is None: - raise ValueError("Requested library {0} not found.".format(library_key)) + raise ValueError(f"Requested library {library_key} not found.") if user_perms and not user_perms.can_read(library_key): raise PermissionDenied() filter_children = (dest_block.capa_type != ANY_CAPA_TYPE_VALUE) @@ -202,7 +201,7 @@ class LibraryToolsService(object): """ dest_key = dest_block.scope_ids.usage_id if not isinstance(dest_key, BlockUsageLocator): - raise TypeError("Destination {} should be a modulestore course.".format(dest_key)) + raise TypeError(f"Destination {dest_key} should be a modulestore course.") if self.user_id is None: raise ValueError("Cannot check user permissions - LibraryTools user_id is None") @@ -286,7 +285,7 @@ class LibraryToolsService(object): if isinstance(field_value, str): # If string field (which may also be JSON/XML data), rewrite /static/... URLs to point to blockstore for asset in all_assets: - field_value = field_value.replace('/static/{}'.format(asset.path), asset.url) + field_value = field_value.replace(f'/static/{asset.path}', asset.url) # Make sure the URL is one that will work from the user's browser when using the docker devstack field_value = blockstore_api.force_browser_url(field_value) setattr(new_block, field_name, field_value) diff --git a/common/lib/xmodule/xmodule/lti_2_util.py b/common/lib/xmodule/xmodule/lti_2_util.py index 70e6437ec80b3d23a0f9c52563eaa5f503c6390c..dd87ab4c83dbc6a5d5c765134343d2727d4ecd85 100644 --- a/common/lib/xmodule/xmodule/lti_2_util.py +++ b/common/lib/xmodule/xmodule/lti_2_util.py @@ -9,11 +9,10 @@ import hashlib import json import logging import re +from unittest import mock +from urllib import parse -import mock -import six from oauthlib.oauth1 import Client -from six import text_type from webob import Response from xblock.core import XBlock @@ -29,7 +28,7 @@ class LTIError(Exception): """Error class for LTIBlock and LTI20BlockMixin""" -class LTI20BlockMixin(object): +class LTI20BlockMixin: """ This class MUST be mixed into LTIBlock. It does not do anything on its own. It's just factored out for modularity. @@ -81,7 +80,7 @@ class LTI20BlockMixin(object): real_user = self.system.get_real_user(anon_id) if not real_user: # that means we can't save to database, as we do not have real user id. - msg = "[LTI]: Real user not found against anon_id: {}".format(anon_id) + msg = f"[LTI]: Real user not found against anon_id: {anon_id}" log.info(msg) return Response(status=404) # have to do 404 due to spec, but 400 is better, with error msg in body if request.method == "PUT": @@ -108,22 +107,22 @@ class LTI20BlockMixin(object): """ sha1 = hashlib.sha1() sha1.update(request.body) - oauth_body_hash = six.text_type(base64.b64encode(sha1.digest())) - log.debug("[LTI] oauth_body_hash = {}".format(oauth_body_hash)) + oauth_body_hash = str(base64.b64encode(sha1.digest())) + log.debug(f"[LTI] oauth_body_hash = {oauth_body_hash}") client_key, client_secret = self.get_client_key_secret() client = Client(client_key, client_secret) mock_request = mock.Mock( - uri=six.text_type(six.moves.urllib.parse.unquote(request.url)), + uri=str(parse.unquote(request.url)), headers=request.headers, - body=u"", - decoded_body=u"", - http_method=six.text_type(request.method), + body="", + decoded_body="", + http_method=str(request.method), ) params = client.get_oauth_params(mock_request) mock_request.oauth_params = params - mock_request.oauth_params.append((u'oauth_body_hash', oauth_body_hash)) + mock_request.oauth_params.append(('oauth_body_hash', oauth_body_hash)) sig = client.get_oauth_signature(mock_request) - mock_request.oauth_params.append((u'oauth_signature', sig)) + mock_request.oauth_params.append(('oauth_signature', sig)) _, headers, _ = client._render(mock_request) # pylint: disable=protected-access log.debug("\n\n#### COPY AND PASTE AUTHORIZATION HEADER ####\n{}\n####################################\n\n" @@ -151,7 +150,7 @@ class LTI20BlockMixin(object): return match_obj.group('anon_id') # fall-through handles all error cases msg = "No valid user id found in endpoint URL" - log.info("[LTI]: {}".format(msg)) + log.info(f"[LTI]: {msg}") raise LTIError(msg) def _lti_2_0_result_get_handler(self, request, real_user): @@ -236,7 +235,7 @@ class LTI20BlockMixin(object): """ self.set_user_module_score(user, None, None, score_deleted=True) - def set_user_module_score(self, user, score, max_score, comment=u"", score_deleted=False): + def set_user_module_score(self, user, score, max_score, comment="", score_deleted=False): """ Sets the module user state, including grades and comments, and also scoring in db's courseware_studentmodule @@ -286,15 +285,15 @@ class LTI20BlockMixin(object): """ content_type = request.headers.get('Content-Type') if verify_content_type and content_type != LTI_2_0_JSON_CONTENT_TYPE: - log.info("[LTI]: v2.0 result service -- bad Content-Type: {}".format(content_type)) + log.info(f"[LTI]: v2.0 result service -- bad Content-Type: {content_type}") raise LTIError( "For LTI 2.0 result service, Content-Type must be {}. Got {}".format(LTI_2_0_JSON_CONTENT_TYPE, content_type)) try: self.verify_oauth_body_sign(request, content_type=LTI_2_0_JSON_CONTENT_TYPE) except (ValueError, LTIError) as err: - log.info("[LTI]: v2.0 result service -- OAuth body verification failed: {}".format(text_type(err))) - raise LTIError(text_type(err)) # lint-amnesty, pylint: disable=raise-missing-from + log.info("[LTI]: v2.0 result service -- OAuth body verification failed: {}".format(str(err))) + raise LTIError(str(err)) # lint-amnesty, pylint: disable=raise-missing-from def parse_lti_2_0_result_json(self, json_str): """ @@ -319,8 +318,8 @@ class LTI20BlockMixin(object): try: json_obj = json.loads(json_str) except (ValueError, TypeError): - msg = "Supplied JSON string in request body could not be decoded: {}".format(json_str) - log.info("[LTI] {}".format(msg)) + msg = f"Supplied JSON string in request body could not be decoded: {json_str}" + log.info(f"[LTI] {msg}") raise LTIError(msg) # lint-amnesty, pylint: disable=raise-missing-from # the standard supports a list of objects, who knows why. It must contain at least 1 element, and the @@ -331,22 +330,22 @@ class LTI20BlockMixin(object): else: msg = ("Supplied JSON string is a list that does not contain an object as the first element. {}" .format(json_str)) - log.info("[LTI] {}".format(msg)) + log.info(f"[LTI] {msg}") raise LTIError(msg) # '@type' must be "Result" result_type = json_obj.get("@type") if result_type != "Result": - msg = "JSON object does not contain correct @type attribute (should be 'Result', is {})".format(result_type) - log.info("[LTI] {}".format(msg)) + msg = f"JSON object does not contain correct @type attribute (should be 'Result', is {result_type})" + log.info(f"[LTI] {msg}") raise LTIError(msg) # '@context' must be present as a key REQUIRED_KEYS = ["@context"] # pylint: disable=invalid-name for key in REQUIRED_KEYS: if key not in json_obj: - msg = "JSON object does not contain required key {}".format(key) - log.info("[LTI] {}".format(msg)) + msg = f"JSON object does not contain required key {key}" + log.info(f"[LTI] {msg}") raise LTIError(msg) # 'resultScore' is not present. If this was a PUT this means it's actually a DELETE according @@ -360,11 +359,11 @@ class LTI20BlockMixin(object): score = float(json_obj.get('resultScore', "unconvertable")) # Check if float is present and the right type if not 0 <= score <= 1: msg = 'score value outside the permitted range of 0-1.' - log.info("[LTI] {}".format(msg)) + log.info(f"[LTI] {msg}") raise LTIError(msg) except (TypeError, ValueError) as err: - msg = "Could not convert resultScore to float: {}".format(text_type(err)) - log.info("[LTI] {}".format(msg)) + msg = "Could not convert resultScore to float: {}".format(str(err)) + log.info(f"[LTI] {msg}") raise LTIError(msg) # lint-amnesty, pylint: disable=raise-missing-from return score, json_obj.get('comment', "") diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index f18859d1d9dacfa8af0d252f327ee248a06377fe..99f7db03c7629345fcd30a2c323f5f5062846afd 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -60,16 +60,15 @@ import hashlib import logging import textwrap from xml.sax.saxutils import escape +from unittest import mock +from urllib import parse import bleach -import mock import oauthlib.oauth1 -import six from lxml import etree from oauthlib.oauth1.rfc5849 import signature from pkg_resources import resource_string from pytz import UTC -from six import text_type from webob import Response from web_fragments.fragment import Fragment from xblock.core import List, Scope, String, XBlock @@ -106,7 +105,7 @@ BREAK_TAG = '<br />' _ = lambda text: text -class LTIFields(object): +class LTIFields: """ Fields to define and obtain LTI tool from provider are set here, except credentials, which should be set in course settings:: @@ -457,7 +456,7 @@ class LTIBlock( except ValueError: _ = self.runtime.service(self, "i18n").ugettext msg = _('Could not parse custom parameter: {custom_parameter}. Should be "x=y" string.').format( - custom_parameter="{0!r}".format(custom_parameter) + custom_parameter=f"{custom_parameter!r}" ) raise LTIError(msg) # lint-amnesty, pylint: disable=raise-missing-from @@ -465,7 +464,7 @@ class LTIBlock( if param_name not in PARAMETERS: param_name = 'custom_' + param_name - custom_parameters[six.text_type(param_name)] = six.text_type(param_value) + custom_parameters[str(param_name)] = str(param_value) return self.oauth_params( custom_parameters, @@ -532,7 +531,7 @@ class LTIBlock( def get_user_id(self): user_id = self.runtime.anonymous_student_id assert user_id is not None - return six.text_type(six.moves.urllib.parse.quote(user_id)) + return str(parse.quote(user_id)) def get_outcome_service_url(self, service_name="grade_handler"): """ @@ -578,7 +577,7 @@ class LTIBlock( i4x-2-3-lti-31de800015cf4afb973356dbe81496df this part of resource_link_id: makes resource_link_id to be unique among courses inside same system. """ - return six.text_type(six.moves.urllib.parse.quote("{}-{}".format(self.system.hostname, self.location.html_id()))) # lint-amnesty, pylint: disable=line-too-long + return str(parse.quote(f"{self.system.hostname}-{self.location.html_id()}")) # lint-amnesty, pylint: disable=line-too-long def get_lis_result_sourcedid(self): """ @@ -590,7 +589,7 @@ class LTIBlock( This field is generally optional, but is required for grading. """ return "{context}:{resource_link}:{user_id}".format( - context=six.moves.urllib.parse.quote(self.context_id), + context=parse.quote(self.context_id), resource_link=self.get_resource_link_id(), user_id=self.get_user_id() ) @@ -609,7 +608,7 @@ class LTIBlock( context_id is an opaque identifier that uniquely identifies the context (e.g., a course) that contains the link being launched. """ - return text_type(self.course_id) + return str(self.course_id) @property def role(self): @@ -617,11 +616,11 @@ class LTIBlock( Get system user role and convert it to LTI role. """ roles = { - 'student': u'Student', - 'staff': u'Administrator', - 'instructor': u'Instructor', + 'student': 'Student', + 'staff': 'Administrator', + 'instructor': 'Instructor', } - return roles.get(self.system.get_user_role(), u'Student') + return roles.get(self.system.get_user_role(), 'Student') def get_icon_class(self): """ Returns the icon class """ @@ -640,29 +639,29 @@ class LTIBlock( """ client = oauthlib.oauth1.Client( - client_key=text_type(client_key), - client_secret=text_type(client_secret) + client_key=str(client_key), + client_secret=str(client_secret) ) # Must have parameters for correct signing from LTI: body = { - u'user_id': self.get_user_id(), - u'oauth_callback': u'about:blank', - u'launch_presentation_return_url': '', - u'lti_message_type': u'basic-lti-launch-request', - u'lti_version': 'LTI-1p0', - u'roles': self.role, + 'user_id': self.get_user_id(), + 'oauth_callback': 'about:blank', + 'launch_presentation_return_url': '', + 'lti_message_type': 'basic-lti-launch-request', + 'lti_version': 'LTI-1p0', + 'roles': self.role, # Parameters required for grading: - u'resource_link_id': self.get_resource_link_id(), - u'lis_result_sourcedid': self.get_lis_result_sourcedid(), + 'resource_link_id': self.get_resource_link_id(), + 'lis_result_sourcedid': self.get_lis_result_sourcedid(), - u'context_id': self.context_id, + 'context_id': self.context_id, } if self.has_score: body.update({ - u'lis_outcome_service_url': self.get_outcome_service_url() + 'lis_outcome_service_url': self.get_outcome_service_url() }) self.user_email = "" # lint-amnesty, pylint: disable=attribute-defined-outside-init @@ -697,21 +696,21 @@ class LTIBlock( try: __, headers, __ = client.sign( - six.text_type(self.launch_url.strip()), - http_method=u'POST', + str(self.launch_url.strip()), + http_method='POST', body=body, headers=headers) except ValueError: # Scheme not in url. # https://github.com/idan/oauthlib/blob/master/oauthlib/oauth1/rfc5849/signature.py#L136 # Stubbing headers for now: log.info( - u"LTI module %s in course %s does not have oauth parameters correctly configured.", + "LTI module %s in course %s does not have oauth parameters correctly configured.", self.location, self.location.course_key, ) headers = { - u'Content-Type': u'application/x-www-form-urlencoded', - u'Authorization': u'OAuth oauth_nonce="80966668944732164491378916897", \ + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'OAuth oauth_nonce="80966668944732164491378916897", \ oauth_timestamp="1378916897", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", \ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} @@ -719,15 +718,15 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} # Parse headers to pass to template as part of context: params = dict([param.strip().replace('"', '').split('=') for param in params.split(',')]) - params[u'oauth_nonce'] = params[u'OAuth oauth_nonce'] - del params[u'OAuth oauth_nonce'] + params['oauth_nonce'] = params['OAuth oauth_nonce'] + del params['OAuth oauth_nonce'] # oauthlib encodes signature with # 'Content-Type': 'application/x-www-form-urlencoded' # so '='' becomes '%3D'. # We send form via browser, so browser will encode it again, # So we need to decode signature back: - params[u'oauth_signature'] = six.moves.urllib.parse.unquote(params[u'oauth_signature']).encode('utf-8').decode('utf8') # lint-amnesty, pylint: disable=line-too-long + params['oauth_signature'] = parse.unquote(params['oauth_signature']).encode('utf-8').decode('utf8') # lint-amnesty, pylint: disable=line-too-long # Add LTI parameters to OAuth parameters for sending in form. params.update(body) @@ -816,7 +815,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} try: imsx_messageIdentifier, sourcedId, score, action = self.parse_grade_xml_body(request.body) except Exception as e: # lint-amnesty, pylint: disable=broad-except - error_message = "Request body XML parsing error: " + escape(text_type(e)) + error_message = "Request body XML parsing error: " + escape(str(e)) log.debug("[LTI]: " + error_message) # lint-amnesty, pylint: disable=logging-not-lazy failure_values['imsx_description'] = error_message return Response(response_xml_template.format(**failure_values), content_type="application/xml") @@ -826,12 +825,12 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} self.verify_oauth_body_sign(request) except (ValueError, LTIError) as e: failure_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier) - error_message = "OAuth verification error: " + escape(text_type(e)) + error_message = "OAuth verification error: " + escape(str(e)) failure_values['imsx_description'] = error_message log.debug("[LTI]: " + error_message) # lint-amnesty, pylint: disable=logging-not-lazy return Response(response_xml_template.format(**failure_values), content_type="application/xml") - real_user = self.system.get_real_user(six.moves.urllib.parse.unquote(sourcedId.split(':')[-1])) + real_user = self.system.get_real_user(parse.unquote(sourcedId.split(':')[-1])) if not real_user: # that means we can't save to database, as we do not have real user id. failure_values['imsx_messageIdentifier'] = escape(imsx_messageIdentifier) failure_values['imsx_description'] = "User not found." @@ -842,7 +841,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} values = { 'imsx_codeMajor': 'success', - 'imsx_description': 'Score for {sourced_id} is now {score}'.format(sourced_id=sourcedId, score=score), + 'imsx_description': f'Score for {sourcedId} is now {score}', 'imsx_messageIdentifier': escape(imsx_messageIdentifier), 'response': '<replaceResultResponse/>' } @@ -900,7 +899,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} client_key, client_secret = self.get_client_key_secret() # lint-amnesty, pylint: disable=unused-variable headers = { - 'Authorization': six.text_type(request.headers.get('Authorization')), + 'Authorization': str(request.headers.get('Authorization')), 'Content-Type': content_type, } @@ -911,14 +910,14 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} oauth_headers = dict(oauth_params) oauth_signature = oauth_headers.pop('oauth_signature') mock_request_lti_1 = mock.Mock( - uri=six.text_type(six.moves.urllib.parse.unquote(self.get_outcome_service_url())), - http_method=six.text_type(request.method), + uri=str(parse.unquote(self.get_outcome_service_url())), + http_method=str(request.method), params=list(oauth_headers.items()), signature=oauth_signature ) mock_request_lti_2 = mock.Mock( - uri=six.text_type(six.moves.urllib.parse.unquote(request.url)), - http_method=six.text_type(request.method), + uri=str(parse.unquote(request.url)), + http_method=str(request.method), params=list(oauth_headers.items()), signature=oauth_signature ) @@ -940,7 +939,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} "headers:{} url:{} method:{}".format( oauth_headers, self.get_outcome_service_url(), - six.text_type(request.method) + str(request.method) )) raise LTIError("OAuth signature verification has failed.") @@ -955,7 +954,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} except ValueError: _ = self.runtime.service(self, "i18n").ugettext msg = _('Could not parse LTI passport: {lti_passport}. Should be "id:key:secret" string.').format( - lti_passport='{0!r}'.format(lti_passport) + lti_passport=f'{lti_passport!r}' ) raise LTIError(msg) # lint-amnesty, pylint: disable=raise-missing-from diff --git a/common/lib/xmodule/xmodule/mako_module.py b/common/lib/xmodule/xmodule/mako_module.py index 1ff9a449c8a9a5a07633cab02d0db68bd983f9d2..ab1a5ed513996c70e94356682be28706d339372b 100644 --- a/common/lib/xmodule/xmodule/mako_module.py +++ b/common/lib/xmodule/xmodule/mako_module.py @@ -10,12 +10,12 @@ from .x_module import DescriptorSystem, XModuleDescriptor, shim_xmodule_js class MakoDescriptorSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstract-method def __init__(self, render_template, **kwargs): - super(MakoDescriptorSystem, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(**kwargs) self.render_template = render_template -class MakoTemplateBlockBase(object): +class MakoTemplateBlockBase: """ XBlock intended as a mixin that uses a mako template to specify the module html. @@ -27,7 +27,7 @@ class MakoTemplateBlockBase(object): # pylint: disable=no-member def __init__(self, *args, **kwargs): - super(MakoTemplateBlockBase, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) if getattr(self.runtime, 'render_template', None) is None: raise TypeError( '{runtime} must have a render_template function' diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index 2ba14ecac2dd518d2fd5ec9141f3d65208f438ad..517918b5cc99648d0c37a74a874ef020529368bd 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -13,12 +13,10 @@ from collections import defaultdict from contextlib import contextmanager from operator import itemgetter -import six from contracts import contract, new_contract from opaque_keys.edx.keys import AssetKey, CourseKey from opaque_keys.edx.locations import Location # For import backwards compatibility from pytz import UTC -from six.moves import range from sortedcontainers import SortedKeyList from xblock.core import XBlock from xblock.plugin import default_select @@ -39,8 +37,8 @@ new_contract('AssetKey', AssetKey) new_contract('AssetMetadata', AssetMetadata) new_contract('XBlock', XBlock) -LIBRARY_ROOT = u'library.xml' -COURSE_ROOT = u'course.xml' +LIBRARY_ROOT = 'library.xml' +COURSE_ROOT = 'course.xml' # List of names of computed fields on xmodules that are of type usage keys. # This list can be used to determine which fields need to be stripped of @@ -48,19 +46,19 @@ COURSE_ROOT = u'course.xml' XMODULE_FIELDS_WITH_USAGE_KEYS = ['location', 'parent'] -class ModuleStoreEnum(object): +class ModuleStoreEnum: """ A class to encapsulate common constants that are used with the various modulestores. """ - class Type(object): + class Type: """ The various types of modulestores provided """ split = 'split' mongo = 'mongo' - class RevisionOption(object): + class RevisionOption: """ Revision constants to use for Module Store operations Note: These values are passed into store APIs and only used at run time @@ -77,7 +75,7 @@ class ModuleStoreEnum(object): # all revisions are queried all = 'rev-opt-all' - class Branch(object): + class Branch: """ Branch constants to use for stores, such as Mongo, that have only 2 branches: DRAFT and PUBLISHED Note: These values are taken from server configuration settings, so should not be changed without alerting DevOps # lint-amnesty, pylint: disable=line-too-long @@ -85,7 +83,7 @@ class ModuleStoreEnum(object): draft_preferred = 'draft-preferred' published_only = 'published-only' - class BranchName(object): + class BranchName: """ Branch constants to use for stores, such as Split, that have named branches """ @@ -93,7 +91,7 @@ class ModuleStoreEnum(object): published = 'published-branch' library = 'library' - class UserID(object): + class UserID: """ Values for user ID defaults """ @@ -112,7 +110,7 @@ class ModuleStoreEnum(object): # user ID for automatic update by the system system = -4 - class SortOrder(object): + class SortOrder: """ Values for sorting asset metadata. """ @@ -120,7 +118,7 @@ class ModuleStoreEnum(object): descending = 2 -class BulkOpsRecord(object): +class BulkOpsRecord: """ For handling nesting of bulk operations """ @@ -161,11 +159,11 @@ class ActiveBulkThread(threading.local): Add the expected vars to the thread. """ def __init__(self, bulk_ops_record_type, **kwargs): - super(ActiveBulkThread, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(**kwargs) self.records = defaultdict(bulk_ops_record_type) -class BulkOperationsMixin(object): +class BulkOperationsMixin: """ This implements the :meth:`bulk_operations` modulestore semantics which handles nested invocations @@ -178,7 +176,7 @@ class BulkOperationsMixin(object): mongo_connection. """ def __init__(self, *args, **kwargs): - super(BulkOperationsMixin, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self._active_bulk_ops = ActiveBulkThread(self._bulk_ops_record_type) self.signal_handler = None @@ -209,7 +207,7 @@ class BulkOperationsMixin(object): # Retrieve the bulk record based on matching org/course/run (possibly ignoring case) if ignore_case: - for key, record in six.iteritems(self._active_bulk_ops.records): + for key, record in self._active_bulk_ops.records.items(): # Shortcut: check basic equivalence for cases where org/course/run might be None. if (key == course_key) or ( # lint-amnesty, pylint: disable=too-many-boolean-expressions (key.org and key.org.lower() == course_key.org.lower()) and @@ -225,7 +223,7 @@ class BulkOperationsMixin(object): """ Yield all active (CourseLocator, BulkOpsRecord) tuples. """ - for course_key, record in six.iteritems(self._active_bulk_ops.records): + for course_key, record in self._active_bulk_ops.records.items(): if record.active: yield (course_key, record) @@ -336,7 +334,7 @@ class BulkOperationsMixin(object): bulk_ops_record.has_library_updated_item = False -class EditInfo(object): +class EditInfo: # pylint: disable=eq-without-hash """ Encapsulates the editing info of a block. """ @@ -415,7 +413,7 @@ class EditInfo(object): return not self == edit_info -class BlockData(object): +class BlockData: # pylint: disable=eq-without-hash """ Wrap the block data in an object instead of using a straight Python dictionary. Allows the storing of meta-information about a structure that doesn't persist along with @@ -519,7 +517,7 @@ class SortedAssetList(SortedKeyList): # lint-amnesty, pylint: disable=abstract- if key_func is None: kwargs['key'] = itemgetter('filename') self.filename_sort = True - super(SortedAssetList, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(**kwargs) @contract(asset_id=AssetKey) def find(self, asset_id): @@ -556,7 +554,7 @@ class SortedAssetList(SortedKeyList): # lint-amnesty, pylint: disable=abstract- self.add(metadata_to_insert) -class ModuleStoreAssetBase(object): +class ModuleStoreAssetBase: """ The methods for accessing assets and their metadata """ @@ -634,7 +632,7 @@ class ModuleStoreAssetBase(object): if asset_type is None: # Add assets of all types to the sorted list. all_assets = SortedAssetList(iterable=[], key=key_func) - for asset_type, val in six.iteritems(course_assets): # lint-amnesty, pylint: disable=redefined-argument-from-local + for asset_type, val in course_assets.items(): # lint-amnesty, pylint: disable=redefined-argument-from-local all_assets.update(val) else: # Add assets of a single type to the sorted list. @@ -776,7 +774,7 @@ class ModuleStoreAssetWriteInterface(ModuleStoreAssetBase): pass # lint-amnesty, pylint: disable=unnecessary-pass -class ModuleStoreRead(six.with_metaclass(ABCMeta, ModuleStoreAssetBase)): +class ModuleStoreRead(ModuleStoreAssetBase, metaclass=ABCMeta): """ An abstract interface for a database backend that stores XModuleDescriptor instances and extends read-only functionality @@ -875,7 +873,7 @@ class ModuleStoreRead(six.with_metaclass(ABCMeta, ModuleStoreAssetBase)): else: return True, field - for key, criteria in six.iteritems(qualifiers): + for key, criteria in qualifiers.items(): is_set, value = _is_set_on(key) if isinstance(criteria, dict) and '$exists' in criteria and criteria['$exists'] == is_set: continue @@ -1031,7 +1029,7 @@ class ModuleStoreRead(six.with_metaclass(ABCMeta, ModuleStoreAssetBase)): pass # lint-amnesty, pylint: disable=unnecessary-pass -class ModuleStoreWrite(six.with_metaclass(ABCMeta, ModuleStoreRead, ModuleStoreAssetWriteInterface)): +class ModuleStoreWrite(ModuleStoreRead, ModuleStoreAssetWriteInterface, metaclass=ABCMeta): """ An abstract interface for a database backend that stores XModuleDescriptor instances and extends both read and write functionality @@ -1172,7 +1170,7 @@ class ModuleStoreReadBase(BulkOperationsMixin, ModuleStoreRead): ''' Set up the error-tracking logic. ''' - super(ModuleStoreReadBase, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(**kwargs) self._course_errors = defaultdict(make_error_tracker) # location -> ErrorLog # TODO move the inheritance_cache_subsystem to classes which use it self.metadata_inheritance_cache_subsystem = metadata_inheritance_cache_subsystem @@ -1260,7 +1258,7 @@ class ModuleStoreReadBase(BulkOperationsMixin, ModuleStoreRead): """ if self.contentstore: self.contentstore.close_connections() - super(ModuleStoreReadBase, self).close_connections() # lint-amnesty, pylint: disable=super-with-arguments + super().close_connections() @contextmanager def default_store(self, store_type): @@ -1268,7 +1266,7 @@ class ModuleStoreReadBase(BulkOperationsMixin, ModuleStoreRead): A context manager for temporarily changing the default store """ if self.get_modulestore_type(None) != store_type: - raise ValueError(u"Cannot set default store to type {}".format(store_type)) + raise ValueError(f"Cannot set default store to type {store_type}") yield @@ -1278,7 +1276,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite): Implement interface functionality that can be shared. ''' def __init__(self, contentstore, **kwargs): - super(ModuleStoreWriteBase, self).__init__(contentstore=contentstore, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(contentstore=contentstore, **kwargs) self.mixologist = Mixologist(self.xblock_mixins) def partition_fields_by_scope(self, category, fields): @@ -1293,7 +1291,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite): return result classes = XBlock.load_class(category, select=prefer_xmodules) cls = self.mixologist.mix(classes) - for field_name, value in six.iteritems(fields): + for field_name, value in fields.items(): field = getattr(cls, field_name) result[field.scope][field_name] = value return result @@ -1338,7 +1336,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite): # delete the assets if self.contentstore: self.contentstore.delete_all_course_assets(course_key) - super(ModuleStoreWriteBase, self).delete_course(course_key, user_id) # lint-amnesty, pylint: disable=super-with-arguments + super().delete_course(course_key, user_id) def _drop_database(self, database=True, collections=True, connections=True): """ @@ -1354,7 +1352,7 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite): """ if self.contentstore: self.contentstore._drop_database(database, collections, connections) # pylint: disable=protected-access - super(ModuleStoreWriteBase, self)._drop_database(database, collections, connections) # lint-amnesty, pylint: disable=super-with-arguments + super()._drop_database(database, collections, connections) def create_child(self, user_id, parent_usage_key, block_type, block_id=None, fields=None, **kwargs): """ diff --git a/common/lib/xmodule/xmodule/modulestore/django.py b/common/lib/xmodule/xmodule/modulestore/django.py index 7ebdf0da230c355b306d4dd5017dd6233bb1c4e3..d96f8ee428ea0503a8c4011743120d3f74c24764 100644 --- a/common/lib/xmodule/xmodule/modulestore/django.py +++ b/common/lib/xmodule/xmodule/modulestore/django.py @@ -8,7 +8,6 @@ from importlib import import_module import gettext import logging -import six from pkg_resources import resource_filename import re # lint-amnesty, pylint: disable=wrong-import-order @@ -68,7 +67,7 @@ class SwitchedSignal(django.dispatch.Signal): All other args are passed to the constructor for django.dispatch.Signal. """ - super(SwitchedSignal, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) self.name = name self._allow_signals = True @@ -103,7 +102,7 @@ class SwitchedSignal(django.dispatch.Signal): "ALLOW" if self._allow_signals else "BLOCK" ) if self._allow_signals: - return super(SwitchedSignal, self).send(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().send(*args, **kwargs) return [] def send_robust(self, *args, **kwargs): @@ -121,14 +120,14 @@ class SwitchedSignal(django.dispatch.Signal): "ALLOW" if self._allow_signals else "BLOCK" ) if self._allow_signals: - return super(SwitchedSignal, self).send_robust(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().send_robust(*args, **kwargs) return [] def __repr__(self): - return u"SwitchedSignal('{}')".format(self.name) + return f"SwitchedSignal('{self.name}')" -class SignalHandler(object): +class SignalHandler: """ This class is to allow the modulestores to emit signals that can be caught by other parts of the Django application. If your app needs to do something @@ -252,7 +251,7 @@ def create_modulestore_instance( FUNCTION_KEYS = ['render_template'] for key in FUNCTION_KEYS: - if key in _options and isinstance(_options[key], six.string_types): + if key in _options and isinstance(_options[key], str): _options[key] = load_function(_options[key]) request_cache = DEFAULT_REQUEST_CACHE @@ -345,7 +344,7 @@ def clear_existing_modulestores(): _MIXED_MODULESTORE = None -class ModuleI18nService(object): +class ModuleI18nService: """ Implement the XBlock runtime "i18n" service. @@ -379,12 +378,12 @@ class ModuleI18nService(object): xblock_locale_path, [to_locale(selected_language if selected_language else settings.LANGUAGE_CODE)] ) - except IOError: + except OSError: # Fall back to the default Django translator if the XBlock translator is not found. pass def __getattr__(self, name): - name = 'gettext' if six.PY3 and name == 'ugettext' else name + name = 'gettext' if name == 'ugettext' else name return getattr(self.translator, name) def strftime(self, *args, **kwargs): diff --git a/common/lib/xmodule/xmodule/modulestore/draft_and_published.py b/common/lib/xmodule/xmodule/modulestore/draft_and_published.py index 92bf0586ffcdf968f2274831f3f4574a9a6c46aa..d69e06763cdc953e8c28872b5a87f99793537a4e 100644 --- a/common/lib/xmodule/xmodule/modulestore/draft_and_published.py +++ b/common/lib/xmodule/xmodule/modulestore/draft_and_published.py @@ -8,9 +8,6 @@ import threading from abc import ABCMeta, abstractmethod from contextlib import contextmanager -import six -from six import text_type - from . import BulkOperationsMixin, ModuleStoreEnum from .exceptions import ItemNotFoundError @@ -20,7 +17,7 @@ DIRECT_ONLY_CATEGORIES = ['course', 'chapter', 'sequential', 'about', 'static_ta log = logging.getLogger(__name__) -class BranchSettingMixin(object): +class BranchSettingMixin: """ A mixin to manage a module store's branch setting. The order of override is (from higher precedence to lower): @@ -38,7 +35,7 @@ class BranchSettingMixin(object): 'branch_setting_func', lambda: ModuleStoreEnum.Branch.published_only ) - super(BranchSettingMixin, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(*args, **kwargs) # cache the branch setting on a local thread to support a multi-threaded environment self.thread_cache = threading.local() @@ -71,7 +68,7 @@ class BranchSettingMixin(object): return self.default_branch_setting_func() -class ModuleStoreDraftAndPublished(six.with_metaclass(ABCMeta, BranchSettingMixin, BulkOperationsMixin)): +class ModuleStoreDraftAndPublished(BranchSettingMixin, BulkOperationsMixin, metaclass=ABCMeta): """ A mixin for a read-write database backend that supports two branches, Draft and Published, with options to prefer Draft and fallback to Published. @@ -161,7 +158,7 @@ class ModuleStoreDraftAndPublished(six.with_metaclass(ABCMeta, BranchSettingMixi old_parent_item = self.get_item(old_parent_location) # pylint: disable=no-member new_parent_item = self.get_item(new_parent_location) # pylint: disable=no-member except ItemNotFoundError as exception: - log.error('Unable to find the item : %s', text_type(exception)) + log.error('Unable to find the item : %s', str(exception)) return # Remove item from the list of children of old parent. @@ -170,8 +167,8 @@ class ModuleStoreDraftAndPublished(six.with_metaclass(ABCMeta, BranchSettingMixi self.update_item(old_parent_item, user_id) # pylint: disable=no-member log.info( '%s removed from %s children', - text_type(source_item.location), - text_type(old_parent_item.location) + str(source_item.location), + str(old_parent_item.location) ) # Add item to new parent at particular location. @@ -183,8 +180,8 @@ class ModuleStoreDraftAndPublished(six.with_metaclass(ABCMeta, BranchSettingMixi self.update_item(new_parent_item, user_id) # pylint: disable=no-member log.info( '%s added to %s children', - text_type(source_item.location), - text_type(new_parent_item.location) + str(source_item.location), + str(new_parent_item.location) ) # Update parent attribute of the item block @@ -192,8 +189,8 @@ class ModuleStoreDraftAndPublished(six.with_metaclass(ABCMeta, BranchSettingMixi self.update_item(source_item, user_id) # pylint: disable=no-member log.info( '%s parent updated to %s', - text_type(source_item.location), - text_type(new_parent_item.location) + str(source_item.location), + str(new_parent_item.location) ) return source_item.location @@ -209,4 +206,4 @@ class UnsupportedRevisionError(ValueError): ModuleStoreEnum.RevisionOption.published_only, ModuleStoreEnum.RevisionOption.draft_only ] - super(UnsupportedRevisionError, self).__init__('revision not one of {}'.format(allowed_revisions)) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(f'revision not one of {allowed_revisions}') diff --git a/common/lib/xmodule/xmodule/modulestore/edit_info.py b/common/lib/xmodule/xmodule/modulestore/edit_info.py index 471544d25b5d213d9a1147260010b4dd5a2600db..cbda7229285c332083324328aae81d0025c0b875 100644 --- a/common/lib/xmodule/xmodule/modulestore/edit_info.py +++ b/common/lib/xmodule/xmodule/modulestore/edit_info.py @@ -5,7 +5,6 @@ Access methods to get EditInfo for xblocks from abc import ABCMeta, abstractmethod -import six from xblock.core import XBlockMixin @@ -56,7 +55,7 @@ class EditInfoMixin(XBlockMixin): return self.runtime.get_published_on(self) -class EditInfoRuntimeMixin(six.with_metaclass(ABCMeta, object)): +class EditInfoRuntimeMixin(metaclass=ABCMeta): """ An abstract mixin class for the functions which the :class: `EditInfoMixin` methods call on the runtime """ diff --git a/common/lib/xmodule/xmodule/modulestore/exceptions.py b/common/lib/xmodule/xmodule/modulestore/exceptions.py index 79b1ec50c5a7ab00eaaa5fd926d39b18041f9dc7..f03b416042036ecf729d2d2dab34294f3247fa66 100644 --- a/common/lib/xmodule/xmodule/modulestore/exceptions.py +++ b/common/lib/xmodule/xmodule/modulestore/exceptions.py @@ -55,7 +55,7 @@ class DuplicateItemError(Exception): Attempted to create an item which already exists. """ def __init__(self, element_id, store=None, collection=None): - super(DuplicateItemError, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.element_id = element_id self.store = store self.collection = collection @@ -77,7 +77,7 @@ class VersionConflictError(Exception): The caller asked for either draft or published head and gave a version which conflicted with it. """ def __init__(self, requestedLocation, currentHeadVersionGuid): - super(VersionConflictError, self).__init__(u'Requested {}, but current head is {}'.format( # lint-amnesty, pylint: disable=super-with-arguments + super().__init__('Requested {}, but current head is {}'.format( requestedLocation, currentHeadVersionGuid )) @@ -91,8 +91,8 @@ class DuplicateCourseError(Exception): """ existing_entry will have the who, when, and other properties of the existing entry """ - super(DuplicateCourseError, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments - u'Cannot create course {}, which duplicates {}'.format(course_id, existing_entry) + super().__init__( + f'Cannot create course {course_id}, which duplicates {existing_entry}' ) self.course_id = course_id self.existing_entry = existing_entry @@ -103,6 +103,6 @@ class InvalidBranchSetting(Exception): Raised when the process' branch setting did not match the required setting for the attempted operation on a store. """ def __init__(self, expected_setting, actual_setting): - super(InvalidBranchSetting, self).__init__(u"Invalid branch: expected {} but got {}".format(expected_setting, actual_setting)) # lint-amnesty, pylint: disable=line-too-long, super-with-arguments + super().__init__(f"Invalid branch: expected {expected_setting} but got {actual_setting}") # lint-amnesty, pylint: disable=line-too-long, super-with-arguments self.expected_setting = expected_setting self.actual_setting = actual_setting diff --git a/common/lib/xmodule/xmodule/modulestore/inheritance.py b/common/lib/xmodule/xmodule/modulestore/inheritance.py index 104d0d05c02e5eb5e6aef0f88825869b7af7c4ad..aac6d198a5f1dfdefae50c7039410259a3bfbab9 100644 --- a/common/lib/xmodule/xmodule/modulestore/inheritance.py +++ b/common/lib/xmodule/xmodule/modulestore/inheritance.py @@ -318,7 +318,7 @@ class InheritingFieldData(KvsFieldData): parents. """ - super(InheritingFieldData, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(**kwargs) self.inheritable_names = set(inheritable_names) def has_default_value(self, name): @@ -349,14 +349,14 @@ class InheritingFieldData(KvsFieldData): if ancestor and \ ancestor.location.block_type == 'library_content' and \ self.has_default_value(name): - return super(InheritingFieldData, self).default(block, name) # lint-amnesty, pylint: disable=super-with-arguments + return super().default(block, name) while ancestor is not None: if field.is_set_on(ancestor): return field.read_json(ancestor) else: ancestor = ancestor.get_parent() - return super(InheritingFieldData, self).default(block, name) # lint-amnesty, pylint: disable=super-with-arguments + return super().default(block, name) def inheriting_field_data(kvs): @@ -375,7 +375,7 @@ class InheritanceKeyValueStore(KeyValueStore): Note: inherited_settings is a dict of key to json values (internal xblock field repr) """ def __init__(self, initial_values=None, inherited_settings=None): - super(InheritanceKeyValueStore, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.inherited_settings = inherited_settings or {} self._fields = initial_values or {} diff --git a/common/lib/xmodule/xmodule/modulestore/mixed.py b/common/lib/xmodule/xmodule/modulestore/mixed.py index 50d262a8c4b6095bbb3d11c6788c27e1b80b4441..8450032de833b061b8f9a4d7a4859792fc7f5868 100644 --- a/common/lib/xmodule/xmodule/modulestore/mixed.py +++ b/common/lib/xmodule/xmodule/modulestore/mixed.py @@ -11,7 +11,6 @@ import itertools import logging from contextlib import contextmanager -import six from contracts import contract, new_contract from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import AssetKey, CourseKey @@ -28,10 +27,7 @@ new_contract('CourseKey', CourseKey) new_contract('AssetKey', AssetKey) new_contract('AssetMetadata', AssetMetadata) new_contract('LibraryLocator', LibraryLocator) -if six.PY2: - new_contract('long', long) # lint-amnesty, pylint: disable=undefined-variable -else: - new_contract('long', int) +new_contract('long', int) log = logging.getLogger(__name__) @@ -85,7 +81,7 @@ def strip_key(func): if isinstance(field_value, list): field_value = [strip_key_func(fv) for fv in field_value] elif isinstance(field_value, dict): - for key, val in six.iteritems(field_value): + for key, val in field_value.items(): field_value[key] = strip_key_func(val) else: field_value = strip_key_func(field_value) @@ -125,7 +121,7 @@ def prepare_asides_to_store(asides_source): asides = [] for asd in asides_source: aside_fields = {} - for asd_field_key, asd_field_val in six.iteritems(asd.fields): + for asd_field_key, asd_field_val in asd.fields.items(): aside_fields[asd_field_key] = asd_field_val.read_from(asd) asides.append({ 'aside_type': asd.scope_ids.block_type, @@ -154,7 +150,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): Initialize a MixedModuleStore. Here we look into our passed in kwargs which should be a collection of other modulestore configuration information """ - super(MixedModuleStore, self).__init__(contentstore, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(contentstore, **kwargs) if create_modulestore_instance is None: raise ValueError('MixedModuleStore constructor must be passed a create_modulestore_instance function') @@ -162,7 +158,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): self.modulestores = [] self.mappings = {} - for course_id, store_name in six.iteritems(mappings): + for course_id, store_name in mappings.items(): try: self.mappings[CourseKey.from_string(course_id)] = store_name except InvalidKeyError: @@ -182,7 +178,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): signal_handler=signal_handler, ) # replace all named pointers to the store into actual pointers - for course_key, store_name in six.iteritems(self.mappings): + for course_key, store_name in self.mappings.items(): if store_name == key: self.mappings[course_key] = store self.modulestores.append(store) @@ -306,7 +302,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): # Check if course is indeed unique. Save it in result if unique if course_id in course_summaries: log.warning( - u"Modulestore %s have duplicate courses %s; skipping from result.", store, course_id + "Modulestore %s have duplicate courses %s; skipping from result.", store, course_id ) else: course_summaries[course_id] = course_summary @@ -338,10 +334,10 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): for store in self.modulestores: if not hasattr(store, 'get_library_keys'): continue - all_library_keys |= set( + all_library_keys |= { self._clean_locator_for_mapping(library_key) for library_key in store.get_library_keys() - ) + } return list(all_library_keys) @strip_key @@ -386,7 +382,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): This key may represent a course that doesn't exist in this modulestore. """ # If there is a mapping that match this org/course/run, use that - for course_id, store in six.iteritems(self.mappings): + for course_id, store in self.mappings.items(): candidate_key = store.make_course_key(org, course, run) if candidate_key == course_id: return candidate_key @@ -745,7 +741,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): dest_course_id.course, dest_course_id.run, fields, **kwargs) # the super handles assets and any other necessities - super(MixedModuleStore, self).clone_course(source_course_id, dest_course_id, user_id, fields, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().clone_course(source_course_id, dest_course_id, user_id, fields, **kwargs) else: raise NotImplementedError("No code for cloning from {} to {}".format( source_modulestore, dest_modulestore @@ -916,7 +912,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): # could be done in parallel threads if needed return dict( itertools.chain.from_iterable( - six.iteritems(store.heartbeat()) + store.heartbeat().items() for store in self.modulestores ) ) @@ -994,7 +990,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): if hasattr(store, method): return store else: - raise NotImplementedError(u"Cannot call {} on store {}".format(method, store)) + raise NotImplementedError(f"Cannot call {method} on store {store}") @property def default_modulestore(self): @@ -1017,7 +1013,7 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): # find the store corresponding to the given type store = next((store for store in self.modulestores if store.get_modulestore_type() == store_type), None) if not store: - raise Exception(u"Cannot find store of type {}".format(store_type)) + raise Exception(f"Cannot find store of type {store_type}") prev_thread_local_store = getattr(self.thread_cache, 'default_store', None) try: diff --git a/common/lib/xmodule/xmodule/modulestore/modulestore_settings.py b/common/lib/xmodule/xmodule/modulestore/modulestore_settings.py index 5f9aa72df1bc27f2073065ce30fa96fb63f86ed7..05470cf8f1ef28502e26bb6d6ef7dbe2377c572e 100644 --- a/common/lib/xmodule/xmodule/modulestore/modulestore_settings.py +++ b/common/lib/xmodule/xmodule/modulestore/modulestore_settings.py @@ -6,8 +6,6 @@ This file contains helper functions for configuring module_store_setting setting import copy import warnings -import six - def convert_module_store_setting_if_needed(module_store_setting): """ @@ -19,7 +17,7 @@ def convert_module_store_setting_if_needed(module_store_setting): Converts and returns the given stores in old (unordered) dict-style format to the new (ordered) list format """ new_store_list = [] - for store_name, store_settings in six.iteritems(old_stores): + for store_name, store_settings in old_stores.items(): store_settings['NAME'] = store_name if store_name == 'default': @@ -125,7 +123,7 @@ def update_module_store_settings( mixed_stores.remove(store) mixed_stores.insert(0, store) return - raise Exception("Could not find setting for requested default store: {}".format(default_store)) + raise Exception(f"Could not find setting for requested default store: {default_store}") if mappings and 'mappings' in module_store_setting['default']['OPTIONS']: module_store_setting['default']['OPTIONS']['mappings'] = mappings diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/base.py b/common/lib/xmodule/xmodule/modulestore/mongo/base.py index 52491a7efaa61d5259bb16892d95fac7b141faef..7fa9912d826f1b7466261f2fa8aaa665a782e153 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/base.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/base.py @@ -22,7 +22,6 @@ from importlib import import_module from uuid import uuid4 import pymongo -import six from bson.son import SON from contracts import contract, new_contract from fs.osfs import OSFS @@ -58,10 +57,7 @@ log = logging.getLogger(__name__) new_contract('CourseKey', CourseKey) new_contract('AssetKey', AssetKey) new_contract('AssetMetadata', AssetMetadata) -if six.PY2: - new_contract('long', long) # lint-amnesty, pylint: disable=undefined-variable -else: - new_contract('long', int) +new_contract('long', int) new_contract('BlockUsageLocator', BlockUsageLocator) # sort order that returns DRAFT items first @@ -70,9 +66,9 @@ SORT_REVISION_FAVOR_DRAFT = ('_id.revision', pymongo.DESCENDING) # sort order that returns PUBLISHED items first SORT_REVISION_FAVOR_PUBLISHED = ('_id.revision', pymongo.ASCENDING) -BLOCK_TYPES_WITH_CHILDREN = list(set( +BLOCK_TYPES_WITH_CHILDREN = list({ name for name, class_ in XBlock.load_classes() if getattr(class_, 'has_children', False) -)) +}) # Allow us to call _from_deprecated_(son|string) throughout the file # pylint: disable=protected-access @@ -81,7 +77,7 @@ BLOCK_TYPES_WITH_CHILDREN = list(set( _OSFS_INSTANCE = {} -class MongoRevisionKey(object): +class MongoRevisionKey: """ Key Revision constants to use for Location and Usage Keys in the Mongo modulestore Note: These values are persisted in the database, so should not be changed without migrations @@ -104,7 +100,7 @@ class MongoKeyValueStore(InheritanceKeyValueStore): known to the MongoModuleStore (data, children, and metadata) """ def __init__(self, data, parent, children, metadata): - super(MongoKeyValueStore, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() if not isinstance(data, dict): self._data = {'data': data} else: @@ -184,10 +180,10 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li def __repr__(self): return "CachingDescriptorSystem{!r}".format(( self.modulestore, - six.text_type(self.course_id), - [six.text_type(key) for key in self.module_data.keys()], + str(self.course_id), + [str(key) for key in self.module_data.keys()], self.default_class, - [six.text_type(key) for key in self.cached_metadata.keys()], + [str(key) for key in self.cached_metadata.keys()], )) def __init__(self, modulestore, course_key, module_data, default_class, cached_metadata, **kwargs): @@ -214,7 +210,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li id_manager = CourseLocationManager(course_key) kwargs.setdefault('id_reader', id_manager) kwargs.setdefault('id_generator', id_manager) - super(CachingDescriptorSystem, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments + super().__init__( field_data=None, load_item=self.load_item, **kwargs @@ -266,7 +262,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li parent = None if self.cached_metadata is not None: # fish the parent out of here if it's available - parent_url = self.cached_metadata.get(six.text_type(location), {}).get('parent', {}).get( + parent_url = self.cached_metadata.get(str(location), {}).get('parent', {}).get( ModuleStoreEnum.Branch.published_only if location.branch is None else ModuleStoreEnum.Branch.draft_preferred ) @@ -282,7 +278,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li ) data = definition.get('data', {}) - if isinstance(data, six.string_types): + if isinstance(data, str): data = {'data': data} mixed_class = self.mixologist.mix(class_) @@ -306,7 +302,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li # Convert the serialized fields values in self.cached_metadata # to python values - metadata_to_inherit = self.cached_metadata.get(six.text_type(non_draft_loc), {}) + metadata_to_inherit = self.cached_metadata.get(str(non_draft_loc), {}) inherit_metadata(module, metadata_to_inherit) module._edit_info = json_data.get('edit_info') @@ -352,7 +348,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li :param jsonfields: a dict of the jsonified version of the fields """ result = {} - for field_name, value in six.iteritems(jsonfields): + for field_name, value in jsonfields.items(): field = class_.fields.get(field_name) if field is None: continue @@ -366,7 +362,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li ] elif isinstance(field, ReferenceValueDict): result[field_name] = { - key: self._convert_reference_to_key(subvalue) for key, subvalue in six.iteritems(value) + key: self._convert_reference_to_key(subvalue) for key, subvalue in value.items() } else: result[field_name] = value @@ -473,7 +469,7 @@ class MongoBulkOpsRecord(BulkOpsRecord): Tracks whether there've been any writes per course and disables inheritance generation """ def __init__(self): - super(MongoBulkOpsRecord, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.dirty = False @@ -506,7 +502,7 @@ class MongoBulkOpsMixin(BulkOperationsMixin): """ Returns whether a bulk operation is in progress for the given course. """ - return super(MongoBulkOpsMixin, self)._is_in_bulk_operation( # lint-amnesty, pylint: disable=super-with-arguments + return super()._is_in_bulk_operation( course_id.for_branch(None), ignore_case ) @@ -516,17 +512,17 @@ class ParentLocationCache(dict): Dict-based object augmented with a more cache-like interface, for internal use. """ - @contract(key=six.text_type) + @contract(key=str) def has(self, key): return key in self - @contract(key=six.text_type, value="BlockUsageLocator | None") + @contract(key=str, value="BlockUsageLocator | None") def set(self, key, value): self[key] = value @contract(value="BlockUsageLocator") def delete_by_value(self, value): - keys_to_delete = [k for k, v in six.iteritems(self) if v == value] + keys_to_delete = [k for k, v in self.items() if v == value] for key in keys_to_delete: del self[key] @@ -555,7 +551,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo :param doc_store_config: must have a host, db, and collection entries. Other common entries: port, tz_aware. """ - super(MongoModuleStore, self).__init__(contentstore=contentstore, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(contentstore=contentstore, **kwargs) def do_connection( db, collection, host, port=27017, tz_aware=True, user=None, password=None, asset_collection=None, **kwargs @@ -617,7 +613,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo If connections is True, then close the connection to the database as well. """ # drop the assets - super(MongoModuleStore, self)._drop_database(database, collections, connections) # lint-amnesty, pylint: disable=super-with-arguments + super()._drop_database(database, collections, connections) connection = self.collection.database.client @@ -673,7 +669,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ParentLocationCaches associated with the current request (if any). """ if self.request_cache is not None: - return self.request_cache.data.setdefault('parent-location-{}'.format(branch), ParentLocationCache()) + return self.request_cache.data.setdefault(f'parent-location-{branch}', ParentLocationCache()) else: return ParentLocationCache() @@ -698,7 +694,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # just get the inheritable metadata since that is all we need for the computation # this minimizes both data pushed over the wire for field_name in InheritanceMixin.fields: - record_filter['metadata.{0}'.format(field_name)] = 1 + record_filter[f'metadata.{field_name}'] = 1 # call out to the DB resultset = self.collection.find(query, record_filter) @@ -713,7 +709,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # manually pick it apart b/c the db has tag and we want as_published revision regardless location = as_published(BlockUsageLocator._from_deprecated_son(result['_id'], course_id.run)) - location_url = six.text_type(location) + location_url = str(location) if location_url in results_by_url: # found either draft or live to complement the other revision # FIXME this is wrong. If the child was moved in draft from one parent to the other, it will @@ -770,12 +766,12 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo course_id = self.fill_in_run(course_id) if not force_refresh: # see if we are first in the request cache (if present) - if self.request_cache is not None and six.text_type(course_id) in self.request_cache.data.get('metadata_inheritance', {}): # lint-amnesty, pylint: disable=line-too-long - return self.request_cache.data['metadata_inheritance'][six.text_type(course_id)] + if self.request_cache is not None and str(course_id) in self.request_cache.data.get('metadata_inheritance', {}): # lint-amnesty, pylint: disable=line-too-long + return self.request_cache.data['metadata_inheritance'][str(course_id)] # then look in any caching subsystem (e.g. memcached) if self.metadata_inheritance_cache_subsystem is not None: - tree = self.metadata_inheritance_cache_subsystem.get(six.text_type(course_id), {}) + tree = self.metadata_inheritance_cache_subsystem.get(str(course_id), {}) else: logging.warning( 'Running MongoModuleStore without a metadata_inheritance_cache_subsystem. This is \ @@ -788,7 +784,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # now write out computed tree to caching subsystem (e.g. memcached), if available if self.metadata_inheritance_cache_subsystem is not None: - self.metadata_inheritance_cache_subsystem.set(six.text_type(course_id), tree) + self.metadata_inheritance_cache_subsystem.set(str(course_id), tree) # now populate a request_cache, if available. NOTE, we are outside of the # scope of the above if: statement so that after a memcache hit, it'll get @@ -798,7 +794,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # defined if 'metadata_inheritance' not in self.request_cache.data: self.request_cache.data['metadata_inheritance'] = {} - self.request_cache.data['metadata_inheritance'][six.text_type(course_id)] = tree + self.request_cache.data['metadata_inheritance'][str(course_id)] = tree return tree @@ -1014,8 +1010,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo course_queries = [] for course_key in course_keys: course_query = { - '_id.{}'.format(value_attr): getattr(course_key, key_attr) - for key_attr, value_attr in six.iteritems({'org': 'org', 'course': 'course', 'run': 'name'}) + f'_id.{value_attr}': getattr(course_key, key_attr) + for key_attr, value_attr in {'org': 'org', 'course': 'course', 'run': 'name'}.items() } course_query.update(query) course_queries.append(course_query) @@ -1141,8 +1137,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo location = course_key.make_usage_key('course', course_key.run) if ignore_case: course_query = location.to_deprecated_son('_id.') - for key in six.iterkeys(course_query): - if isinstance(course_query[key], six.string_types): + for key in course_query.keys(): + if isinstance(course_query[key], str): course_query[key] = re.compile(r"(?i)^{}$".format(course_query[key])) else: course_query = {'_id': location.to_deprecated_son()} @@ -1266,9 +1262,9 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo qualifier_value = {'$in': qualifier_value} query['_id.' + field] = qualifier_value - for key, value in six.iteritems((settings or {})): + for key, value in (settings or {}).items(): query['metadata.' + key] = value - for key, value in six.iteritems((content or {})): + for key, value in (content or {}).items(): query['definition.data.' + key] = value if 'children' in qualifiers: query['definition.children'] = qualifiers.pop('children') @@ -1308,8 +1304,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # Check if a course with this org/course has been defined before (case-insensitive) course_search_location = SON([ ('_id.tag', 'i4x'), - ('_id.org', re.compile(u'^{}$'.format(course_id.org), re.IGNORECASE)), - ('_id.course', re.compile(u'^{}$'.format(course_id.course), re.IGNORECASE)), + ('_id.org', re.compile(f'^{course_id.org}$', re.IGNORECASE)), + ('_id.course', re.compile(f'^{course_id.course}$', re.IGNORECASE)), ('_id.category', 'course'), ]) courses = self.collection.find(course_search_location, projection={'_id': True}) @@ -1323,7 +1319,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo xblock = self.create_item(user_id, course_id, 'course', course_id.run, fields=fields, **kwargs) # create any other necessary things as a side effect - super(MongoModuleStore, self).create_course( # lint-amnesty, pylint: disable=super-with-arguments + super().create_course( org, course, run, user_id, runtime=xblock.runtime, **kwargs ) @@ -1350,7 +1346,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo if block_type == 'course': block_id = course_key.run else: - block_id = u'{}_{}'.format(block_type, uuid4().hex[:5]) + block_id = '{}_{}'.format(block_type, uuid4().hex[:5]) if runtime is None: services = {} @@ -1391,7 +1387,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo for_parent=kwargs.get('for_parent'), ) if fields is not None: - for key, value in six.iteritems(fields): + for key, value in fields.items(): setattr(xmodule, key, value) # decache any pending field settings from init xmodule.save() @@ -1415,7 +1411,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo if block_type == 'course': block_id = course_key.run else: - block_id = u'{}_{}'.format(block_type, uuid4().hex[:5]) + block_id = '{}_{}'.format(block_type, uuid4().hex[:5]) runtime = kwargs.pop('runtime', None) xblock = self.create_xblock(runtime, course_key, block_type, block_id, **kwargs) @@ -1545,7 +1541,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo parent_cache = self._get_parent_cache(self.get_branch_setting()) parent_cache.delete_by_value(xblock.location) for child in xblock.children: - parent_cache.set(six.text_type(child), xblock.location) + parent_cache.set(str(child), xblock.location) self._update_single_item(xblock.scope_ids.usage_id, payload, allow_not_found=allow_not_found) @@ -1579,19 +1575,19 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo :param jsonfields: a dict of the jsonified version of the fields """ jsonfields = {} - for field_name, field in six.iteritems(xblock.fields): + for field_name, field in xblock.fields.items(): if field.scope == scope and field.is_set_on(xblock): if field.scope == Scope.parent: continue elif isinstance(field, Reference): - jsonfields[field_name] = six.text_type(field.read_from(xblock)) + jsonfields[field_name] = str(field.read_from(xblock)) elif isinstance(field, ReferenceList): jsonfields[field_name] = [ - six.text_type(ele) for ele in field.read_from(xblock) + str(ele) for ele in field.read_from(xblock) ] elif isinstance(field, ReferenceValueDict): jsonfields[field_name] = { - key: six.text_type(subvalue) for key, subvalue in six.iteritems(field.read_from(xblock)) + key: str(subvalue) for key, subvalue in field.read_from(xblock).items() } else: jsonfields[field_name] = field.read_json(xblock) @@ -1641,19 +1637,19 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo or revision == ModuleStoreEnum.RevisionOption.draft_preferred # lint-amnesty, pylint: disable=consider-using-in parent_cache = self._get_parent_cache(self.get_branch_setting()) - if parent_cache.has(six.text_type(location)): - return parent_cache.get(six.text_type(location)) + if parent_cache.has(str(location)): + return parent_cache.get(str(location)) # create a query with tag, org, course, and the children field set to the given location query = self._course_key_to_son(location.course_key) - query['definition.children'] = six.text_type(location) + query['definition.children'] = str(location) # if only looking for the PUBLISHED parent, set the revision in the query to None if revision == ModuleStoreEnum.RevisionOption.published_only: query['_id.revision'] = MongoRevisionKey.published def cache_and_return(parent_loc): - parent_cache.set(six.text_type(location), parent_loc) + parent_cache.set(str(location), parent_loc) return parent_loc # query the collection, sorting by DRAFT first @@ -1674,7 +1670,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo if len(non_orphan_parents) > 1: # lint-amnesty, pylint: disable=no-else-raise # should never have multiple PUBLISHED parents raise ReferentialIntegrityError( - u"{} parents claim {}".format(len(parents), location) + "{} parents claim {}".format(len(parents), location) ) else: return cache_and_return(non_orphan_parents[0].replace(run=location.course_key.run)) @@ -1746,7 +1742,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo if item['_id']['category'] != 'course': # It would be nice to change this method to return UsageKeys instead of the deprecated string. item_locs.add( - six.text_type(as_published(BlockUsageLocator._from_deprecated_son(item['_id'], course_key.run))) + str(as_published(BlockUsageLocator._from_deprecated_son(item['_id'], course_key.run))) ) all_reachable = all_reachable.union(item.get('definition', {}).get('children', [])) item_locs -= all_reachable @@ -1797,14 +1793,14 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # A single document exists per course to store the course asset metadata. course_key = self.fill_in_run(course_key) if course_key.run is None: - log.warning(u'No run found for combo org "{}" course "{}" on asset request.'.format( + log.warning('No run found for combo org "{}" course "{}" on asset request.'.format( course_key.org, course_key.course )) course_assets = None else: # Complete course key, so query for asset metadata. course_assets = self.asset_collection.find_one( - {'course_id': six.text_type(course_key)}, + {'course_id': str(course_key)}, ) doc_id = None if course_assets is None else course_assets['_id'] @@ -1814,7 +1810,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo raise ItemNotFoundError(course_key) else: # Course exists, so create matching assets document. - course_assets = {'course_id': six.text_type(course_key), 'assets': {}} + course_assets = {'course_id': str(course_key), 'assets': {}} doc_id = self.asset_collection.insert_one(course_assets).inserted_id elif isinstance(course_assets['assets'], list): # This record is in the old course assets format. @@ -1833,7 +1829,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo """ Given a asset type, form a key needed to update the proper embedded field in the Mongo doc. """ - return 'assets.{}'.format(asset_type) + return f'assets.{asset_type}' @contract(asset_metadata_list='list(AssetMetadata)', user_id='int|long') def _save_asset_metadata_list(self, asset_metadata_list, user_id, import_only): @@ -1851,7 +1847,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # Build an update set with potentially multiple embedded fields. updates_by_type = {} - for asset_type, assets in six.iteritems(assets_by_type): + for asset_type, assets in assets_by_type.items(): updates_by_type[self._make_mongo_asset_key(asset_type)] = list(assets) # Update the document. @@ -1904,8 +1900,8 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo dest_course_key (CourseKey): identifier of course to copy to """ source_assets = self._find_course_assets(source_course_key) - dest_assets = {'assets': source_assets.asset_md.copy(), 'course_id': six.text_type(dest_course_key)} - self.asset_collection.delete_many({'course_id': six.text_type(dest_course_key)}) + dest_assets = {'assets': source_assets.asset_md.copy(), 'course_id': str(dest_course_key)} + self.asset_collection.delete_many({'course_id': str(dest_course_key)}) # Update the document. self.asset_collection.insert_one(dest_assets) @@ -1991,7 +1987,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo self.database.client.admin.command('ismaster') return {ModuleStoreEnum.Type.mongo: True} except pymongo.errors.ConnectionFailure: - raise HeartbeatFailure("Can't connect to {}".format(self.database.name), 'mongo') # lint-amnesty, pylint: disable=raise-missing-from + raise HeartbeatFailure(f"Can't connect to {self.database.name}", 'mongo') # lint-amnesty, pylint: disable=raise-missing-from def ensure_indexes(self): """ diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/draft.py b/common/lib/xmodule/xmodule/modulestore/mongo/draft.py index 3f2dc91f34e1d118ff849e351785995c5cb2658c..29c2536f3372ef41f846fe3a9c7a9c792f0c38ec 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/draft.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/draft.py @@ -10,10 +10,8 @@ and otherwise returns i4x://org/course/cat/name). import logging import pymongo -import six from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.locator import BlockUsageLocator -from six import text_type from xblock.core import XBlock from openedx.core.lib.cache_utils import request_cached @@ -173,7 +171,7 @@ class DraftModuleStore(MongoModuleStore): # Note: does not need to inform the bulk mechanism since after the course is deleted, # it can't calculate inheritance anyway. Nothing is there to be dirty. # delete the assets - super(DraftModuleStore, self).delete_course(course_key, user_id) # lint-amnesty, pylint: disable=super-with-arguments + super().delete_course(course_key, user_id) # lint-amnesty, pylint: disable=super-with-arguments # delete all of the db records for the course course_query = self._course_key_to_son(course_key) @@ -189,7 +187,7 @@ class DraftModuleStore(MongoModuleStore): """ # check to see if the source course is actually there if not self.has_course(source_course_id): - raise ItemNotFoundError("Cannot find a course at {0}. Aborting".format(source_course_id)) + raise ItemNotFoundError(f"Cannot find a course at {source_course_id}. Aborting") with self.bulk_operations(dest_course_id): # verify that the dest_location really is an empty course @@ -199,14 +197,14 @@ class DraftModuleStore(MongoModuleStore): if self.collection.count_documents(query, limit=1) > 0: raise DuplicateCourseError( dest_course_id, - "Course at destination {0} is not an empty course. " + "Course at destination {} is not an empty course. " "You can only clone into an empty course. Aborting...".format( dest_course_id ) ) # clone the assets - super(DraftModuleStore, self).clone_course(source_course_id, dest_course_id, user_id, fields) # lint-amnesty, pylint: disable=super-with-arguments + super().clone_course(source_course_id, dest_course_id, user_id, fields) # lint-amnesty, pylint: disable=super-with-arguments # get the whole old course new_course = self.get_course(dest_course_id) @@ -217,7 +215,7 @@ class DraftModuleStore(MongoModuleStore): ) else: # update fields on existing course - for key, value in six.iteritems(fields): + for key, value in fields.items(): setattr(new_course, key, value) self.update_item(new_course, user_id) @@ -242,7 +240,7 @@ class DraftModuleStore(MongoModuleStore): log.info("Cloning module %s to %s....", original_loc, module.location) - if 'data' in module.fields and module.fields['data'].is_set_on(module) and isinstance(module.data, six.string_types): # lint-amnesty, pylint: disable=line-too-long + if 'data' in module.fields and module.fields['data'].is_set_on(module) and isinstance(module.data, str): # lint-amnesty, pylint: disable=line-too-long module.data = rewrite_nonportable_content_links( original_loc.course_key, dest_course_id, module.data ) @@ -275,7 +273,7 @@ class DraftModuleStore(MongoModuleStore): # create a query to find all items in the course that have the given location listed as a child query = self._course_key_to_son(location.course_key) - query['definition.children'] = text_type(location) + query['definition.children'] = str(location) # find all the items that satisfy the query parents = self.collection.find(query, {'_id': True}, sort=[SORT_REVISION_FAVOR_DRAFT]) @@ -319,7 +317,7 @@ class DraftModuleStore(MongoModuleStore): revision = ModuleStoreEnum.RevisionOption.published_only \ if self.get_branch_setting() == ModuleStoreEnum.Branch.published_only \ else ModuleStoreEnum.RevisionOption.draft_preferred - return super(DraftModuleStore, self).get_parent_location(location, revision, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_parent_location(location, revision, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments def create_xblock(self, runtime, course_key, block_type, block_id=None, fields=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ @@ -332,7 +330,7 @@ class DraftModuleStore(MongoModuleStore): :param runtime: if you already have an xmodule from the course, the xmodule.runtime value :param fields: a dictionary of field names and values for the new xmodule """ - new_block = super(DraftModuleStore, self).create_xblock( # lint-amnesty, pylint: disable=super-with-arguments + new_block = super().create_xblock( # lint-amnesty, pylint: disable=super-with-arguments runtime, course_key, block_type, block_id, fields, **kwargs ) new_block.location = self.for_branch_setting(new_block.location) @@ -481,13 +479,13 @@ class DraftModuleStore(MongoModuleStore): # if the revision is published, defer to base if draft_loc.branch == MongoRevisionKey.published: - item = super(DraftModuleStore, self).update_item(xblock, user_id, allow_not_found) # lint-amnesty, pylint: disable=super-with-arguments + item = super().update_item(xblock, user_id, allow_not_found) # lint-amnesty, pylint: disable=super-with-arguments course_key = xblock.location.course_key if isPublish or (item.category in DIRECT_ONLY_CATEGORIES and not child_update): self._flag_publish_event(course_key) return item - if not super(DraftModuleStore, self).has_item(draft_loc): # lint-amnesty, pylint: disable=super-with-arguments + if not super().has_item(draft_loc): # lint-amnesty, pylint: disable=super-with-arguments try: # ignore any descendants which are already draft self._convert_to_draft(xblock.location, user_id, ignore_if_draft=True) @@ -499,7 +497,7 @@ class DraftModuleStore(MongoModuleStore): raise xblock.location = draft_loc - super(DraftModuleStore, self).update_item(xblock, user_id, allow_not_found, isPublish=isPublish) # lint-amnesty, pylint: disable=super-with-arguments + super().update_item(xblock, user_id, allow_not_found, isPublish=isPublish) # lint-amnesty, pylint: disable=super-with-arguments return wrap_draft(xblock) def delete_item(self, location, user_id, revision=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ @@ -560,7 +558,7 @@ class DraftModuleStore(MongoModuleStore): if self.collection.count_documents(query) > 1: continue - parent_block = super(DraftModuleStore, self).get_item(parent_location) # lint-amnesty, pylint: disable=super-with-arguments + parent_block = super().get_item(parent_location) # lint-amnesty, pylint: disable=super-with-arguments parent_block.children.remove(location) parent_block.location = parent_location # ensure the location is with the correct revision self.update_item(parent_block, user_id, child_update=True) @@ -663,7 +661,7 @@ class DraftModuleStore(MongoModuleStore): @request_cached( # use the XBlock's location value in the cache key - arg_map_function=lambda arg: six.text_type(arg.location if isinstance(arg, XBlock) else arg), + arg_map_function=lambda arg: str(arg.location if isinstance(arg, XBlock) else arg), # use this store's request_cache request_cache_getter=lambda args, kwargs: args[1], ) @@ -858,7 +856,7 @@ class DraftModuleStore(MongoModuleStore): try: source_item = self.get_item(item_location) except ItemNotFoundError: - log.error('Unable to find the item %s', six.text_type(item_location)) + log.error('Unable to find the item %s', str(item_location)) return if source_item.parent and source_item.parent.block_id != original_parent_location.block_id: @@ -867,7 +865,7 @@ class DraftModuleStore(MongoModuleStore): def _query_children_for_cache_children(self, course_key, items): # first get non-draft in a round-trip - to_process_non_drafts = super(DraftModuleStore, self)._query_children_for_cache_children(course_key, items) # lint-amnesty, pylint: disable=super-with-arguments + to_process_non_drafts = super()._query_children_for_cache_children(course_key, items) # lint-amnesty, pylint: disable=super-with-arguments to_process_dict = {} for non_draft in to_process_non_drafts: diff --git a/common/lib/xmodule/xmodule/modulestore/mongoengine_fields.py b/common/lib/xmodule/xmodule/modulestore/mongoengine_fields.py index 5e5e85309cf56a88d4fc0c9939aa0987df7962a4..532d5f3a00c32bc8c78d1c82fab841027697a47d 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongoengine_fields.py +++ b/common/lib/xmodule/xmodule/modulestore/mongoengine_fields.py @@ -4,11 +4,9 @@ Custom field types for mongoengine import mongoengine -import six from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locations import Location -from six import text_type class CourseKeyField(mongoengine.StringField): @@ -17,7 +15,7 @@ class CourseKeyField(mongoengine.StringField): """ def __init__(self, **kwargs): # it'd be useful to add init args such as support_deprecated, force_deprecated - super(CourseKeyField, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(**kwargs) def to_mongo(self, course_key): # lint-amnesty, pylint: disable=arguments-differ """ @@ -26,7 +24,7 @@ class CourseKeyField(mongoengine.StringField): assert isinstance(course_key, (type(None), CourseKey)) if course_key: # don't call super as base.BaseField.to_mongo calls to_python() for some odd reason - return text_type(course_key) + return str(course_key) else: return None @@ -35,21 +33,21 @@ class CourseKeyField(mongoengine.StringField): Deserialize to a CourseKey instance """ # calling super b/c it decodes utf (and doesn't have circularity of from_python) - course_key = super(CourseKeyField, self).to_python(course_key) # lint-amnesty, pylint: disable=super-with-arguments - assert isinstance(course_key, (type(None), six.string_types, CourseKey)) + course_key = super().to_python(course_key) + assert isinstance(course_key, (type(None), (str,), CourseKey)) if course_key == '': return None - if isinstance(course_key, six.string_types): + if isinstance(course_key, str): return CourseKey.from_string(course_key) else: return course_key def validate(self, value): - assert isinstance(value, (type(None), six.string_types, CourseKey)) + assert isinstance(value, (type(None), str, CourseKey)) if isinstance(value, CourseKey): - return super(CourseKeyField, self).validate(text_type(value)) # lint-amnesty, pylint: disable=super-with-arguments + return super().validate(str(value)) else: - return super(CourseKeyField, self).validate(value) # lint-amnesty, pylint: disable=super-with-arguments + return super().validate(value) def prepare_query_value(self, _opt, value): return self.to_mongo(value) @@ -66,27 +64,27 @@ class UsageKeyField(mongoengine.StringField): assert isinstance(location, (type(None), UsageKey)) if location is None: return None - return super(UsageKeyField, self).to_mongo(text_type(location)) # lint-amnesty, pylint: disable=super-with-arguments + return super().to_mongo(str(location)) def to_python(self, location): # lint-amnesty, pylint: disable=arguments-differ """ Deserialize to a UsageKey instance: for now it's a location missing the run """ - assert isinstance(location, (type(None), six.string_types, UsageKey)) + assert isinstance(location, (type(None), str, UsageKey)) if location == '': return None - if isinstance(location, six.string_types): - location = super(UsageKeyField, self).to_python(location) # lint-amnesty, pylint: disable=super-with-arguments + if isinstance(location, str): + location = super().to_python(location) return Location.from_string(location) else: return location def validate(self, value): - assert isinstance(value, (type(None), six.string_types, UsageKey)) + assert isinstance(value, (type(None), str, UsageKey)) if isinstance(value, UsageKey): - return super(UsageKeyField, self).validate(text_type(value)) # lint-amnesty, pylint: disable=super-with-arguments + return super().validate(str(value)) else: - return super(UsageKeyField, self).validate(value) # lint-amnesty, pylint: disable=super-with-arguments + return super().validate(value) def prepare_query_value(self, _opt, value): return self.to_mongo(value) diff --git a/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_asset_xml.py b/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_asset_xml.py index ff8bd47cf386d0ec1428ef89dd702b5e8548c509..e59f44098d04c4b369a15cd807b0e2b445d3d214 100644 --- a/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_asset_xml.py +++ b/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_asset_xml.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Generates fake XML for asset metadata. @@ -11,7 +10,6 @@ from datetime import datetime, timedelta from lxml import etree from opaque_keys.edx.keys import CourseKey -from six.moves import range from xmodule.assetstore import AssetMetadata @@ -24,8 +22,8 @@ except ImportError: ASSET_XSD_FILE = 'assets.xsd' # Characters used in name generation below. -NAME_CHARS = u'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-' -NAME_CHARS_W_UNICODE = NAME_CHARS + u'à ĚŘDžΦШΩΣӔ' +NAME_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-' +NAME_CHARS_W_UNICODE = NAME_CHARS + 'à ĚŘDžΦШΩΣӔ' def coin_flip(): @@ -54,7 +52,7 @@ def filename(): """ Fake a filename. """ - fname = u'' + fname = '' for __ in range(random.randint(10, 30)): fname += random.choice(NAME_CHARS_W_UNICODE) fname += random.choice(('.jpg', '.pdf', '.png', '.txt')) @@ -65,7 +63,7 @@ def pathname(): """ Fake a pathname. """ - pname = u'' + pname = '' for __ in range(random.randint(2, 3)): for __ in range(random.randint(5, 10)): pname += random.choice(NAME_CHARS) @@ -113,7 +111,7 @@ def versions(): """ Version string. """ - return 'v{}.0'.format(ver) + return f'v{ver}.0' return (ver_str(curr_ver), ver_str(prev_ver)) @@ -195,13 +193,13 @@ def validate_xml(xsd_filename, xml_filename): """ Validate a generated XML file against the XSD. """ - with open(xsd_filename, 'r') as f: + with open(xsd_filename) as f: schema_root = etree.XML(f.read()) schema = etree.XMLSchema(schema_root) xmlparser = etree.XMLParser(schema=schema) - with open(xml_filename, 'r') as f: + with open(xml_filename) as f: etree.fromstring(f.read(), xmlparser) if click is not None: diff --git a/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_report.py b/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_report.py index e67589c268b7d578919a17efb7dc603ce8da6dc0..fbecd33bbbd9537cc92bd866333abba8560f807e 100644 --- a/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_report.py +++ b/common/lib/xmodule/xmodule/modulestore/perf_tests/generate_report.py @@ -1,4 +1,3 @@ - """ Reads the data generated by performance tests and generates a savable report which can be viewed over time to examine the performance effects of code changes on @@ -20,7 +19,7 @@ except ImportError: DB_NAME = 'block_times.db' -class HTMLTable(object): +class HTMLTable: """ Simple wrapper for an HTML table. """ @@ -53,7 +52,7 @@ class HTMLTable(object): ) -class HTMLDocument(object): +class HTMLDocument: """ Simple wrapper for an entire HTML document. """ @@ -64,7 +63,7 @@ class HTMLDocument(object): def add_header(self, level, text): """Add a header to the document.""" - func_name = "H{}".format(level) + func_name = f"H{level}" self.body.append(getattr(E, func_name)(text)) def add_to_body(self, elem): @@ -76,7 +75,7 @@ class HTMLDocument(object): return lxml.html.tostring(self.html, pretty_print=pretty_print) -class ReportGenerator(object): +class ReportGenerator: """ Base class for report generation. """ @@ -95,7 +94,7 @@ class ImportExportReportGen(ReportGenerator): Class which generates report for course import/export performance test data. """ def __init__(self, db_name): - super(ImportExportReportGen, self).__init__(db_name) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(db_name) self._read_timing_data() def _read_timing_data(self): @@ -147,7 +146,7 @@ class ImportExportReportGen(ReportGenerator): columns = ["Asset Metadata Amount", ] ms_keys = sorted(self.all_modulestore_combos) for k in ms_keys: - columns.append("{} ({})".format(k, table_type)) + columns.append(f"{k} ({table_type})") phase_table = HTMLTable(columns) # Make a row for each amount of asset metadata. @@ -168,7 +167,7 @@ class ImportExportReportGen(ReportGenerator): value = 0 else: value = (per_amount[modulestore] - per_phase['0'][modulestore]) / float(amount) - row.append("{}".format(value)) + row.append(f"{value}") phase_table.add_row(row) # Add the table title and the table. @@ -183,7 +182,7 @@ class FindReportGen(ReportGenerator): Class which generates report for asset access performance test data. """ def __init__(self, db_name): - super(FindReportGen, self).__init__(db_name) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(db_name) self._read_timing_data() def _read_timing_data(self): @@ -236,7 +235,7 @@ class FindReportGen(ReportGenerator): columns = ["Asset Metadata Amount", ] ms_keys = sorted(self.all_modulestores) for k in ms_keys: - columns.append("Time Taken (ms) ({})".format(k)) + columns.append(f"Time Taken (ms) ({k})") phase_table = HTMLTable(columns) if phase != 'get_asset_list': for amount in sorted(per_phase.keys()): @@ -244,7 +243,7 @@ class FindReportGen(ReportGenerator): row = [amount, ] for modulestore in ms_keys: time_taken = per_amount[modulestore] - row.append("{}".format(time_taken)) + row.append(f"{time_taken}") phase_table.add_row(row) html.add_header(2, phase) html.add_to_body(phase_table.table) @@ -260,7 +259,7 @@ class FindReportGen(ReportGenerator): for modulestore in ms_keys: # Each sort has two different ranges retrieved. time_taken = per_amount[modulestore] / 2.0 - row.append("{}".format(time_taken)) + row.append(f"{time_taken}") sort_table.add_row(row) html.add_header(3, sort) html.add_to_body(sort_table.table) diff --git a/common/lib/xmodule/xmodule/modulestore/perf_tests/test_asset_import_export.py b/common/lib/xmodule/xmodule/modulestore/perf_tests/test_asset_import_export.py index 1dc6a43e333342f08b0e1b293d68b732b5ba115c..1d806e4706b579b4ae7518dd409891527d1a5a71 100644 --- a/common/lib/xmodule/xmodule/modulestore/perf_tests/test_asset_import_export.py +++ b/common/lib/xmodule/xmodule/modulestore/perf_tests/test_asset_import_export.py @@ -61,7 +61,7 @@ class CrossStoreXMLRoundtrip(unittest.TestCase): perf_test = True def setUp(self): - super(CrossStoreXMLRoundtrip, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.export_dir = mkdtemp() self.addCleanup(rmtree, self.export_dir, ignore_errors=True) diff --git a/common/lib/xmodule/xmodule/modulestore/search.py b/common/lib/xmodule/xmodule/modulestore/search.py index 608a967a37d6071c5e44eec6e7c3a7f4821a21bd..ee662ff9892b6bbf68ac738069cd76f470ebadd7 100644 --- a/common/lib/xmodule/xmodule/modulestore/search.py +++ b/common/lib/xmodule/xmodule/modulestore/search.py @@ -3,7 +3,6 @@ from logging import getLogger -from six.moves import range from lms.djangoapps.courseware.masquerade import MASQUERADE_SETTINGS_KEY from common.djangoapps.student.roles import GlobalStaff @@ -182,7 +181,7 @@ def navigation_index(position): try: navigation_position = int(position.split('_', 1)[0]) except (ValueError, TypeError): - LOGGER.exception(u'Bad position %r passed to navigation_index, will assume first position', position) + LOGGER.exception('Bad position %r passed to navigation_index, will assume first position', position) navigation_position = 1 return navigation_position diff --git a/common/lib/xmodule/xmodule/modulestore/split_migrator.py b/common/lib/xmodule/xmodule/modulestore/split_migrator.py index 23b1fbc986c7303e870ff49527b358168cebf084..c773648bbbad90952c24271b66a9d1334e5c9693 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_migrator.py +++ b/common/lib/xmodule/xmodule/modulestore/split_migrator.py @@ -10,9 +10,7 @@ manipulate storage but use existing api's. import logging -import six from opaque_keys.edx.locator import CourseLocator -from six.moves import range from xblock.fields import Reference, ReferenceList, ReferenceValueDict from xmodule.modulestore import ModuleStoreEnum @@ -21,13 +19,13 @@ from xmodule.modulestore.exceptions import ItemNotFoundError log = logging.getLogger(__name__) -class SplitMigrator(object): +class SplitMigrator: """ Copies courses from old mongo to split mongo and sets up location mapping so any references to the old name will be able to find the new elements. """ def __init__(self, split_modulestore, source_modulestore): - super(SplitMigrator, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.split_modulestore = split_modulestore self.source_modulestore = source_modulestore @@ -53,7 +51,7 @@ class SplitMigrator(object): # create the course: set fields to explicitly_set for each scope, id_root = new_course_locator, master_branch = 'production' # lint-amnesty, pylint: disable=line-too-long original_course = self.source_modulestore.get_course(source_course_key, **kwargs) if original_course is None: - raise ItemNotFoundError(six.text_type(source_course_key)) + raise ItemNotFoundError(str(source_course_key)) if new_org is None: new_org = source_course_key.org @@ -144,12 +142,12 @@ class SplitMigrator(object): # was in 'direct' so draft is a new version split_module = self.split_modulestore.get_item(new_locator, **kwargs) # need to remove any no-longer-explicitly-set values and add/update any now set values. - for name, field in six.iteritems(split_module.fields): + for name, field in split_module.fields.items(): if field.is_set_on(split_module) and not module.fields[name].is_set_on(module): field.delete_from(split_module) - for field, value in six.iteritems(self._get_fields_translate_references( + for field, value in self._get_fields_translate_references( module, new_draft_course_loc, published_course_usage_key.block_id, field_names=False - )): + ).items(): field.write_to(split_module, value) _new_module = self.split_modulestore.update_item(split_module, user_id, **kwargs) @@ -165,12 +163,12 @@ class SplitMigrator(object): **kwargs ) awaiting_adoption[module.location] = new_locator - for draft_location, new_locator in six.iteritems(awaiting_adoption): + for draft_location, new_locator in awaiting_adoption.items(): parent_loc = self.source_modulestore.get_parent_location( draft_location, revision=ModuleStoreEnum.RevisionOption.draft_preferred, **kwargs ) if parent_loc is None: - log.warning(u'No parent found in source course for %s', draft_location) + log.warning('No parent found in source course for %s', draft_location) continue old_parent = self.source_modulestore.get_item(parent_loc, **kwargs) split_parent_loc = new_draft_course_loc.make_usage_key( @@ -212,7 +210,7 @@ class SplitMigrator(object): ) result = {} - for field_name, field in six.iteritems(xblock.fields): + for field_name, field in xblock.fields.items(): if field.is_set_on(xblock): field_value = field.read_from(xblock) field_key = field_name if field_names else field @@ -225,7 +223,7 @@ class SplitMigrator(object): elif isinstance(field, ReferenceValueDict): result[field_key] = { key: get_translation(subvalue) - for key, subvalue in six.iteritems(field_value) + for key, subvalue in field_value.items() } else: result[field_key] = field_value diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py index abdf51d333aef06f7e57b02d4b2b34af40993169..0ac90314c34679527ed1bbddb29d16c7e717a861 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py @@ -14,7 +14,7 @@ class BlockKey(namedtuple('BlockKey', 'type id')): # lint-amnesty, pylint: disa @contract(type="string[>0]") def __new__(cls, type, id): # lint-amnesty, pylint: disable=redefined-builtin - return super(BlockKey, cls).__new__(cls, type, id) + return super().__new__(cls, type, id) @classmethod @contract(usage_key=BlockUsageLocator) diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py index 1359a59b63d0c150454648f4664df4e3e5da53dd..6775b239e81ee9c47733a8b2e9f7a8a98186a8ce 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py @@ -3,7 +3,6 @@ import logging import sys -import six from contracts import contract, new_contract from fs.osfs import OSFS from lazy import lazy @@ -70,7 +69,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li kwargs.setdefault('id_reader', id_manager) kwargs.setdefault('id_generator', id_manager) - super(CachingDescriptorSystem, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments + super().__init__( field_data=None, load_item=self._load_item, resources_fs=OSFS(root), @@ -91,7 +90,7 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li @contract(returns="dict(BlockKey: BlockKey)") def _parent_map(self): # lint-amnesty, pylint: disable=missing-function-docstring parent_map = {} - for block_key, block in six.iteritems(self.course_entry.structure['blocks']): + for block_key, block in self.course_entry.structure['blocks'].items(): for child in block.fields.get('children', []): parent_map[child] = block_key return parent_map @@ -382,10 +381,10 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li if aside.scope_ids.block_type == aside_type: return aside - new_aside = super(CachingDescriptorSystem, self).get_aside_of_type(block, aside_type) # lint-amnesty, pylint: disable=super-with-arguments + new_aside = super().get_aside_of_type(block, aside_type) new_aside._field_data = block._field_data # pylint: disable=protected-access - for key, _ in six.iteritems(new_aside.fields): + for key, _ in new_aside.fields.items(): if isinstance(key, KeyValueStore.Key) and block._field_data.has(new_aside, key): # pylint: disable=protected-access try: value = block._field_data.get(new_aside, key) # pylint: disable=protected-access diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/definition_lazy_loader.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/definition_lazy_loader.py index 94c1eca39c362a0393d3be0b413340e84ebacd02..c3fd7133e2cc22f434a31c6b5f834d408852aecc 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/definition_lazy_loader.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/definition_lazy_loader.py @@ -5,7 +5,7 @@ import copy from opaque_keys.edx.locator import DefinitionLocator -class DefinitionLazyLoader(object): +class DefinitionLazyLoader: """ A placeholder to put into an xblock in place of its definition which when accessed knows how to get its content. Only useful if the containing diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py index e3ba02a3028489897efa152d8673143fd65ca5bd..3cefdbe8ac694480ff4662a659920d68c48d0dd4 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py @@ -6,6 +6,7 @@ Segregation of pymongo functions from the data modeling mechanisms for split mod import datetime import logging import math +import pickle import re import zlib from contextlib import contextmanager @@ -13,8 +14,6 @@ from time import time import pymongo import pytz -import six -from six.moves import cPickle as pickle from contracts import check, new_contract from mongodb_proxy import autoretry_read # Import this just to export it @@ -53,7 +52,7 @@ def round_power_2(value): return math.pow(2, math.ceil(math.log(value, 2))) -class Tagger(object): +class Tagger: """ An object used by :class:`QueryTimer` to allow timed code blocks to add measurements and tags to the timer. @@ -94,12 +93,12 @@ class Tagger(object): '{}:{}'.format(name, round_power_2(size)) for name, size in self.measures ] + [ - '{}:{}'.format(name, value) + f'{name}:{value}' for name, value in self.added_tags ] -class QueryTimer(object): +class QueryTimer: """ An object that allows timing a block of code while also recording measurements about that code. @@ -127,7 +126,7 @@ class QueryTimer(object): course_context: The course which the query is being made for. """ tagger = Tagger(self._sample_rate) - metric_name = "{}.{}".format(self._metric_base, metric_name) + metric_name = f"{self._metric_base}.{metric_name}" start = time() # lint-amnesty, pylint: disable=unused-variable try: @@ -135,7 +134,7 @@ class QueryTimer(object): finally: end = time() # lint-amnesty, pylint: disable=unused-variable tags = tagger.tags - tags.append('course:{}'.format(course_context)) + tags.append(f'course:{course_context}') TIMER = QueryTimer(__name__, 0.01) @@ -187,14 +186,14 @@ def structure_to_mongo(structure, course_context=None): check('BlockKey', structure['root']) check('dict(BlockKey: BlockData)', structure['blocks']) - for block in six.itervalues(structure['blocks']): + for block in structure['blocks'].values(): if 'children' in block.fields: check('list(BlockKey)', block.fields['children']) new_structure = dict(structure) new_structure['blocks'] = [] - for block_key, block in six.iteritems(structure['blocks']): + for block_key, block in structure['blocks'].items(): new_block = dict(block.to_storable()) new_block.setdefault('block_type', block_key.type) new_block['block_id'] = block_key.id @@ -203,7 +202,7 @@ def structure_to_mongo(structure, course_context=None): return new_structure -class CourseStructureCache(object): +class CourseStructureCache: """ Wrapper around django cache object to cache course structure objects. The course structures are pickled and compressed when cached. @@ -239,10 +238,7 @@ class CourseStructureCache(object): pickled_data = zlib.decompress(compressed_pickled_data) tagger.measure('uncompressed_size', len(pickled_data)) - if six.PY2: - return pickle.loads(pickled_data) - else: - return pickle.loads(pickled_data, encoding='latin-1') + return pickle.loads(pickled_data, encoding='latin-1') except Exception: # lint-amnesty, pylint: disable=broad-except # The cached data is corrupt in some way, get rid of it. log.warning("CourseStructureCache: Bad data in cache for %s", course_context) @@ -266,7 +262,7 @@ class CourseStructureCache(object): self.cache.set(key, compressed_pickled_data, None) -class MongoConnection(object): +class MongoConnection: """ Segregation of pymongo functions from the data modeling mechanisms for split modulestore. """ @@ -300,7 +296,7 @@ class MongoConnection(object): self.database.client.admin.command('ismaster') return True except pymongo.errors.ConnectionFailure: - raise HeartbeatFailure("Can't connect to {}".format(self.database.name), 'mongo') # lint-amnesty, pylint: disable=raise-missing-from + raise HeartbeatFailure(f"Can't connect to {self.database.name}", 'mongo') # lint-amnesty, pylint: disable=raise-missing-from def get_structure(self, key, course_context=None): """ @@ -322,7 +318,7 @@ class MongoConnection(object): if doc is None: log.warning( "doc was None when attempting to retrieve structure for item with key %s", - six.text_type(key) + str(key) ) return None tagger_find_one.measure("blocks", len(doc['blocks'])) @@ -431,7 +427,7 @@ class MongoConnection(object): with TIMER.timer("get_course_index", key): if ignore_case: query = { - key_attr: re.compile(u'^{}$'.format(re.escape(getattr(key, key_attr))), re.IGNORECASE) + key_attr: re.compile('^{}$'.format(re.escape(getattr(key, key_attr))), re.IGNORECASE) for key_attr in ('org', 'course', 'run') } else: @@ -467,11 +463,11 @@ class MongoConnection(object): query['$or'] = courses_queries else: if branch is not None: - query['versions.{}'.format(branch)] = {'$exists': True} + query[f'versions.{branch}'] = {'$exists': True} if search_targets: - for key, value in six.iteritems(search_targets): - query['search_targets.{}'.format(key)] = value + for key, value in search_targets.items(): + query[f'search_targets.{key}'] = value if org_target: query['org'] = org_target @@ -485,7 +481,7 @@ class MongoConnection(object): courses_queries = [] query = {} if branch: - query = {'versions.{}'.format(branch): {'$exists': True}} + query = {f'versions.{branch}': {'$exists': True}} for course_key in course_keys: course_query = { diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py index d2f490b250f6ae2e9ff45e48bd264cb977743918..ac5674742f543b8419d3993f4c321adadd846ed9 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py @@ -62,7 +62,6 @@ import logging from collections import defaultdict from importlib import import_module -import six from bson.objectid import ObjectId from ccx_keys.locator import CCXBlockUsageLocator, CCXLocator from contracts import contract, new_contract @@ -142,7 +141,7 @@ new_contract('XBlock', XBlock) class SplitBulkWriteRecord(BulkOpsRecord): # lint-amnesty, pylint: disable=missing-class-docstring def __init__(self): - super(SplitBulkWriteRecord, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments + super().__init__() self.initial_index = None self.index = None self.structures = {} @@ -187,7 +186,7 @@ class SplitBulkWriteRecord(BulkOpsRecord): # lint-amnesty, pylint: disable=miss self.structures[structure['_id']] = structure def __repr__(self): - return u"SplitBulkWriteRecord<{!r}, {!r}, {!r}, {!r}, {!r}>".format( + return "SplitBulkWriteRecord<{!r}, {!r}, {!r}, {!r}, {!r}>".format( self._active_count, self.initial_index, self.index, @@ -220,7 +219,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin): return self._bulk_ops_record_type() if not isinstance(course_key, (CourseLocator, LibraryLocator)): - raise TypeError(u'{!r} is not a CourseLocator or LibraryLocator'.format(course_key)) + raise TypeError(f'{course_key!r} is not a CourseLocator or LibraryLocator') # handle version_guid based retrieval locally if course_key.org is None or course_key.course is None or course_key.run is None: return self._active_bulk_ops.records[ @@ -228,7 +227,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin): ] # handle ignore case and general use - return super(SplitBulkWriteMixin, self)._get_bulk_ops_record( # lint-amnesty, pylint: disable=super-with-arguments + return super()._get_bulk_ops_record( course_key.replace(branch=None, version_guid=None), ignore_case ) @@ -237,7 +236,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin): Clear the record for this course """ if not isinstance(course_key, (CourseLocator, LibraryLocator)): - raise TypeError('{!r} is not a CourseLocator or LibraryLocator'.format(course_key)) + raise TypeError(f'{course_key!r} is not a CourseLocator or LibraryLocator') if course_key.org and course_key.course and course_key.run: del self._active_bulk_ops.records[course_key.replace(branch=None, version_guid=None)] @@ -263,7 +262,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin): dirty = False # If the content is dirty, then update the database - for _id in six.viewkeys(bulk_write_record.structures) - bulk_write_record.structures_in_db: + for _id in bulk_write_record.structures.keys() - bulk_write_record.structures_in_db: dirty = True try: @@ -274,7 +273,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin): # append only, so if it's already been written, we can just keep going. log.debug("Attempted to insert duplicate structure %s", _id) - for _id in six.viewkeys(bulk_write_record.definitions) - bulk_write_record.definitions_in_db: + for _id in bulk_write_record.definitions.keys() - bulk_write_record.definitions_in_db: dirty = True try: @@ -455,7 +454,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin): defs_from_db = list(self.db_connection.get_definitions(list(ids), course_key)) defs_dict = {d.get('_id'): d for d in defs_from_db} # Add the retrieved definitions to the cache. - bulk_write_record.definitions_in_db.update(six.iterkeys(defs_dict)) + bulk_write_record.definitions_in_db.update(defs_dict.keys()) bulk_write_record.definitions.update(defs_dict) definitions.extend(defs_from_db) return definitions @@ -571,7 +570,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin): 'search_targets' not in record.index or field not in record.index['search_targets'] or record.index['search_targets'][field] != value - for field, value in six.iteritems(search_targets) + for field, value in search_targets.items() ): continue # if we've specified a filter by org, @@ -718,7 +717,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): :param doc_store_config: must have a host, db, and collection entries. Other common entries: port, tz_aware. """ - super(SplitMongoModuleStore, self).__init__(contentstore, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(contentstore, **kwargs) self.db_connection = MongoConnection(**doc_store_config) @@ -765,7 +764,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): If connections is True, then close the connection to the database as well. """ # drop the assets - super(SplitMongoModuleStore, self)._drop_database(database, collections, connections) # lint-amnesty, pylint: disable=super-with-arguments + super()._drop_database(database, collections, connections) self.db_connection._drop_database(database, collections, connections) # pylint: disable=protected-access @@ -799,14 +798,14 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): course_key, [ block.definition - for block in six.itervalues(new_module_data) + for block in new_module_data.values() ] ) # Turn definitions into a map. definitions = {definition['_id']: definition for definition in descendent_definitions} - for block in six.itervalues(new_module_data): + for block in new_module_data.values(): if block.definition in definitions: definition = definitions[block.definition] # convert_fields gets done later in the runtime's xblock_from_json @@ -917,7 +916,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): entry = self.get_structure(course_key, version_guid) if entry is None: - raise ItemNotFoundError('Structure: {}'.format(version_guid)) + raise ItemNotFoundError(f'Structure: {version_guid}') # b/c more than one course can use same structure, the 'org', 'course', # 'run', and 'branch' are not intrinsic to structure @@ -1065,7 +1064,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): if len(course_block) > 1: raise MultipleCourseBlocksFound( - "Expected 1 course block to be found in the course, but found {0}".format(len(course_block)) + "Expected 1 course block to be found in the course, but found {}".format(len(course_block)) ) course_summary = extract_course_summary(course_block[0]) courses_summaries.append( @@ -1107,7 +1106,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): if len(library_block) > 1: raise MultipleLibraryBlocksFound( - "Expected 1 library block, but found {0}".format(len(library_block)) + "Expected 1 library block, but found {}".format(len(library_block)) ) library_block_fields = library_block[0].fields @@ -1245,7 +1244,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): if len(items) == 0: # lint-amnesty, pylint: disable=no-else-raise raise ItemNotFoundError(usage_key) elif len(items) > 1: - log.debug("Found more than one item for '{}'".format(usage_key)) + log.debug(f"Found more than one item for '{usage_key}'") return items[0] def get_items(self, course_locator, settings=None, content=None, qualifiers=None, include_orphans=True, **kwargs): # lint-amnesty, pylint: disable=arguments-differ @@ -1303,14 +1302,14 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): # odd case where we don't search just confirm block_name = qualifiers.pop('name') block_ids = [] - for block_id, block in six.iteritems(course.structure['blocks']): + for block_id, block in course.structure['blocks'].items(): # Don't do an in comparison blindly; first check to make sure # that the name qualifier we're looking at isn't a plain string; # if it is a string, then it should match exactly. If it's other # than a string, we check whether it contains the block ID; this # is so a list or other iterable can be passed with multiple # valid qualifiers. - if isinstance(block_name, six.string_types): + if isinstance(block_name, str): name_matches = block_id.id == block_name else: name_matches = block_id.id in block_name @@ -1334,7 +1333,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): path_cache = {} parents_cache = self.build_block_key_to_parents_mapping(course.structure) - for block_id, value in six.iteritems(course.structure['blocks']): + for block_id, value in course.structure['blocks'].items(): if _block_matches_all(value): if not include_orphans: if ( @@ -1360,7 +1359,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): :return dict: a dictionary containing mapping of block_keys against their parents. """ children_to_parents = defaultdict(list) - for parent_key, value in six.iteritems(structure['blocks']): + for parent_key, value in structure['blocks'].items(): for child_key in value.fields.get('children', []): children_to_parents[child_key].append(parent_key) @@ -1452,7 +1451,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): items = set(course.structure['blocks'].keys()) items.remove(course.structure['root']) blocks = course.structure['blocks'] - for block_id, block_data in six.iteritems(blocks): + for block_id, block_data in blocks.items(): items.difference_update(BlockKey(*child) for child in block_data.fields.get('children', [])) if block_data.block_type in detached_categories: items.discard(block_id) @@ -1605,7 +1604,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): elif len(possible_roots) == 0: return None # convert the results value sets to locators - for k, versions in six.iteritems(result): + for k, versions in result.items(): result[k] = [ block_locator.for_version(version) for version in versions @@ -1670,10 +1669,10 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): :param user_id: request.user """ def needs_saved(): - for key, value in six.iteritems(new_def_data): + for key, value in new_def_data.items(): if key not in old_definition['fields'] or value != old_definition['fields'][key]: return True - for key, value in six.iteritems(old_definition.get('fields', {})): + for key, value in old_definition.get('fields', {}).items(): if key not in new_def_data: return True @@ -1720,7 +1719,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): # moves, its id won't change and will be confusing serial = 1 while True: - potential_key = BlockKey(category, "{}{}".format(category, serial)) + potential_key = BlockKey(category, f"{category}{serial}") if potential_key not in course_blocks: return potential_key serial += 1 @@ -1910,7 +1909,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): """ source_index = self.get_course_index_info(source_course_id) if source_index is None: - raise ItemNotFoundError("Cannot find a course at {0}. Aborting".format(source_course_id)) + raise ItemNotFoundError(f"Cannot find a course at {source_course_id}. Aborting") with self.bulk_operations(dest_course_id): new_course = self.create_course( @@ -1923,7 +1922,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): **kwargs ) # don't copy assets until we create the course in case something's awry - super(SplitMongoModuleStore, self).clone_course(source_course_id, dest_course_id, user_id, fields, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + super().clone_course(source_course_id, dest_course_id, user_id, fields, **kwargs) return new_course DEFAULT_ROOT_COURSE_BLOCK_ID = 'course' @@ -2116,7 +2115,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): with self.bulk_operations(course_key): if allow_not_found and isinstance(block_key.id, (LocalId, type(None))): fields = {} - for subfields in six.itervalues(partitioned_fields): + for subfields in partitioned_fields.values(): fields.update(subfields) return self.create_item( user_id, course_key, block_key.type, fields=fields, asides=asides, force=force @@ -2129,7 +2128,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): if original_entry is None: if allow_not_found: fields = {} - for subfields in six.itervalues(partitioned_fields): + for subfields in partitioned_fields.values(): fields.update(subfields) return self.create_item(user_id, course_key, block_key.type, block_id=block_key.id, fields=fields, asides=asides, force=force) @@ -2260,7 +2259,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): BlockData(**json_data), **kwargs ) - for field_name, value in six.iteritems((fields or {})): + for field_name, value in (fields or {}).items(): setattr(new_block, field_name, value) if parent_xblock is not None: @@ -2437,7 +2436,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): # must be copying the dag root if there's no current dag root_block_key = source_structure['root'] if not any(root_block_key == BlockKey.from_usage_key(subtree) for subtree in subtree_list): - raise ItemNotFoundError(u'Must publish course root {}'.format(root_block_key)) + raise ItemNotFoundError(f'Must publish course root {root_block_key}') root_source = source_structure['blocks'][root_block_key] # create branch destination_structure = self._new_structure( @@ -2600,7 +2599,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): # Compute a new block ID. This new block ID must be consistent when this # method is called with the same (source_key, dest_structure) pair unique_data = "{}:{}:{}".format( - six.text_type(hashable_source_id).encode("utf-8"), + str(hashable_source_id).encode("utf-8"), block_key.id, new_parent_block_key.id, ) @@ -2640,7 +2639,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): # Setting it to the source_block_info structure version here breaks split_draft's has_changes() method. new_block_info.edit_info.edited_by = user_id new_block_info.edit_info.edited_on = datetime.datetime.now(UTC) - new_block_info.edit_info.original_usage = six.text_type(usage_key.replace(branch=None, version_guid=None)) + new_block_info.edit_info.original_usage = str(usage_key.replace(branch=None, version_guid=None)) new_block_info.edit_info.original_usage_version = source_block_info.edit_info.update_version dest_structure['blocks'][new_block_key] = new_block_info @@ -2733,7 +2732,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): """ # create mapping from each child's key to its parents' keys child_parent_map = defaultdict(set) - for block_key, block_data in six.iteritems(blocks): + for block_key, block_data in blocks.items(): for child in block_data.fields.get('children', []): child_parent_map[BlockKey(*child)].add(block_key) @@ -2764,7 +2763,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): edited_by won't reflect the originals, of course. """ # this is the only real delete in the system. should it do something else? - log.info(u"deleting course from split-mongo: %s", course_key) + log.info("deleting course from split-mongo: %s", course_key) self.delete_course_index(course_key) # We do NOT call the super class here since we need to keep the assets @@ -2805,7 +2804,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): for child in block_fields.get('children', []): try: if child in inherited_from: - raise Exception(u'Infinite loop detected when inheriting to {}, having already inherited from {}'.format(child, inherited_from)) # lint-amnesty, pylint: disable=line-too-long + raise Exception(f'Infinite loop detected when inheriting to {child}, having already inherited from {inherited_from}') # lint-amnesty, pylint: disable=line-too-long self.inherit_settings( block_map, BlockKey(*child), @@ -2855,8 +2854,8 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): try: course_assets = self._lookup_course(course_key).structure.get('assets', {}) except (InsufficientSpecificationError, VersionConflictError) as err: # lint-amnesty, pylint: disable=unused-variable - log.warning(u'Error finding assets for org "%s" course "%s" on asset ' - u'request. Either version of course_key is None or invalid.', + log.warning('Error finding assets for org "%s" course "%s" on asset ' + 'request. Either version of course_key is None or invalid.', course_key.org, course_key.course) return {} @@ -2912,7 +2911,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): course_key, asset_metadata_list, course_assets, user_id, import_only ) - for asset_type, assets in six.iteritems(assets_by_type): + for asset_type, assets in assets_by_type.items(): new_structure['assets'][asset_type] = list(assets) # update index if appropriate and structures @@ -3021,7 +3020,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): original_structure = self._lookup_course(course_locator).structure index_entry = self._get_index_if_valid(course_locator) new_structure = self.version_structure(course_locator, original_structure, user_id) - for block in six.itervalues(new_structure['blocks']): + for block in new_structure['blocks'].values(): if 'children' in block.fields: block.fields['children'] = [ block_id for block_id in block.fields['children'] @@ -3064,7 +3063,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): xblock_class = self.mixologist.mix(xblock_class) # Make a shallow copy, so that we aren't manipulating a cached field dictionary output_fields = dict(jsonfields) - for field_name, value in six.iteritems(output_fields): + for field_name, value in output_fields.items(): if value: try: field = xblock_class.fields.get(field_name) @@ -3075,7 +3074,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): elif isinstance(field, ReferenceList): output_fields[field_name] = [robust_usage_key(ele) for ele in value] elif isinstance(field, ReferenceValueDict): - for key, subvalue in six.iteritems(value): + for key, subvalue in value.items(): value[key] = robust_usage_key(subvalue) return output_fields @@ -3124,7 +3123,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): :param fields: a dictionary of fields and values usually only those explicitly set and already ready for persisting (e.g., references converted to block_ids) """ - for field_name, field_value in six.iteritems(fields): + for field_name, field_value in fields.items(): if field_name in self.SEARCH_TARGET_DICT: index_entry.setdefault('search_targets', {})[field_name] = field_value @@ -3137,7 +3136,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): :param new_id: """ if not isinstance(new_id, ObjectId): - raise TypeError('new_id must be an ObjectId, but is {!r}'.format(new_id)) + raise TypeError(f'new_id must be an ObjectId, but is {new_id!r}') index_entry['versions'][branch] = new_id self.update_course_index(course_key, index_entry) @@ -3148,7 +3147,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): # explicitly_set_fields_by_scope converts to json; so, avoiding it # the existing partition_fields_by_scope works on a dict not an xblock result = defaultdict(dict) - for field in six.itervalues(xblock.fields): + for field in xblock.fields.values(): if field.is_set_on(xblock): result[field.scope][field.name] = field.read_from(xblock) return result @@ -3171,13 +3170,13 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): Handle client possibly setting field to strings rather than keys to get the block_id """ # perhaps replace by fixing the views or Field Reference*.from_json to return a Key - if isinstance(reference, six.string_types): + if isinstance(reference, str): reference = BlockUsageLocator.from_string(reference) elif isinstance(reference, BlockKey): return reference return BlockKey.from_usage_key(reference) - for field_name, value in six.iteritems(fields): + for field_name, value in fields.items(): if value is not None: if isinstance(xblock_class.fields[field_name], Reference): fields[field_name] = reference_block_id(value) @@ -3186,7 +3185,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): reference_block_id(ele) for ele in value ] elif isinstance(xblock_class.fields[field_name], ReferenceValueDict): - for key, subvalue in six.iteritems(value): + for key, subvalue in value.items(): value[key] = reference_block_id(subvalue) # should this recurse down dicts and lists just in case they contain datetime? elif not isinstance(value, datetime.datetime): # don't convert datetimes! @@ -3229,7 +3228,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): """ return [ parent_block_key - for parent_block_key, value in six.iteritems(structure['blocks']) + for parent_block_key, value in structure['blocks'].items() if block_key in value.fields.get('children', []) ] @@ -3301,7 +3300,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): block_defaults=new_block.defaults ) # Extend the block's new edit_info with any extra edit_info fields from the source (e.g. original_usage): - for key, val in six.iteritems(new_block.edit_info.to_storable()): + for key, val in new_block.edit_info.to_storable().items(): if getattr(destination_block.edit_info, key) is None: setattr(destination_block.edit_info, key, val) @@ -3408,7 +3407,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): result_list.append(aside) if tmp_new_asides_data: - for _, asd in six.iteritems(tmp_new_asides_data): + for _, asd in tmp_new_asides_data.items(): result_list.append(asd) updated = True diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py index 83963cc8ae2130933a65fb64f472d599cc8956d4..0f83cde52b22fe6f6e30c894a3e04eabb89fdc88 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py @@ -38,7 +38,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli """ master_branch = kwargs.pop('master_branch', ModuleStoreEnum.BranchName.draft) with self.bulk_operations(CourseLocator(org, course, run), ignore_case=True): - item = super(DraftVersioningModuleStore, self).create_course( # lint-amnesty, pylint: disable=super-with-arguments + item = super().create_course( org, course, run, user_id, master_branch=master_branch, **kwargs ) if master_branch == ModuleStoreEnum.BranchName.draft and not skip_auto_publish: @@ -59,7 +59,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli def get_course(self, course_id, depth=0, **kwargs): course_id = self._map_revision_to_branch(course_id) - return super(DraftVersioningModuleStore, self).get_course(course_id, depth=depth, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_course(course_id, depth=depth, **kwargs) def get_library(self, library_id, depth=0, head_validation=True, **kwargs): if not head_validation and library_id.version_guid: @@ -67,14 +67,14 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli self, library_id, depth=depth, head_validation=head_validation, **kwargs ) library_id = self._map_revision_to_branch(library_id) - return super(DraftVersioningModuleStore, self).get_library(library_id, depth=depth, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_library(library_id, depth=depth, **kwargs) def clone_course(self, source_course_id, dest_course_id, user_id, fields=None, revision=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ See :py:meth: xmodule.modulestore.split_mongo.split.SplitMongoModuleStore.clone_course """ dest_course_id = self._map_revision_to_branch(dest_course_id, revision=revision) - return super(DraftVersioningModuleStore, self).clone_course( # lint-amnesty, pylint: disable=super-with-arguments + return super().clone_course( source_course_id, dest_course_id, user_id, fields=fields, **kwargs ) @@ -84,11 +84,11 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli """ branch_setting = self.get_branch_setting() if branch_setting == ModuleStoreEnum.Branch.draft_preferred: - return super(DraftVersioningModuleStore, self).get_course_summaries( # lint-amnesty, pylint: disable=super-with-arguments + return super().get_course_summaries( ModuleStoreEnum.BranchName.draft, **kwargs ) elif branch_setting == ModuleStoreEnum.Branch.published_only: - return super(DraftVersioningModuleStore, self).get_course_summaries( # lint-amnesty, pylint: disable=super-with-arguments + return super().get_course_summaries( ModuleStoreEnum.BranchName.published, **kwargs ) else: @@ -100,9 +100,9 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli """ branch_setting = self.get_branch_setting() if branch_setting == ModuleStoreEnum.Branch.draft_preferred: - return super(DraftVersioningModuleStore, self).get_courses(ModuleStoreEnum.BranchName.draft, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_courses(ModuleStoreEnum.BranchName.draft, **kwargs) elif branch_setting == ModuleStoreEnum.Branch.published_only: - return super(DraftVersioningModuleStore, self).get_courses(ModuleStoreEnum.BranchName.published, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_courses(ModuleStoreEnum.BranchName.published, **kwargs) else: raise InsufficientSpecificationError() @@ -124,7 +124,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli source_keys = [self._map_revision_to_branch(key) for key in source_keys] dest_key = self._map_revision_to_branch(dest_key) head_validation = kwargs.get('head_validation') - new_keys = super(DraftVersioningModuleStore, self).copy_from_template( # lint-amnesty, pylint: disable=super-with-arguments + new_keys = super().copy_from_template( source_keys, dest_key, user_id, head_validation ) if dest_key.branch == ModuleStoreEnum.BranchName.draft: @@ -148,7 +148,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli or descriptor.location.block_type in DIRECT_ONLY_CATEGORIES with self.bulk_operations(descriptor.location.course_key, emit_signals=emit_signals): - item = super(DraftVersioningModuleStore, self).update_item( # lint-amnesty, pylint: disable=super-with-arguments + item = super().update_item( descriptor, user_id, allow_not_found=allow_not_found, @@ -169,7 +169,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli emit_signals = course_key.branch == ModuleStoreEnum.BranchName.published \ or block_type in DIRECT_ONLY_CATEGORIES with self.bulk_operations(course_key, emit_signals=emit_signals): - item = super(DraftVersioningModuleStore, self).create_item( # lint-amnesty, pylint: disable=super-with-arguments + item = super().create_item( user_id, course_key, block_type, block_id=block_id, definition_locator=definition_locator, fields=fields, asides=asides, force=force, **kwargs @@ -184,7 +184,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli ): parent_usage_key = self._map_revision_to_branch(parent_usage_key) with self.bulk_operations(parent_usage_key.course_key): - item = super(DraftVersioningModuleStore, self).create_child( # lint-amnesty, pylint: disable=super-with-arguments + item = super().create_child( user_id, parent_usage_key, block_type, block_id=block_id, fields=fields, asides=asides, **kwargs ) @@ -238,7 +238,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli self._flag_publish_event(location.course_key) for branch in branches_to_delete: branched_location = location.for_branch(branch) - super(DraftVersioningModuleStore, self).delete_item(branched_location, user_id) # lint-amnesty, pylint: disable=super-with-arguments + super().delete_item(branched_location, user_id) if autopublish_parent: self.publish(parent_loc.version_agnostic(), user_id, blacklist=EXCLUDE_ALL, **kwargs) @@ -274,14 +274,14 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli Returns True if location exists in this ModuleStore. """ usage_key = self._map_revision_to_branch(usage_key, revision=revision) - return super(DraftVersioningModuleStore, self).has_item(usage_key) # lint-amnesty, pylint: disable=super-with-arguments + return super().has_item(usage_key) def get_item(self, usage_key, depth=0, revision=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ Returns the item identified by usage_key and revision. """ usage_key = self._map_revision_to_branch(usage_key, revision=revision) - return super(DraftVersioningModuleStore, self).get_item(usage_key, depth=depth, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_item(usage_key, depth=depth, **kwargs) def get_items(self, course_locator, revision=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ @@ -289,7 +289,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli the given course_locator. """ course_locator = self._map_revision_to_branch(course_locator, revision=revision) - return super(DraftVersioningModuleStore, self).get_items(course_locator, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_items(course_locator, **kwargs) def get_parent_location(self, location, revision=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ ''' @@ -306,7 +306,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli if revision == ModuleStoreEnum.RevisionOption.draft_preferred: revision = ModuleStoreEnum.RevisionOption.draft_only location = self._map_revision_to_branch(location, revision=revision) - return super(DraftVersioningModuleStore, self).get_parent_location(location, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_parent_location(location, **kwargs) def get_block_original_usage(self, usage_key): """ @@ -315,18 +315,18 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli copy was inherited. """ usage_key = self._map_revision_to_branch(usage_key) - return super(DraftVersioningModuleStore, self).get_block_original_usage(usage_key) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_block_original_usage(usage_key) def get_orphans(self, course_key, **kwargs): course_key = self._map_revision_to_branch(course_key) - return super(DraftVersioningModuleStore, self).get_orphans(course_key, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_orphans(course_key, **kwargs) def fix_not_found(self, course_key, user_id): # lint-amnesty, pylint: disable=arguments-differ """ Fix any children which point to non-existent blocks in the course's published and draft branches """ for branch in [ModuleStoreEnum.RevisionOption.published_only, ModuleStoreEnum.RevisionOption.draft_only]: - super(DraftVersioningModuleStore, self).fix_not_found( # lint-amnesty, pylint: disable=super-with-arguments + super().fix_not_found( self._map_revision_to_branch(course_key, branch), user_id ) @@ -373,7 +373,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli Publishes the subtree under location from the draft branch to the published branch Returns the newly published item. """ - super(DraftVersioningModuleStore, self).copy( # lint-amnesty, pylint: disable=super-with-arguments + super().copy( user_id, # Directly using the replace function rather than the for_branch function # because for_branch obliterates the version_guid and will lead to missed version conflicts. @@ -519,14 +519,14 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli See :py:meth `xmodule.modulestore.split_mongo.split.SplitMongoModuleStore.get_course_history_info` """ course_locator = self._map_revision_to_branch(course_locator) - return super(DraftVersioningModuleStore, self).get_course_history_info(course_locator) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_course_history_info(course_locator) def get_course_successors(self, course_locator, version_history_depth=1): """ See :py:meth `xmodule.modulestore.split_mongo.split.SplitMongoModuleStore.get_course_successors` """ course_locator = self._map_revision_to_branch(course_locator) - return super(DraftVersioningModuleStore, self).get_course_successors( # lint-amnesty, pylint: disable=super-with-arguments + return super().get_course_successors( course_locator, version_history_depth=version_history_depth ) @@ -535,7 +535,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli See :py:meth `xmodule.modulestore.split_mongo.split.SplitMongoModuleStore.get_block_generations` """ block_locator = self._map_revision_to_branch(block_locator) - return super(DraftVersioningModuleStore, self).get_block_generations(block_locator) # lint-amnesty, pylint: disable=super-with-arguments + return super().get_block_generations(block_locator) def has_published_version(self, xblock): """ @@ -614,12 +614,12 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli @contract(asset_key='AssetKey') def find_asset_metadata(self, asset_key, **kwargs): - return super(DraftVersioningModuleStore, self).find_asset_metadata( # lint-amnesty, pylint: disable=super-with-arguments + return super().find_asset_metadata( self._map_revision_to_branch(asset_key), **kwargs ) def get_all_asset_metadata(self, course_key, asset_type, start=0, maxresults=-1, sort=None, **kwargs): - return super(DraftVersioningModuleStore, self).get_all_asset_metadata( # lint-amnesty, pylint: disable=super-with-arguments + return super().get_all_asset_metadata( self._map_revision_to_branch(course_key), asset_type, start, maxresults, sort, **kwargs ) @@ -628,11 +628,11 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli Updates both the published and draft branches """ # if one call gets an exception, don't do the other call but pass on the exception - super(DraftVersioningModuleStore, self)._update_course_assets( # lint-amnesty, pylint: disable=super-with-arguments + super()._update_course_assets( user_id, self._map_revision_to_branch(asset_key, ModuleStoreEnum.RevisionOption.published_only), update_function ) - super(DraftVersioningModuleStore, self)._update_course_assets( # lint-amnesty, pylint: disable=super-with-arguments + super()._update_course_assets( user_id, self._map_revision_to_branch(asset_key, ModuleStoreEnum.RevisionOption.draft_only), update_function ) @@ -646,17 +646,17 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli for asset_md in asset_metadata_list: asset_key = asset_md.asset_id asset_md.asset_id = self._map_revision_to_branch(asset_key, ModuleStoreEnum.RevisionOption.published_only) - super(DraftVersioningModuleStore, self).save_asset_metadata_list(asset_metadata_list, user_id, import_only) # lint-amnesty, pylint: disable=super-with-arguments + super().save_asset_metadata_list(asset_metadata_list, user_id, import_only) for asset_md in asset_metadata_list: asset_key = asset_md.asset_id asset_md.asset_id = self._map_revision_to_branch(asset_key, ModuleStoreEnum.RevisionOption.draft_only) - super(DraftVersioningModuleStore, self).save_asset_metadata_list(asset_metadata_list, user_id, import_only) # lint-amnesty, pylint: disable=super-with-arguments + super().save_asset_metadata_list(asset_metadata_list, user_id, import_only) # Change each asset key back to its original state. for k in asset_keys: asset_md.asset_id = k def _find_course_asset(self, asset_key): - return super(DraftVersioningModuleStore, self)._find_course_asset( # lint-amnesty, pylint: disable=super-with-arguments + return super()._find_course_asset( self._map_revision_to_branch(asset_key) ) @@ -664,7 +664,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli """ Split specific lookup """ - return super(DraftVersioningModuleStore, self)._find_course_assets( # lint-amnesty, pylint: disable=super-with-arguments + return super()._find_course_assets( self._map_revision_to_branch(course_key) ) @@ -673,7 +673,7 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli Copies to and from both branches """ for revision in [ModuleStoreEnum.RevisionOption.published_only, ModuleStoreEnum.RevisionOption.draft_only]: - super(DraftVersioningModuleStore, self).copy_all_asset_metadata( # lint-amnesty, pylint: disable=super-with-arguments + super().copy_all_asset_metadata( self._map_revision_to_branch(source_course_key, revision), self._map_revision_to_branch(dest_course_key, revision), user_id diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py index 251bee078e6141bd8fa6d526d80b841b80cf128a..0d3bf16f8cb753b870b724a0489e886f2a5ce3a3 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py @@ -3,7 +3,6 @@ import copy from collections import namedtuple -import six from contracts import contract, new_contract from opaque_keys.edx.locator import BlockUsageLocator from xblock.core import XBlockAside @@ -37,7 +36,7 @@ class SplitMongoKVS(InheritanceKeyValueStore): (copied from a template block with copy_from_template) """ # deepcopy so that manipulations of fields does not pollute the source - super(SplitMongoKVS, self).__init__(copy.deepcopy(initial_values)) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(copy.deepcopy(initial_values)) self._definition = definition # either a DefinitionLazyLoader or the db id of the definition. # if the db id, then the definition is presumed to be loaded into _fields @@ -178,7 +177,7 @@ class SplitMongoKVS(InheritanceKeyValueStore): if self._defaults and key.field_name in self._defaults: return self._defaults[key.field_name] # If not, try inheriting from a parent, then use the XBlock type's normal default value: - return super(SplitMongoKVS, self).default(key) # lint-amnesty, pylint: disable=super-with-arguments + return super().default(key) def _load_definition(self): """ @@ -192,7 +191,7 @@ class SplitMongoKVS(InheritanceKeyValueStore): aside_fields_p = persisted_definition.get('aside_fields') if aside_fields_p: aside_fields = self._definition.field_converter(aside_fields_p) - for aside_type, fields in six.iteritems(aside_fields): + for aside_type, fields in aside_fields.items(): self.aside_fields.setdefault(aside_type, {}).update(fields) # do we want to cache any of the edit_info? self._definition = None # already loaded diff --git a/common/lib/xmodule/xmodule/modulestore/store_utilities.py b/common/lib/xmodule/xmodule/modulestore/store_utilities.py index 5e97179286555b51f90e3dd04e3f456b7b2cf679..a01152da474e045d6c8f461c36b5a76df2f6d632 100644 --- a/common/lib/xmodule/xmodule/modulestore/store_utilities.py +++ b/common/lib/xmodule/xmodule/modulestore/store_utilities.py @@ -5,17 +5,16 @@ import re import uuid from collections import namedtuple -import six from xblock.core import XBlock -DETACHED_XBLOCK_TYPES = set(name for name, __ in XBlock.load_tagged_classes("detached")) +DETACHED_XBLOCK_TYPES = {name for name, __ in XBlock.load_tagged_classes("detached")} def _prefix_only_url_replace_regex(pattern): """ Match urls in quotes pulling out the fields from pattern """ - return re.compile(u""" + return re.compile(""" (?x) # flags=re.VERBOSE (?P<quote>\\\\?['"]) # the opening quotes {} @@ -45,19 +44,19 @@ def rewrite_nonportable_content_links(source_course_id, dest_course_id, text): # create a serialized template for what the id will look like in the source_course but with # the block_id as a regex pattern placeholder_id = uuid.uuid4().hex - asset_block_pattern = six.text_type(source_course_id.make_asset_key('asset', placeholder_id)) + asset_block_pattern = str(source_course_id.make_asset_key('asset', placeholder_id)) asset_block_pattern = asset_block_pattern.replace(placeholder_id, r'(?P<block_id>.*?)') try: text = _prefix_only_url_replace_regex(asset_block_pattern).sub(portable_asset_link_subtitution, text) except Exception as exc: # pylint: disable=broad-except logging.warning("Error producing regex substitution %r for text = %r.\n\nError msg = %s", asset_block_pattern, text, str(exc)) # lint-amnesty, pylint: disable=line-too-long - placeholder_category = 'cat_{}'.format(uuid.uuid4().hex) - usage_block_pattern = six.text_type(source_course_id.make_usage_key(placeholder_category, placeholder_id)) + placeholder_category = f'cat_{uuid.uuid4().hex}' + usage_block_pattern = str(source_course_id.make_usage_key(placeholder_category, placeholder_id)) usage_block_pattern = usage_block_pattern.replace(placeholder_category, r'(?P<category>[^/+@]+)') usage_block_pattern = usage_block_pattern.replace(placeholder_id, r'(?P<block_id>.*?)') - jump_to_link_base = u'/courses/{course_key_string}/jump_to/{usage_key_string}'.format( - course_key_string=six.text_type(source_course_id), usage_key_string=usage_block_pattern + jump_to_link_base = '/courses/{course_key_string}/jump_to/{usage_key_string}'.format( + course_key_string=str(source_course_id), usage_key_string=usage_block_pattern ) try: text = _prefix_only_url_replace_regex(jump_to_link_base).sub(portable_jump_to_link_substitution, text) @@ -72,7 +71,7 @@ def rewrite_nonportable_content_links(source_course_id, dest_course_id, text): # if source_course_id != dest_course_id: try: - generic_courseware_link_base = u'/courses/{}/'.format(six.text_type(source_course_id)) + generic_courseware_link_base = '/courses/{}/'.format(str(source_course_id)) text = re.sub(_prefix_only_url_replace_regex(generic_courseware_link_base), portable_asset_link_subtitution, text) # lint-amnesty, pylint: disable=line-too-long except Exception as exc: # pylint: disable=broad-except logging.warning("Error producing regex substitution %r for text = %r.\n\nError msg = %s", source_course_id, text, str(exc)) # lint-amnesty, pylint: disable=line-too-long