refactor: pyupgrade in common/lib/xmodule (#26780)
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
# encoding: utf-8
|
||||
"""
|
||||
Modulestore configuration for test cases.
|
||||
"""
|
||||
@@ -9,14 +8,13 @@ import functools
|
||||
import os
|
||||
from contextlib import contextmanager
|
||||
from enum import Enum
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser, User # lint-amnesty, pylint: disable=imported-auth-user
|
||||
from django.db import connections
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from mock import patch
|
||||
from six.moves import range
|
||||
|
||||
from lms.djangoapps.courseware.tests.factories import StaffFactory
|
||||
from lms.djangoapps.courseware.field_overrides import OverrideFieldData
|
||||
@@ -43,7 +41,7 @@ class CourseUserType(Enum):
|
||||
UNENROLLED_STAFF = 'unenrolled_staff'
|
||||
|
||||
|
||||
class StoreConstructors(object):
|
||||
class StoreConstructors:
|
||||
"""Enumeration of store constructor types."""
|
||||
draft, split = list(range(2))
|
||||
|
||||
@@ -108,7 +106,7 @@ def draft_mongo_store_config(data_dir):
|
||||
'DOC_STORE_CONFIG': {
|
||||
'host': MONGO_HOST,
|
||||
'port': MONGO_PORT_NUM,
|
||||
'db': 'test_xmodule_{}'.format(os.getpid()),
|
||||
'db': f'test_xmodule_{os.getpid()}',
|
||||
'collection': 'modulestore',
|
||||
},
|
||||
'OPTIONS': modulestore_options
|
||||
@@ -135,7 +133,7 @@ def split_mongo_store_config(data_dir):
|
||||
'DOC_STORE_CONFIG': {
|
||||
'host': MONGO_HOST,
|
||||
'port': MONGO_PORT_NUM,
|
||||
'db': 'test_xmodule_{}'.format(os.getpid()),
|
||||
'db': f'test_xmodule_{os.getpid()}',
|
||||
'collection': 'modulestore',
|
||||
},
|
||||
'OPTIONS': modulestore_options
|
||||
@@ -154,7 +152,7 @@ def contentstore_config():
|
||||
'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore',
|
||||
'DOC_STORE_CONFIG': {
|
||||
'host': MONGO_HOST,
|
||||
'db': 'test_xcontent_{}'.format(os.getpid()),
|
||||
'db': f'test_xcontent_{os.getpid()}',
|
||||
'port': MONGO_PORT_NUM,
|
||||
},
|
||||
# allow for additional options that can be keyed on a name, e.g. 'trashcan'
|
||||
@@ -210,7 +208,7 @@ TEST_DATA_SPLIT_MODULESTORE = functools.partial(
|
||||
)
|
||||
|
||||
|
||||
class SignalIsolationMixin(object):
|
||||
class SignalIsolationMixin:
|
||||
"""
|
||||
Simple utility mixin class to toggle ModuleStore signals on and off. This
|
||||
class operates on `SwitchedSignal` objects on the modulestore's
|
||||
@@ -421,7 +419,7 @@ class SharedModuleStoreTestCase(
|
||||
# Now yield to allow the test class to run its setUpClass() setup code.
|
||||
yield
|
||||
# Now call the base class, which calls back into the test class's setUpTestData().
|
||||
super(SharedModuleStoreTestCase, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
@@ -429,19 +427,19 @@ class SharedModuleStoreTestCase(
|
||||
Start modulestore isolation, and then load modulestore specific
|
||||
test data.
|
||||
"""
|
||||
super(SharedModuleStoreTestCase, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
cls.start_modulestore_isolation()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.end_modulestore_isolation()
|
||||
super(SharedModuleStoreTestCase, cls).tearDownClass()
|
||||
super().tearDownClass()
|
||||
|
||||
def setUp(self):
|
||||
# OverrideFieldData.provider_classes is always reset to `None` so
|
||||
# that they're recalculated for every test
|
||||
OverrideFieldData.provider_classes = None
|
||||
super(SharedModuleStoreTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
|
||||
class ModuleStoreTestCase(
|
||||
@@ -491,11 +489,11 @@ class ModuleStoreTestCase(
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ModuleStoreTestCase, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(ModuleStoreTestCase, cls).tearDownClass()
|
||||
super().tearDownClass()
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@@ -511,7 +509,7 @@ class ModuleStoreTestCase(
|
||||
# that they're recalculated for every test
|
||||
OverrideFieldData.provider_classes = None
|
||||
|
||||
super(ModuleStoreTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.store = modulestore()
|
||||
|
||||
|
||||
@@ -11,13 +11,12 @@ import traceback
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from uuid import uuid4
|
||||
import pytz
|
||||
from unittest.mock import patch
|
||||
|
||||
import pymongo.message
|
||||
import six
|
||||
import pytz
|
||||
from factory import Factory, Sequence, lazy_attribute, lazy_attribute_sequence
|
||||
from factory.errors import CyclicDefinitionError
|
||||
from mock import patch
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from opaque_keys.edx.locator import BlockUsageLocator
|
||||
from xblock.core import XBlock
|
||||
@@ -31,7 +30,7 @@ from xmodule.tabs import CourseTab
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Dummy(object):
|
||||
class Dummy:
|
||||
pass
|
||||
|
||||
|
||||
@@ -45,7 +44,7 @@ class XModuleFactoryLock(threading.local):
|
||||
will be called.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(XModuleFactoryLock, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
self._enabled = False
|
||||
|
||||
def enable(self):
|
||||
@@ -81,7 +80,7 @@ class XModuleFactory(Factory):
|
||||
# We have to give a model for Factory.
|
||||
# However, the class that we create is actually determined by the category
|
||||
# specified in the factory
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = Dummy
|
||||
|
||||
@lazy_attribute
|
||||
@@ -155,7 +154,7 @@ class SampleCourseFactory(CourseFactory):
|
||||
user_id = kwargs.get('user_id', ModuleStoreEnum.UserID.test)
|
||||
|
||||
with store.branch_setting(ModuleStoreEnum.Branch.draft_preferred, None):
|
||||
course = super(SampleCourseFactory, cls)._create(target_class, **kwargs)
|
||||
course = super()._create(target_class, **kwargs)
|
||||
|
||||
def create_sub_tree(parent_loc, block_info):
|
||||
"""Recursively creates a sub_tree on this parent_loc with this block."""
|
||||
@@ -216,7 +215,7 @@ class ToyCourseFactory(SampleCourseFactory):
|
||||
}
|
||||
fields.update(kwargs)
|
||||
|
||||
toy_course = super(ToyCourseFactory, cls)._create(
|
||||
toy_course = super()._create(
|
||||
target_class,
|
||||
**fields
|
||||
)
|
||||
@@ -292,9 +291,9 @@ class ItemFactory(XModuleFactory):
|
||||
@lazy_attribute_sequence
|
||||
def display_name(self, n): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
if self.descriptive_tag:
|
||||
return "{} {} - {}".format(self.category, n, self.descriptive_tag)
|
||||
return f"{self.category} {n} - {self.descriptive_tag}"
|
||||
else:
|
||||
return "{} {}".format(self.category, n)
|
||||
return f"{self.category} {n}"
|
||||
|
||||
@lazy_attribute
|
||||
def location(self): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
@@ -389,7 +388,7 @@ class ItemFactory(XModuleFactory):
|
||||
template = clz.get_template(template_id)
|
||||
assert template is not None
|
||||
metadata.update(template.get('metadata', {}))
|
||||
if not isinstance(data, six.string_types):
|
||||
if not isinstance(data, str):
|
||||
data.update(template.get('data'))
|
||||
|
||||
# replace the display name with an optional parameter passed in from the caller
|
||||
@@ -464,7 +463,7 @@ def check_number_of_calls(object_with_method, method_name, maximum_calls, minimu
|
||||
)
|
||||
|
||||
|
||||
class StackTraceCounter(object):
|
||||
class StackTraceCounter:
|
||||
"""
|
||||
A class that counts unique stack traces underneath a particular stack frame.
|
||||
"""
|
||||
@@ -505,13 +504,13 @@ class StackTraceCounter(object):
|
||||
try:
|
||||
safe_args.append(repr(arg))
|
||||
except Exception as exc:
|
||||
safe_args.append('<un-repr-able value: {}'.format(exc))
|
||||
safe_args.append(f'<un-repr-able value: {exc}')
|
||||
safe_kwargs = {}
|
||||
for key, kwarg in kwargs.items():
|
||||
try:
|
||||
safe_kwargs[key] = repr(kwarg)
|
||||
except Exception as exc:
|
||||
safe_kwargs[key] = '<un-repr-able value: {}'.format(exc)
|
||||
safe_kwargs[key] = f'<un-repr-able value: {exc}'
|
||||
|
||||
self._stacks[tuple(stack)][tuple(safe_args), tuple(safe_kwargs.items())] += 1
|
||||
else:
|
||||
@@ -612,8 +611,8 @@ def check_sum_of_calls(object_, methods, maximum_calls, minimum_calls=1, include
|
||||
messages.append("\n\n")
|
||||
if include_arguments:
|
||||
for (args, kwargs), count in stack_counter[stack].items():
|
||||
messages.append(" called {} times with:\n".format(count))
|
||||
messages.append(" args: {}\n".format(args))
|
||||
messages.append(f" called {count} times with:\n")
|
||||
messages.append(f" args: {args}\n")
|
||||
messages.append(" kwargs: {}\n\n".format(dict(kwargs)))
|
||||
|
||||
# verify that we called the methods within the desired range
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# encoding: utf-8
|
||||
"""
|
||||
The data type and use of it for declaratively creating test courses.
|
||||
"""
|
||||
@@ -67,7 +66,7 @@ TOY_BLOCK_INFO_TREE = [
|
||||
),
|
||||
BlockInfo(
|
||||
"toyjumpto", "html", {
|
||||
"data": u"<a href=\"/jump_to_id/vertical_test\">This is a link to another page and some Chinese 四節比分和七年前</a> <p>Some more Chinese 四節比分和七年前</p>\n", # lint-amnesty, pylint: disable=line-too-long
|
||||
"data": "<a href=\"/jump_to_id/vertical_test\">This is a link to another page and some Chinese 四節比分和七年前</a> <p>Some more Chinese 四節比分和七年前</p>\n", # lint-amnesty, pylint: disable=line-too-long
|
||||
"xml_attributes": {"filename": ["html/toyjumpto.xml", "html/toyjumpto.xml"]}
|
||||
}, []),
|
||||
BlockInfo(
|
||||
@@ -129,7 +128,7 @@ TOY_BLOCK_INFO_TREE = [
|
||||
"poll_test", "chapter", {}, [
|
||||
BlockInfo(
|
||||
"T1_changemind_poll_foo", "poll_question", {
|
||||
"question": u"<p>Have you changed your mind? ’</p>",
|
||||
"question": "<p>Have you changed your mind? ’</p>",
|
||||
"answers": [{"text": "Yes", "id": "yes"}, {"text": "No", "id": "no"}],
|
||||
"xml_attributes": {"reset": "false", "filename": ["", None]},
|
||||
"display_name": "Change your answer"
|
||||
@@ -177,7 +176,7 @@ TOY_BLOCK_INFO_TREE = [
|
||||
}, []),
|
||||
]),
|
||||
BlockInfo("unicode", "html", {
|
||||
"data": u"…", "xml_attributes": {"filename": ["", None]}
|
||||
"data": "…", "xml_attributes": {"filename": ["", None]}
|
||||
}, [])
|
||||
]),
|
||||
]
|
||||
|
||||
@@ -4,8 +4,8 @@ Tests for Asides
|
||||
|
||||
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from mock import patch
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlockAside
|
||||
from xblock.fields import Scope, String
|
||||
@@ -17,7 +17,7 @@ class AsideTestType(XBlockAside):
|
||||
"""
|
||||
Test Aside type
|
||||
"""
|
||||
FRAG_CONTENT = u"<p>Aside rendered</p>"
|
||||
FRAG_CONTENT = "<p>Aside rendered</p>"
|
||||
|
||||
content = String(default="default_content", scope=Scope.content)
|
||||
data_field = String(default="default_data", scope=Scope.settings)
|
||||
@@ -45,11 +45,11 @@ class TestAsidesXmlStore(TestCase):
|
||||
Check whether block has the expected aside w/ its fields and then recurse to the block's children
|
||||
"""
|
||||
asides = block.runtime.get_asides(block)
|
||||
assert len(asides) == 1, 'Found {} asides but expected only test_aside'.format(asides)
|
||||
assert len(asides) == 1, f'Found {asides} asides but expected only test_aside'
|
||||
assert isinstance(asides[0], AsideTestType)
|
||||
category = block.scope_ids.block_type
|
||||
assert asides[0].data_field == '{} aside data'.format(category)
|
||||
assert asides[0].content == '{} Aside'.format(category.capitalize())
|
||||
assert asides[0].data_field == f'{category} aside data'
|
||||
assert asides[0].content == f'{category.capitalize()} Aside'
|
||||
|
||||
for child in block.get_children():
|
||||
check_block(child)
|
||||
|
||||
@@ -9,12 +9,10 @@ from datetime import datetime, timedelta
|
||||
import pytest
|
||||
import ddt
|
||||
import pytz
|
||||
import six
|
||||
|
||||
from django.test import TestCase
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from six.moves import range, zip
|
||||
|
||||
from openedx.core.lib.tests import attr
|
||||
from xmodule.assetstore import AssetMetadata
|
||||
@@ -29,16 +27,13 @@ from xmodule.modulestore.tests.utils import (
|
||||
)
|
||||
|
||||
|
||||
class AssetStoreTestData(object):
|
||||
class AssetStoreTestData:
|
||||
"""
|
||||
Shared data for constructing test assets.
|
||||
"""
|
||||
now = datetime.now(pytz.utc)
|
||||
user_id = 144
|
||||
if six.PY2:
|
||||
user_id_long = long(user_id) # lint-amnesty, pylint: disable=undefined-variable
|
||||
else:
|
||||
user_id_long = int(user_id)
|
||||
user_id_long = int(user_id)
|
||||
|
||||
user_email = "me@example.com"
|
||||
|
||||
@@ -75,7 +70,7 @@ class TestSortedAssetList(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestSortedAssetList, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
asset_list = [dict(list(zip(AssetStoreTestData.asset_fields, asset))) for asset in AssetStoreTestData.all_asset_data] # lint-amnesty, pylint: disable=line-too-long
|
||||
self.sorted_asset_list_by_filename = SortedAssetList(iterable=asset_list)
|
||||
self.sorted_asset_list_by_last_edit = SortedAssetList(iterable=asset_list, key=lambda x: x['edited_on'])
|
||||
@@ -105,7 +100,7 @@ class TestMongoAssetMetadataStorage(TestCase):
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestMongoAssetMetadataStorage, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.differents = (('different', 'burn.jpg'),)
|
||||
self.vrmls = (
|
||||
@@ -560,7 +555,7 @@ class TestMongoAssetMetadataStorage(TestCase):
|
||||
course1.id, None, start=0, maxresults=-1,
|
||||
sort=('displayname', ModuleStoreEnum.SortOrder.ascending)
|
||||
)
|
||||
assert len(assets) == len((self.differents + self.vrmls))
|
||||
assert len(assets) == len(self.differents + self.vrmls)
|
||||
self._check_asset_values(assets, self.differents + self.vrmls)
|
||||
|
||||
@ddt.data(*MODULESTORE_SETUPS)
|
||||
|
||||
@@ -52,7 +52,7 @@ class TestContentstore(unittest.TestCase):
|
||||
CourseLocator.deprecated = cls.ssck_deprecated
|
||||
else:
|
||||
del CourseLocator.deprecated
|
||||
return super(TestContentstore, cls).tearDownClass()
|
||||
return super().tearDownClass()
|
||||
|
||||
def set_up_assets(self, deprecated):
|
||||
"""
|
||||
@@ -86,7 +86,7 @@ class TestContentstore(unittest.TestCase):
|
||||
"""
|
||||
Load and save the given file.
|
||||
"""
|
||||
with open("{}/static/{}".format(DATA_DIR, filename), "rb") as f:
|
||||
with open(f"{DATA_DIR}/static/{filename}", "rb") as f:
|
||||
content = StaticContent(
|
||||
asset_key, displayname, mimetypes.guess_type(filename)[0], f.read(),
|
||||
locked=locked
|
||||
@@ -115,9 +115,9 @@ class TestContentstore(unittest.TestCase):
|
||||
"""
|
||||
self.set_up_assets(deprecated)
|
||||
asset_key = self.course1_key.make_asset_key('asset', self.course1_files[0])
|
||||
assert self.contentstore.find(asset_key) is not None, 'Could not find {}'.format(asset_key)
|
||||
assert self.contentstore.find(asset_key) is not None, f'Could not find {asset_key}'
|
||||
|
||||
assert self.contentstore.find(asset_key, as_stream=True) is not None, 'Could not find {}'.format(asset_key)
|
||||
assert self.contentstore.find(asset_key, as_stream=True) is not None, f'Could not find {asset_key}'
|
||||
|
||||
unknown_asset = self.course1_key.make_asset_key('asset', 'no_such_file.gif')
|
||||
with pytest.raises(NotFoundError):
|
||||
@@ -139,11 +139,11 @@ class TestContentstore(unittest.TestCase):
|
||||
)
|
||||
for filename in self.course1_files:
|
||||
filepath = path.Path(root_dir / filename)
|
||||
assert filepath.isfile(), '{} is not a file'.format(filepath)
|
||||
assert filepath.isfile(), f'{filepath} is not a file'
|
||||
for filename in self.course2_files:
|
||||
if filename not in self.course1_files:
|
||||
filepath = path.Path(root_dir / filename)
|
||||
assert not filepath.isfile(), '{} is unexpected exported a file'.format(filepath)
|
||||
assert not filepath.isfile(), f'{filepath} is unexpected exported a file'
|
||||
finally:
|
||||
shutil.rmtree(root_dir)
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ import itertools
|
||||
import os
|
||||
from shutil import rmtree
|
||||
from tempfile import mkdtemp
|
||||
from unittest.mock import patch
|
||||
|
||||
import ddt
|
||||
from mock import patch
|
||||
from path import Path as path
|
||||
|
||||
from openedx.core.lib.tests import attr
|
||||
@@ -43,7 +43,7 @@ COURSE_DATA_NAMES = (
|
||||
'split_test_module_draft',
|
||||
)
|
||||
|
||||
EXPORTED_COURSE_DIR_NAME = u'exported_source_course'
|
||||
EXPORTED_COURSE_DIR_NAME = 'exported_source_course'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -55,7 +55,7 @@ class CrossStoreXMLRoundtrip(CourseComparisonTest, PartitionTestCase):
|
||||
"""
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ Unit tests for testing inheritance mixins
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
import ddt
|
||||
from django.utils.timezone import now, timedelta
|
||||
from mock import Mock
|
||||
from xblock.core import XBlock
|
||||
from xblock.fields import ScopeIds
|
||||
from xblock.test.tools import TestRuntime
|
||||
@@ -38,7 +38,7 @@ class TestInheritanceMixin(unittest.TestCase):
|
||||
self.xblock = runtime.construct_xblock_from_class(
|
||||
TestXBlock, ScopeIds('user', 'TestXBlock', 'def_id', 'usage_id')
|
||||
)
|
||||
super(TestInheritanceMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
def add_submission_deadline_information(self, due_date, graceperiod, self_paced):
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Basic unit tests related to content libraries.
|
||||
|
||||
@@ -7,10 +6,8 @@ Higher-level tests are in `cms/djangoapps/contentstore`.
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
import six
|
||||
from bson.objectid import ObjectId
|
||||
from opaque_keys.edx.locator import LibraryLocator
|
||||
from six.moves import range
|
||||
|
||||
from xmodule.modulestore.exceptions import DuplicateCourseError
|
||||
from xmodule.modulestore.tests.factories import ItemFactory, LibraryFactory, check_mongo_calls
|
||||
@@ -50,15 +47,15 @@ class TestLibraries(MixedSplitTestCase):
|
||||
|
||||
@ddt.data(
|
||||
"This is a test library!",
|
||||
u"Ωμέγα Βιβλιοθήκη",
|
||||
"Ωμέγα Βιβλιοθήκη",
|
||||
)
|
||||
def test_str_repr(self, name):
|
||||
"""
|
||||
Test __unicode__() and __str__() methods of libraries
|
||||
"""
|
||||
library = LibraryFactory.create(metadata={"display_name": name}, modulestore=self.store)
|
||||
assert name in six.text_type(library)
|
||||
if not isinstance(name, six.text_type):
|
||||
assert name in str(library)
|
||||
if not isinstance(name, str):
|
||||
assert name in str(library)
|
||||
|
||||
def test_display_with_default_methods(self):
|
||||
@@ -155,7 +152,7 @@ class TestLibraries(MixedSplitTestCase):
|
||||
def test_get_libraries(self):
|
||||
""" Test get_libraries() """
|
||||
libraries = [LibraryFactory.create(modulestore=self.store) for _ in range(3)]
|
||||
lib_dict = dict([(lib.location.library_key, lib) for lib in libraries]) # lint-amnesty, pylint: disable=consider-using-dict-comprehension
|
||||
lib_dict = {lib.location.library_key: lib for lib in libraries}
|
||||
|
||||
lib_list = self.store.get_libraries()
|
||||
|
||||
|
||||
@@ -12,20 +12,18 @@ from contextlib import contextmanager
|
||||
from shutil import rmtree
|
||||
from tempfile import mkdtemp
|
||||
from uuid import uuid4
|
||||
from unittest.mock import Mock, call, patch
|
||||
|
||||
import ddt
|
||||
import pymongo
|
||||
import pytest
|
||||
import six
|
||||
# Mixed modulestore depends on django, so we'll manually configure some django settings
|
||||
# before importing the module
|
||||
# TODO remove this import and the configuration -- xmodule should not depend on django!
|
||||
from django.conf import settings
|
||||
from mock import Mock, call, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator
|
||||
from pytz import UTC
|
||||
from six.moves import range
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlockAside
|
||||
from xblock.fields import Scope, ScopeIds, String
|
||||
@@ -121,7 +119,7 @@ class CommonMixedModuleStoreSetup(CourseComparisonTest):
|
||||
"""
|
||||
Set up the database for testing
|
||||
"""
|
||||
super(CommonMixedModuleStoreSetup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.exclude_field(None, 'wiki_slug')
|
||||
self.exclude_field(None, 'xml_attributes')
|
||||
@@ -281,7 +279,7 @@ class AsideFoo(XBlockAside):
|
||||
"""
|
||||
Test xblock aside class
|
||||
"""
|
||||
FRAG_CONTENT = u"<p>Aside Foo rendered</p>"
|
||||
FRAG_CONTENT = "<p>Aside Foo rendered</p>"
|
||||
|
||||
field11 = String(default="aside1_default_value1", scope=Scope.content)
|
||||
field12 = String(default="aside1_default_value2", scope=Scope.settings)
|
||||
@@ -296,7 +294,7 @@ class AsideBar(XBlockAside):
|
||||
"""
|
||||
Test xblock aside class
|
||||
"""
|
||||
FRAG_CONTENT = u"<p>Aside Bar rendered</p>"
|
||||
FRAG_CONTENT = "<p>Aside Bar rendered</p>"
|
||||
|
||||
field21 = String(default="aside2_default_value1", scope=Scope.content)
|
||||
field22 = String(default="aside2_default_value2", scope=Scope.settings)
|
||||
@@ -483,7 +481,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
|
||||
items = self.store.get_items(course_key)
|
||||
# Check items found are either course or about type
|
||||
assert set(['course', 'about']).issubset(set([item.location.block_type for item in items])) # pylint: disable=consider-using-set-comprehension, line-too-long
|
||||
assert {'course', 'about'}.issubset({item.location.block_type for item in items}) # pylint: disable=line-too-long
|
||||
# Assert that about is a detached category found in get_items
|
||||
assert [item.location.block_type for item in items if item.location.block_type == 'about'][0]\
|
||||
in DETACHED_XBLOCK_TYPES
|
||||
@@ -511,7 +509,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
items_in_tree = self.store.get_items(course_key, include_orphans=False)
|
||||
|
||||
# Check that course and about blocks are found in get_items
|
||||
assert set(['course', 'about']).issubset({item.location.block_type for item in items_in_tree})
|
||||
assert {'course', 'about'}.issubset({item.location.block_type for item in items_in_tree})
|
||||
# Check orphan is found or not - this is based on mongo/split modulestore. It should be found in mongo.
|
||||
assert (orphan in [item.location for item in items_in_tree]) == orphan_in_items
|
||||
assert len(items_in_tree) == expected_items_in_tree
|
||||
@@ -1046,7 +1044,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
with check_mongo_calls(max_find, max_send):
|
||||
courses = self.store.get_courses()
|
||||
course_ids = [course.location for course in courses]
|
||||
assert len(courses) == 1, 'Not one course: {}'.format(course_ids)
|
||||
assert len(courses) == 1, f'Not one course: {course_ids}'
|
||||
assert self.course_locations[self.MONGO_COURSEID] in course_ids
|
||||
|
||||
with self.store.branch_setting(ModuleStoreEnum.Branch.draft_preferred):
|
||||
@@ -1608,7 +1606,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
# add another parent (unit) "vertical_x1b" for problem "problem_x1a_1"
|
||||
mongo_store.collection.update_one(
|
||||
self.vertical_x1b.to_deprecated_son('_id.'), # lint-amnesty, pylint: disable=no-member
|
||||
{'$push': {'definition.children': six.text_type(self.problem_x1a_1)}} # lint-amnesty, pylint: disable=no-member
|
||||
{'$push': {'definition.children': str(self.problem_x1a_1)}} # lint-amnesty, pylint: disable=no-member
|
||||
)
|
||||
|
||||
# convert first parent (unit) "vertical_x1a" of problem "problem_x1a_1" to draft
|
||||
@@ -1647,7 +1645,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
|
||||
should_work = (
|
||||
(self.problem_x1a_2, # lint-amnesty, pylint: disable=no-member
|
||||
(course_key, u"Chapter_x", u"Sequential_x1", u'Vertical_x1a', '1', self.problem_x1a_2)), # lint-amnesty, pylint: disable=no-member
|
||||
(course_key, "Chapter_x", "Sequential_x1", 'Vertical_x1a', '1', self.problem_x1a_2)), # lint-amnesty, pylint: disable=no-member
|
||||
(self.chapter_x, # lint-amnesty, pylint: disable=no-member
|
||||
(course_key, "Chapter_x", None, None, None, self.chapter_x)), # lint-amnesty, pylint: disable=no-member
|
||||
)
|
||||
@@ -1908,7 +1906,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
|
||||
with check_mongo_calls(max_find, max_send):
|
||||
found_orphans = self.store.get_orphans(self.course_locations[self.MONGO_COURSEID].course_key)
|
||||
six.assertCountEqual(self, found_orphans, orphan_locations)
|
||||
self.assertCountEqual(found_orphans, orphan_locations)
|
||||
|
||||
@ddt.data(ModuleStoreEnum.Type.mongo)
|
||||
def test_get_non_orphan_parents(self, default_ms):
|
||||
@@ -1948,11 +1946,11 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
# add orphan vertical and sequential as another parents of problem "problem_x1a_1"
|
||||
mongo_store.collection.update_one(
|
||||
orphan_sequential.to_deprecated_son('_id.'),
|
||||
{'$push': {'definition.children': six.text_type(self.problem_x1a_1)}} # lint-amnesty, pylint: disable=no-member
|
||||
{'$push': {'definition.children': str(self.problem_x1a_1)}} # lint-amnesty, pylint: disable=no-member
|
||||
)
|
||||
mongo_store.collection.update_one(
|
||||
orphan_vertical.to_deprecated_son('_id.'),
|
||||
{'$push': {'definition.children': six.text_type(self.problem_x1a_1)}} # lint-amnesty, pylint: disable=no-member
|
||||
{'$push': {'definition.children': str(self.problem_x1a_1)}} # lint-amnesty, pylint: disable=no-member
|
||||
)
|
||||
# test that "get_parent_location" method of published branch still returns the correct non-orphan parent for
|
||||
# problem "problem_x1a_1" since the two other parents are orphans
|
||||
@@ -1961,7 +1959,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
assert parent == self.vertical_x1a # lint-amnesty, pylint: disable=no-member
|
||||
|
||||
# now add valid published vertical as another parent of problem
|
||||
mongo_store.collection.update_one(self.sequential_x1.to_deprecated_son('_id.'), {'$push': {'definition.children': six.text_type(self.problem_x1a_1)}}) # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
mongo_store.collection.update_one(self.sequential_x1.to_deprecated_son('_id.'), {'$push': {'definition.children': str(self.problem_x1a_1)}}) # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
# now check that "get_parent_location" method of published branch raises "ReferentialIntegrityError" for
|
||||
# problem "problem_x1a_1" since it has now 2 valid published parents
|
||||
with self.store.branch_setting(ModuleStoreEnum.Branch.published_only, course_id):
|
||||
@@ -1983,7 +1981,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
block_id='orphan'
|
||||
)
|
||||
orphans = self.store.get_orphans(self.course_locations[self.MONGO_COURSEID].course_key)
|
||||
assert len(orphans) == 0, 'unexpected orphans: {}'.format(orphans)
|
||||
assert len(orphans) == 0, f'unexpected orphans: {orphans}'
|
||||
|
||||
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
|
||||
def test_create_item_populates_edited_info(self, default_ms):
|
||||
@@ -2487,7 +2485,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
self._initialize_mixed(mappings={})
|
||||
|
||||
fake_store = "fake"
|
||||
with self.assertRaisesRegex(Exception, "Cannot find store of type {}".format(fake_store)):
|
||||
with self.assertRaisesRegex(Exception, f"Cannot find store of type {fake_store}"):
|
||||
with self.store.default_store(fake_store):
|
||||
pass # pragma: no cover
|
||||
|
||||
@@ -2495,7 +2493,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
"""
|
||||
Load and save the given file. (taken from test_contentstore)
|
||||
"""
|
||||
with open("{}/static/{}".format(DATA_DIR, asset_key.block_id), "rb") as f:
|
||||
with open(f"{DATA_DIR}/static/{asset_key.block_id}", "rb") as f:
|
||||
content = StaticContent(
|
||||
asset_key, "Funky Pix", mimetypes.guess_type(asset_key.block_id)[0], f.read(),
|
||||
)
|
||||
@@ -3002,7 +3000,7 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
||||
"""
|
||||
Set up the database for testing
|
||||
"""
|
||||
super(TestPublishOverExportImport, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.user_id = ModuleStoreEnum.UserID.test
|
||||
self.export_dir = mkdtemp()
|
||||
@@ -3353,11 +3351,11 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
||||
"""
|
||||
asides = block.runtime.get_asides(block)
|
||||
|
||||
assert len(asides) == 1, 'Found {} asides but expected only test_aside'.format(asides)
|
||||
assert len(asides) == 1, f'Found {asides} asides but expected only test_aside'
|
||||
assert isinstance(asides[0], AsideTestType)
|
||||
category = block.scope_ids.block_type
|
||||
assert asides[0].data_field == '{} aside data'.format(category)
|
||||
assert asides[0].content == '{} Aside'.format(category.capitalize())
|
||||
assert asides[0].data_field == f'{category} aside data'
|
||||
assert asides[0].content == f'{category.capitalize()} Aside'
|
||||
|
||||
for child in block.get_children():
|
||||
check_block(child)
|
||||
@@ -3368,7 +3366,7 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
||||
new_chapter = self.store.create_child(self.user_id, courses[0].location, 'chapter', 'new_chapter')
|
||||
asides = new_chapter.runtime.get_asides(new_chapter)
|
||||
|
||||
assert len(asides) == 1, 'Found {} asides but expected only test_aside'.format(asides)
|
||||
assert len(asides) == 1, f'Found {asides} asides but expected only test_aside'
|
||||
chapter_aside = asides[0]
|
||||
assert isinstance(chapter_aside, AsideTestType)
|
||||
assert not chapter_aside.fields['data_field'].is_set_on(chapter_aside), \
|
||||
@@ -3468,11 +3466,11 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
||||
"""
|
||||
asides = block.runtime.get_asides(block)
|
||||
|
||||
assert len(asides) == 1, 'Found {} asides but expected only test_aside'.format(asides)
|
||||
assert len(asides) == 1, f'Found {asides} asides but expected only test_aside'
|
||||
assert isinstance(asides[0], AsideTestType)
|
||||
category = block.scope_ids.block_type
|
||||
assert asides[0].data_field == 'Exported data_field {} aside data'.format(category)
|
||||
assert asides[0].content == 'Exported content {} Aside'.format(category.capitalize())
|
||||
assert asides[0].data_field == f'Exported data_field {category} aside data'
|
||||
assert asides[0].content == f'Exported content {category.capitalize()} Aside'
|
||||
|
||||
for child in block.get_children():
|
||||
check_block(child)
|
||||
@@ -3515,7 +3513,7 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
||||
new_chapter.display_name = new_chapter_display_name
|
||||
asides = new_chapter.runtime.get_asides(new_chapter)
|
||||
|
||||
assert len(asides) == 1, 'Found {} asides but expected only test_aside'.format(asides)
|
||||
assert len(asides) == 1, f'Found {asides} asides but expected only test_aside'
|
||||
chapter_aside = asides[0]
|
||||
assert isinstance(chapter_aside, AsideTestType)
|
||||
chapter_aside.data_field = 'new value'
|
||||
@@ -3528,7 +3526,7 @@ class TestPublishOverExportImport(CommonMixedModuleStoreSetup):
|
||||
new_problem.display_name = new_problem_display_name
|
||||
asides = new_problem.runtime.get_asides(new_problem)
|
||||
|
||||
assert len(asides) == 1, 'Found {} asides but expected only test_aside'.format(asides)
|
||||
assert len(asides) == 1, f'Found {asides} asides but expected only test_aside'
|
||||
problem_aside = asides[0]
|
||||
assert isinstance(problem_aside, AsideTestType)
|
||||
problem_aside.data_field = 'new problem value'
|
||||
@@ -3607,7 +3605,7 @@ class TestAsidesWithMixedModuleStore(CommonMixedModuleStoreSetup):
|
||||
"""
|
||||
Setup environment for testing
|
||||
"""
|
||||
super(TestAsidesWithMixedModuleStore, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
key_store = DictKeyValueStore()
|
||||
field_data = KvsFieldData(key_store)
|
||||
self.runtime = TestRuntime(services={'field-data': field_data})
|
||||
|
||||
@@ -5,6 +5,7 @@ Tests for testing the modulestore settings migration code.
|
||||
|
||||
import copy
|
||||
from unittest import TestCase
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
|
||||
|
||||
@@ -7,16 +7,15 @@ import logging
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from tempfile import mkdtemp
|
||||
from unittest.mock import patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pymongo
|
||||
import pytest
|
||||
import six
|
||||
|
||||
# pylint: disable=protected-access
|
||||
from django.test import TestCase
|
||||
# pylint: enable=E0611
|
||||
from mock import patch
|
||||
from opaque_keys.edx.keys import CourseKey, UsageKey
|
||||
from opaque_keys.edx.locator import AssetLocator, BlockUsageLocator, CourseLocator, LibraryLocator
|
||||
from path import Path as path
|
||||
@@ -172,7 +171,7 @@ class TestMongoModuleStoreBase(TestCase):
|
||||
connection.drop_database(DB)
|
||||
|
||||
def setUp(self):
|
||||
super(TestMongoModuleStoreBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.dummy_user = ModuleStoreEnum.UserID.test
|
||||
|
||||
|
||||
@@ -188,11 +187,11 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestMongoModuleStore, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(TestMongoModuleStore, cls).tearDownClass()
|
||||
super().tearDownClass()
|
||||
|
||||
def test_init(self):
|
||||
'''Make sure the db loads'''
|
||||
@@ -370,7 +369,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
courses = self.draft_store.get_courses()
|
||||
for course in courses:
|
||||
assert not ((course.location.org == 'edx') and (course.location.course == 'templates')),\
|
||||
'{0} is a template course'.format(course)
|
||||
f'{course} is a template course'
|
||||
|
||||
def test_contentstore_attrs(self):
|
||||
"""
|
||||
@@ -523,7 +522,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
for ref in refele.reference_list:
|
||||
assert isinstance(ref, UsageKey)
|
||||
assert len(refele.reference_dict) > 0
|
||||
for ref in six.itervalues(refele.reference_dict):
|
||||
for ref in refele.reference_dict.values():
|
||||
assert isinstance(ref, UsageKey)
|
||||
|
||||
def check_mongo_fields():
|
||||
@@ -532,17 +531,17 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
|
||||
def check_children(payload):
|
||||
for child in payload['definition']['children']:
|
||||
assert isinstance(child, six.string_types)
|
||||
assert isinstance(child, str)
|
||||
|
||||
refele = get_item(self.refloc)
|
||||
check_children(refele)
|
||||
assert isinstance(refele['definition']['data']['reference_link'], six.string_types)
|
||||
assert isinstance(refele['definition']['data']['reference_link'], str)
|
||||
assert len(refele['definition']['data']['reference_list']) > 0
|
||||
for ref in refele['definition']['data']['reference_list']:
|
||||
assert isinstance(ref, six.string_types)
|
||||
assert isinstance(ref, str)
|
||||
assert len(refele['metadata']['reference_dict']) > 0
|
||||
for ref in six.itervalues(refele['metadata']['reference_dict']):
|
||||
assert isinstance(ref, six.string_types)
|
||||
for ref in refele['metadata']['reference_dict'].values():
|
||||
assert isinstance(ref, str)
|
||||
|
||||
setup_test()
|
||||
check_xblock_fields()
|
||||
@@ -563,9 +562,9 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
|
||||
root_dir = path(mkdtemp())
|
||||
self.addCleanup(shutil.rmtree, root_dir)
|
||||
export_course_to_xml(self.draft_store, self.content_store, course_key, root_dir, u'test_export')
|
||||
assert path((root_dir / 'test_export/static/images/course_image.jpg')).isfile()
|
||||
assert path((root_dir / 'test_export/static/images_course_image.jpg')).isfile()
|
||||
export_course_to_xml(self.draft_store, self.content_store, course_key, root_dir, 'test_export')
|
||||
assert path(root_dir / 'test_export/static/images/course_image.jpg').isfile()
|
||||
assert path(root_dir / 'test_export/static/images_course_image.jpg').isfile()
|
||||
|
||||
@patch('xmodule.video_module.video_module.edxval_api', None)
|
||||
@patch('xmodule.tabs.CourseTab.from_json', side_effect=mock_tab_from_json)
|
||||
@@ -579,9 +578,9 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
|
||||
root_dir = path(mkdtemp())
|
||||
self.addCleanup(shutil.rmtree, root_dir)
|
||||
export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, u'test_export')
|
||||
assert path((root_dir / 'test_export/static/just_a_test.jpg')).isfile()
|
||||
assert not path((root_dir / 'test_export/static/images/course_image.jpg')).isfile()
|
||||
export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, 'test_export')
|
||||
assert path(root_dir / 'test_export/static/just_a_test.jpg').isfile()
|
||||
assert not path(root_dir / 'test_export/static/images/course_image.jpg').isfile()
|
||||
|
||||
@patch('xmodule.video_module.video_module.edxval_api', None)
|
||||
def test_course_without_image(self):
|
||||
@@ -592,9 +591,9 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
course = self.draft_store.get_course(CourseKey.from_string('edX/simple_with_draft/2012_Fall'))
|
||||
root_dir = path(mkdtemp())
|
||||
self.addCleanup(shutil.rmtree, root_dir)
|
||||
export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, u'test_export')
|
||||
assert not path((root_dir / 'test_export/static/images/course_image.jpg')).isfile()
|
||||
assert not path((root_dir / 'test_export/static/images_course_image.jpg')).isfile()
|
||||
export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, 'test_export')
|
||||
assert not path(root_dir / 'test_export/static/images/course_image.jpg').isfile()
|
||||
assert not path(root_dir / 'test_export/static/images_course_image.jpg').isfile()
|
||||
|
||||
def _create_test_tree(self, name, user_id=None):
|
||||
"""
|
||||
@@ -610,7 +609,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
user_id = self.dummy_user
|
||||
|
||||
org = 'edX'
|
||||
course = 'tree{}'.format(name)
|
||||
course = f'tree{name}'
|
||||
run = name
|
||||
|
||||
if not self.draft_store.has_course(CourseKey.from_string('/'.join[org, course, run])): # lint-amnesty, pylint: disable=unsubscriptable-object
|
||||
@@ -701,8 +700,8 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
|
||||
|
||||
# First child should have been moved to second position, and better child takes the lead
|
||||
course = self.draft_store.get_course(course.id)
|
||||
assert six.text_type(course.children[1]) == six.text_type(first_child.location)
|
||||
assert six.text_type(course.children[0]) == six.text_type(second_child.location)
|
||||
assert str(course.children[1]) == str(first_child.location)
|
||||
assert str(course.children[0]) == str(second_child.location)
|
||||
|
||||
# Clean up the data so we don't break other tests which apparently expect a particular state
|
||||
self.draft_store.delete_course(course.id, self.dummy_user)
|
||||
@@ -729,11 +728,11 @@ class TestMongoModuleStoreWithNoAssetCollection(TestMongoModuleStore): # lint-a
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestMongoModuleStoreWithNoAssetCollection, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(TestMongoModuleStoreWithNoAssetCollection, cls).tearDownClass()
|
||||
super().tearDownClass()
|
||||
|
||||
def test_no_asset_collection(self):
|
||||
courses = self.draft_store.get_courses()
|
||||
@@ -753,7 +752,7 @@ class TestMongoKeyValueStore(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestMongoKeyValueStore, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.data = {'foo': 'foo_value'}
|
||||
self.course_id = CourseKey.from_string('org/course/run')
|
||||
self.parent = self.course_id.make_usage_key('parent', 'p')
|
||||
|
||||
@@ -9,7 +9,6 @@ from tempfile import mkdtemp
|
||||
from unittest import skip
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.test import TestCase # lint-amnesty, pylint: disable=reimported
|
||||
|
||||
from xmodule.modulestore.tests.factories import check_mongo_calls
|
||||
@@ -35,7 +34,7 @@ class CountMongoCallsXMLRoundtrip(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(CountMongoCallsXMLRoundtrip, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.export_dir = mkdtemp()
|
||||
self.addCleanup(rmtree, self.export_dir, ignore_errors=True)
|
||||
|
||||
@@ -112,7 +111,7 @@ class CountMongoCallsCourseTraversal(TestCase):
|
||||
if access_all_block_fields:
|
||||
# Read the fields on each block in order to ensure each block and its definition is loaded.
|
||||
for xblock in all_blocks:
|
||||
for __, field in six.iteritems(xblock.fields):
|
||||
for __, field in xblock.fields.items():
|
||||
if field.is_set_on(xblock):
|
||||
__ = field.read_from(xblock)
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ from tempfile import mkdtemp
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
import six
|
||||
from six.moves import range
|
||||
|
||||
from openedx.core.lib.tests import attr
|
||||
from xmodule.exceptions import InvalidVersionError
|
||||
@@ -48,7 +46,7 @@ class TestPublish(SplitWMongoCourseBootstrapper):
|
||||
# create course: finds: 1 to verify uniqueness, 1 to find parents
|
||||
# sends: 1 to create course, 1 to create overview
|
||||
with check_mongo_calls(4, 2):
|
||||
super(TestPublish, self)._create_course(split=False) # 2 inserts (course and overview) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super()._create_course(split=False) # 2 inserts (course and overview)
|
||||
|
||||
# with bulk will delay all inheritance computations which won't be added into the mongo_calls
|
||||
with self.draft_mongo.bulk_operations(self.old_course_key):
|
||||
@@ -183,7 +181,7 @@ class DraftPublishedOpTestCourseSetup(unittest.TestCase):
|
||||
"""
|
||||
Given a block_type/num, return a block id.
|
||||
"""
|
||||
return '{}{:02d}'.format(block_type, num)
|
||||
return f'{block_type}{num:02d}'
|
||||
|
||||
def _make_course_db_entry(parent_type, parent_id, block_id, idx, child_block_type, child_block_id_base):
|
||||
"""
|
||||
@@ -276,7 +274,7 @@ class DraftPublishedOpTestCourseSetup(unittest.TestCase):
|
||||
# data needed to check the OLX.
|
||||
self.course_db = {}
|
||||
|
||||
super(DraftPublishedOpTestCourseSetup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
|
||||
class OLXFormatChecker(unittest.TestCase):
|
||||
@@ -306,7 +304,7 @@ class OLXFormatChecker(unittest.TestCase):
|
||||
self._ensure_exported()
|
||||
|
||||
block_path = os.path.join(self.root_export_dir, self.export_dir) # pylint: disable=no-member
|
||||
assert os.path.isdir(block_path), '{} is not a dir.'.format(block_path)
|
||||
assert os.path.isdir(block_path), f'{block_path} is not a dir.'
|
||||
return block_path
|
||||
|
||||
def _get_block_type_path(self, course_export_dir, block_type, draft):
|
||||
@@ -322,7 +320,7 @@ class OLXFormatChecker(unittest.TestCase):
|
||||
"""
|
||||
Return the course export filename for a block.
|
||||
"""
|
||||
return '{}.xml'.format(block_id)
|
||||
return f'{block_id}.xml'
|
||||
|
||||
def _get_block_contents(self, block_subdir_path, block_id):
|
||||
"""
|
||||
@@ -333,8 +331,8 @@ class OLXFormatChecker(unittest.TestCase):
|
||||
|
||||
block_file = self._get_block_filename(block_id)
|
||||
block_file_path = os.path.join(block_subdir_path, block_file)
|
||||
assert os.path.isfile(block_file_path), '{} is not an existing file.'.format(block_file_path)
|
||||
with open(block_file_path, "r") as file_handle:
|
||||
assert os.path.isfile(block_file_path), f'{block_file_path} is not an existing file.'
|
||||
with open(block_file_path) as file_handle:
|
||||
return file_handle.read()
|
||||
|
||||
def assertElementTag(self, element, tag):
|
||||
@@ -390,7 +388,7 @@ class OLXFormatChecker(unittest.TestCase):
|
||||
is_draft = kwargs.pop('draft', False)
|
||||
block_path = self._get_block_type_path(course_export_dir, block_type, is_draft)
|
||||
block_file_path = os.path.join(block_path, self._get_block_filename(block_id))
|
||||
assert not os.path.exists(block_file_path), '{} exists but should not!'.format(block_file_path)
|
||||
assert not os.path.exists(block_file_path), f'{block_file_path} exists but should not!'
|
||||
|
||||
def assertParentReferences(self, element, course_key, parent_type, parent_id, index_in_children_list):
|
||||
"""
|
||||
@@ -406,7 +404,7 @@ class OLXFormatChecker(unittest.TestCase):
|
||||
parent_key = course_key.make_usage_key(parent_type, parent_id)
|
||||
|
||||
self.assertElementAttrsSubset(element, {
|
||||
'parent_url': re.escape(six.text_type(parent_key)),
|
||||
'parent_url': re.escape(str(parent_key)),
|
||||
'index_in_children_list': re.escape(str(index_in_children_list)),
|
||||
})
|
||||
|
||||
@@ -496,11 +494,11 @@ class DraftPublishedOpBaseTestSetup(OLXFormatChecker, DraftPublishedOpTestCourse
|
||||
Setup base class for draft/published/OLX tests.
|
||||
"""
|
||||
|
||||
EXPORTED_COURSE_BEFORE_DIR_NAME = u'exported_course_before'
|
||||
EXPORTED_COURSE_AFTER_DIR_NAME = u'exported_course_after_{}'
|
||||
EXPORTED_COURSE_BEFORE_DIR_NAME = 'exported_course_before'
|
||||
EXPORTED_COURSE_AFTER_DIR_NAME = 'exported_course_after_{}'
|
||||
|
||||
def setUp(self):
|
||||
super(DraftPublishedOpBaseTestSetup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.export_dir = self.EXPORTED_COURSE_BEFORE_DIR_NAME
|
||||
self.root_export_dir = None
|
||||
self.contentstore = None
|
||||
@@ -564,7 +562,7 @@ class DraftPublishedOpBaseTestSetup(OLXFormatChecker, DraftPublishedOpTestCourse
|
||||
"""
|
||||
Make a unique name for the new export dir.
|
||||
"""
|
||||
return self.EXPORTED_COURSE_AFTER_DIR_NAME.format(six.text_type(uuid.uuid4())[:8])
|
||||
return self.EXPORTED_COURSE_AFTER_DIR_NAME.format(str(uuid.uuid4())[:8])
|
||||
|
||||
def publish(self, block_list):
|
||||
"""
|
||||
|
||||
@@ -5,10 +5,10 @@ Tests of modulestore semantics: How do the interfaces methods of ModuleStore rel
|
||||
|
||||
import itertools
|
||||
from collections import namedtuple
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
from mock import patch
|
||||
from xblock.core import XBlock, XBlockAside
|
||||
from xblock.fields import Scope, String
|
||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||
@@ -59,7 +59,7 @@ class DirectOnlyCategorySemantics(PureModulestoreTestCase):
|
||||
ASIDE_DATA_FIELD = TestField('content', '<div>aside test data</div>', '<div>aside different test data</div>')
|
||||
|
||||
def setUp(self):
|
||||
super(DirectOnlyCategorySemantics, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course = CourseFactory.create(
|
||||
org='test_org',
|
||||
number='999',
|
||||
@@ -345,7 +345,7 @@ class DirectOnlyCategorySemantics(PureModulestoreTestCase):
|
||||
self.assertCourseSummaryFields(course_summaries)
|
||||
|
||||
# Verify fetched accessible courses list is a list of CourseSummery instances
|
||||
assert all((isinstance(course, CourseSummary) for course in course_summaries))
|
||||
assert all(isinstance(course, CourseSummary) for course in course_summaries)
|
||||
|
||||
@ddt.data(*itertools.product(['chapter', 'sequential'], [True, False]))
|
||||
@ddt.unpack
|
||||
|
||||
@@ -6,7 +6,6 @@ However for these tests, we make sure it also works when copying from course to
|
||||
|
||||
|
||||
import ddt
|
||||
from six.moves import range
|
||||
import pytest
|
||||
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
|
||||
@@ -6,10 +6,8 @@ Tests for split_migrator
|
||||
|
||||
import random
|
||||
import uuid
|
||||
from unittest import mock
|
||||
|
||||
import mock
|
||||
import six
|
||||
from six.moves import range, zip
|
||||
from xblock.fields import UNIQUE_ID, Reference, ReferenceList, ReferenceValueDict
|
||||
|
||||
from openedx.core.lib.tests import attr
|
||||
@@ -24,7 +22,7 @@ class TestMigration(SplitWMongoCourseBootstrapper):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestMigration, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.migrator = SplitMigrator(self.split_mongo, self.draft_mongo)
|
||||
|
||||
def _create_course(self): # lint-amnesty, pylint: disable=arguments-differ
|
||||
@@ -35,7 +33,7 @@ class TestMigration(SplitWMongoCourseBootstrapper):
|
||||
only the live ones get to published. Some are only draft, some are both, some are only live.
|
||||
* about, static_tab, and conditional documents
|
||||
"""
|
||||
super(TestMigration, self)._create_course(split=False) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super()._create_course(split=False)
|
||||
|
||||
# chapters
|
||||
chapter1_name = uuid.uuid4().hex
|
||||
@@ -167,7 +165,7 @@ class TestMigration(SplitWMongoCourseBootstrapper):
|
||||
if split_dag_root.category != 'course':
|
||||
assert presplit_dag_root.location.block_id == split_dag_root.location.block_id
|
||||
# compare all fields but references
|
||||
for name, field in six.iteritems(presplit_dag_root.fields):
|
||||
for name, field in presplit_dag_root.fields.items():
|
||||
# fields generated from UNIQUE_IDs are unique to an XBlock's scope,
|
||||
# so if such a field is unset on an XBlock, we don't expect it
|
||||
# to persist across courses
|
||||
|
||||
@@ -9,17 +9,15 @@ import random
|
||||
import re
|
||||
import unittest
|
||||
from importlib import import_module
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
import six
|
||||
from ccx_keys.locator import CCXBlockUsageLocator
|
||||
from contracts import contract
|
||||
from django.core.cache import InvalidCacheBackendError, caches
|
||||
from mock import patch
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseKey, CourseLocator, LocalId, VersionTree
|
||||
from path import Path as path
|
||||
from six.moves import range
|
||||
from xblock.fields import Reference, ReferenceList, ReferenceValueDict
|
||||
|
||||
from openedx.core.lib import tempdir
|
||||
@@ -58,7 +56,7 @@ class SplitModuleTest(unittest.TestCase):
|
||||
# Snippets of what would be in the django settings envs file
|
||||
DOC_STORE_CONFIG = {
|
||||
'host': MONGO_HOST,
|
||||
'db': 'test_xmodule_{0}'.format(os.getpid()),
|
||||
'db': f'test_xmodule_{os.getpid()}',
|
||||
'port': MONGO_PORT_NUM,
|
||||
'collection': 'modulestore',
|
||||
}
|
||||
@@ -503,7 +501,7 @@ class SplitModuleTest(unittest.TestCase):
|
||||
'''
|
||||
Sets up the initial data into the db
|
||||
'''
|
||||
for _course_id, course_spec in six.iteritems(SplitModuleTest.COURSE_CONTENT):
|
||||
for _course_id, course_spec in SplitModuleTest.COURSE_CONTENT.items():
|
||||
course = split_store.create_course(
|
||||
course_spec['org'],
|
||||
course_spec['course'],
|
||||
@@ -514,7 +512,7 @@ class SplitModuleTest(unittest.TestCase):
|
||||
root_block_id=course_spec['root_block_id']
|
||||
)
|
||||
for revision in course_spec.get('revisions', []):
|
||||
for (block_type, block_id), fields in six.iteritems(revision.get('update', {})):
|
||||
for (block_type, block_id), fields in revision.get('update', {}).items():
|
||||
# cheat since course is most frequent
|
||||
if course.location.block_id == block_id:
|
||||
block = course
|
||||
@@ -522,7 +520,7 @@ class SplitModuleTest(unittest.TestCase):
|
||||
# not easy to figure out the category but get_item won't care
|
||||
block_usage = BlockUsageLocator.make_relative(course.location, block_type, block_id)
|
||||
block = split_store.get_item(block_usage)
|
||||
for key, value in six.iteritems(fields):
|
||||
for key, value in fields.items():
|
||||
setattr(block, key, value)
|
||||
# create new blocks into dag: parent must already exist; thus, order is important
|
||||
new_ele_dict = {}
|
||||
@@ -551,7 +549,7 @@ class SplitModuleTest(unittest.TestCase):
|
||||
split_store.copy("test@edx.org", source_course, destination, [to_publish], None)
|
||||
|
||||
def setUp(self):
|
||||
super(SplitModuleTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.user_id = random.getrandbits(32)
|
||||
|
||||
def tearDown(self):
|
||||
@@ -562,7 +560,7 @@ class SplitModuleTest(unittest.TestCase):
|
||||
modulestore()._drop_database(database=False, connections=False) # pylint: disable=protected-access
|
||||
# drop the modulestore to force re init
|
||||
SplitModuleTest.modulestore = None
|
||||
super(SplitModuleTest, self).tearDown() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().tearDown()
|
||||
|
||||
def findByIdInResult(self, collection, _id): # pylint: disable=invalid-name
|
||||
"""
|
||||
@@ -953,7 +951,7 @@ class TestCourseStructureCache(SplitModuleTest):
|
||||
'org', 'course', 'test_run', self.user, BRANCH_NAME_DRAFT,
|
||||
)
|
||||
|
||||
super(TestCourseStructureCache, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
@patch('xmodule.modulestore.split_mongo.mongo_connection.get_cache')
|
||||
def test_course_structure_cache(self, mock_get_cache):
|
||||
@@ -1741,8 +1739,8 @@ class TestItemCrud(SplitModuleTest):
|
||||
# First child should have been moved to second position, and better child takes the lead
|
||||
refetch_course = store.get_course(versionless_course_locator)
|
||||
children = refetch_course.get_children()
|
||||
assert six.text_type(children[1].location) == six.text_type(first_child.location)
|
||||
assert six.text_type(children[0].location) == six.text_type(second_child.location)
|
||||
assert str(children[1].location) == str(first_child.location)
|
||||
assert str(children[0].location) == str(second_child.location)
|
||||
|
||||
# Clean up the data so we don't break other tests which apparently expect a particular state
|
||||
store.delete_course(refetch_course.id, user)
|
||||
|
||||
@@ -6,20 +6,18 @@ Tests for bulk operations in Split Modulestore.
|
||||
|
||||
import copy
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, Mock, call
|
||||
|
||||
import six
|
||||
import ddt
|
||||
from bson.objectid import ObjectId
|
||||
from mock import MagicMock, Mock, call
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from six.moves import range
|
||||
|
||||
from xmodule.modulestore.split_mongo.mongo_connection import MongoConnection
|
||||
from xmodule.modulestore.split_mongo.split import SplitBulkWriteMixin
|
||||
|
||||
VERSION_GUID_DICT = {
|
||||
'SAMPLE_VERSION_GUID': 'deadbeef1234' * 2,
|
||||
'SAMPLE_UNICODE_VERSION_GUID': u'deadbeef1234' * 2,
|
||||
'SAMPLE_UNICODE_VERSION_GUID': 'deadbeef1234' * 2,
|
||||
'BSON_OBJECTID': ObjectId()
|
||||
}
|
||||
SAMPLE_GUIDS_LIST = ['SAMPLE_VERSION_GUID', 'SAMPLE_UNICODE_VERSION_GUID', 'BSON_OBJECTID']
|
||||
@@ -28,7 +26,7 @@ SAMPLE_GUIDS_LIST = ['SAMPLE_VERSION_GUID', 'SAMPLE_UNICODE_VERSION_GUID', 'BSON
|
||||
class TestBulkWriteMixin(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
|
||||
def setUp(self):
|
||||
super(TestBulkWriteMixin, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.bulk = SplitBulkWriteMixin()
|
||||
self.bulk.SCHEMA_VERSION = 1
|
||||
self.clear_cache = self.bulk._clear_cache = Mock(name='_clear_cache')
|
||||
@@ -53,7 +51,7 @@ class TestBulkWriteMixinPreviousTransaction(TestBulkWriteMixin):
|
||||
Verify that opening and closing a transaction doesn't affect later behaviour.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(TestBulkWriteMixinPreviousTransaction, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.bulk._begin_bulk_operation(self.course_key)
|
||||
self.bulk.insert_course_index(self.course_key, MagicMock('prev-index-entry'))
|
||||
self.bulk.update_structure(self.course_key, {'this': 'is', 'the': 'previous structure', '_id': ObjectId()})
|
||||
@@ -170,8 +168,7 @@ class TestBulkWriteMixinClosed(TestBulkWriteMixin):
|
||||
self.bulk.update_structure(self.course_key.replace(branch='b'), other_structure)
|
||||
self.assertConnCalls()
|
||||
self.bulk._end_bulk_operation(self.course_key)
|
||||
six.assertCountEqual(
|
||||
self,
|
||||
self.assertCountEqual(
|
||||
[
|
||||
call.insert_structure(self.structure, self.course_key),
|
||||
call.insert_structure(other_structure, self.course_key)
|
||||
@@ -207,8 +204,7 @@ class TestBulkWriteMixinClosed(TestBulkWriteMixin):
|
||||
self.bulk.update_definition(self.course_key.replace(branch='b'), other_definition)
|
||||
self.bulk.insert_course_index(self.course_key, {'versions': {'a': self.definition['_id'], 'b': other_definition['_id']}}) # lint-amnesty, pylint: disable=line-too-long
|
||||
self.bulk._end_bulk_operation(self.course_key)
|
||||
six.assertCountEqual(
|
||||
self,
|
||||
self.assertCountEqual(
|
||||
[
|
||||
call.insert_definition(self.definition, self.course_key),
|
||||
call.insert_definition(other_definition, self.course_key),
|
||||
@@ -239,8 +235,7 @@ class TestBulkWriteMixinClosed(TestBulkWriteMixin):
|
||||
self.bulk.update_definition(self.course_key.replace(branch='b'), other_definition)
|
||||
self.assertConnCalls()
|
||||
self.bulk._end_bulk_operation(self.course_key)
|
||||
six.assertCountEqual(
|
||||
self,
|
||||
self.assertCountEqual(
|
||||
[
|
||||
call.insert_definition(self.definition, self.course_key),
|
||||
call.insert_definition(other_definition, self.course_key)
|
||||
@@ -276,8 +271,7 @@ class TestBulkWriteMixinClosed(TestBulkWriteMixin):
|
||||
self.bulk.update_structure(self.course_key.replace(branch='b'), other_structure)
|
||||
self.bulk.insert_course_index(self.course_key, {'versions': {'a': self.structure['_id'], 'b': other_structure['_id']}}) # lint-amnesty, pylint: disable=line-too-long
|
||||
self.bulk._end_bulk_operation(self.course_key)
|
||||
six.assertCountEqual(
|
||||
self,
|
||||
self.assertCountEqual(
|
||||
[
|
||||
call.insert_structure(self.structure, self.course_key),
|
||||
call.insert_structure(other_structure, self.course_key),
|
||||
@@ -385,7 +379,7 @@ class TestBulkWriteMixinFindMethods(TestBulkWriteMixin):
|
||||
def test_find_matching_course_indexes(self, branch, search_targets, matching, unmatching):
|
||||
db_indexes = [{'org': 'what', 'course': 'this', 'run': 'needs'}]
|
||||
for n, index in enumerate(matching + unmatching):
|
||||
course_key = CourseLocator('org', 'course', 'run{}'.format(n))
|
||||
course_key = CourseLocator('org', 'course', f'run{n}')
|
||||
self.bulk._begin_bulk_operation(course_key)
|
||||
for attr in ['org', 'course', 'run']:
|
||||
index[attr] = getattr(course_key, attr)
|
||||
@@ -394,7 +388,7 @@ class TestBulkWriteMixinFindMethods(TestBulkWriteMixin):
|
||||
expected = matching + db_indexes
|
||||
self.conn.find_matching_course_indexes.return_value = db_indexes
|
||||
result = self.bulk.find_matching_course_indexes(branch, search_targets)
|
||||
six.assertCountEqual(self, result, expected)
|
||||
self.assertCountEqual(result, expected)
|
||||
for item in unmatching:
|
||||
assert item not in result
|
||||
|
||||
@@ -419,7 +413,7 @@ class TestBulkWriteMixinFindMethods(TestBulkWriteMixin):
|
||||
|
||||
db_structures = [db_structure(_id) for _id in db_ids if _id not in active_ids]
|
||||
for n, _id in enumerate(active_ids):
|
||||
course_key = CourseLocator('org', 'course', 'run{}'.format(n))
|
||||
course_key = CourseLocator('org', 'course', f'run{n}')
|
||||
self.bulk._begin_bulk_operation(course_key)
|
||||
self.bulk.update_structure(course_key, active_structure(_id))
|
||||
|
||||
@@ -515,7 +509,7 @@ class TestBulkWriteMixinFindMethods(TestBulkWriteMixin):
|
||||
db_structures = [db_structure(_id) for _id in db_ids]
|
||||
active_structures = []
|
||||
for n, _id in enumerate(active_ids):
|
||||
course_key = CourseLocator('org', 'course', 'run{}'.format(n))
|
||||
course_key = CourseLocator('org', 'course', f'run{n}')
|
||||
self.bulk._begin_bulk_operation(course_key)
|
||||
structure = active_structure(_id)
|
||||
self.bulk.update_structure(course_key, structure)
|
||||
@@ -575,14 +569,14 @@ class TestBulkWriteMixinFindMethods(TestBulkWriteMixin):
|
||||
structure.setdefault('_id', ObjectId())
|
||||
|
||||
for n, structure in enumerate(active_match + active_unmatch):
|
||||
course_key = CourseLocator('org', 'course', 'run{}'.format(n))
|
||||
course_key = CourseLocator('org', 'course', f'run{n}')
|
||||
self.bulk._begin_bulk_operation(course_key)
|
||||
self.bulk.update_structure(course_key, structure)
|
||||
|
||||
self.conn.find_ancestor_structures.return_value = db_match + db_unmatch
|
||||
results = self.bulk.find_ancestor_structures(original_version, block_id)
|
||||
self.conn.find_ancestor_structures.assert_called_once_with(original_version, block_id)
|
||||
six.assertCountEqual(self, active_match + db_match, results)
|
||||
self.assertCountEqual(active_match + db_match, results)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -592,7 +586,7 @@ class TestBulkWriteMixinOpen(TestBulkWriteMixin):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestBulkWriteMixinOpen, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.bulk._begin_bulk_operation(self.course_key)
|
||||
|
||||
@ddt.data(*SAMPLE_GUIDS_LIST)
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from mock import patch
|
||||
from pymongo.errors import ConnectionFailure
|
||||
|
||||
from xmodule.exceptions import HeartbeatFailure
|
||||
|
||||
@@ -4,10 +4,9 @@ import datetime
|
||||
import os
|
||||
import random
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import mock
|
||||
import pytest
|
||||
import six
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
@@ -38,7 +37,7 @@ class SplitWMongoCourseBootstrapper(unittest.TestCase):
|
||||
db_config = {
|
||||
'host': MONGO_HOST,
|
||||
'port': MONGO_PORT_NUM,
|
||||
'db': 'test_xmodule_{}'.format(os.getpid()),
|
||||
'db': f'test_xmodule_{os.getpid()}',
|
||||
'collection': 'modulestore'
|
||||
}
|
||||
|
||||
@@ -53,7 +52,7 @@ class SplitWMongoCourseBootstrapper(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.user_id = random.getrandbits(32)
|
||||
super(SplitWMongoCourseBootstrapper, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.split_mongo = SplitMongoModuleStore(
|
||||
None,
|
||||
self.db_config,
|
||||
@@ -90,7 +89,7 @@ class SplitWMongoCourseBootstrapper(unittest.TestCase):
|
||||
)
|
||||
if not draft:
|
||||
self.draft_mongo.publish(location, self.user_id)
|
||||
if isinstance(data, six.string_types):
|
||||
if isinstance(data, str):
|
||||
fields = {'data': data}
|
||||
else:
|
||||
fields = data.copy()
|
||||
|
||||
@@ -4,9 +4,9 @@ Tests for store_utilities.py
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
import ddt
|
||||
from mock import Mock
|
||||
|
||||
from xmodule.modulestore.store_utilities import draft_node_constructor, get_draft_subtree_roots
|
||||
|
||||
|
||||
@@ -6,9 +6,10 @@ well-formed and not-well-formed XML.
|
||||
|
||||
import os.path
|
||||
from glob import glob
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
|
||||
@@ -145,7 +146,7 @@ class TestModuleStoreIgnore(TestXMLModuleStore): # lint-amnesty, pylint: disabl
|
||||
course_dir = DATA_DIR / "course_ignore"
|
||||
|
||||
def setUp(self):
|
||||
super(TestModuleStoreIgnore, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.addCleanup(remove_temp_files_from_list, list(TILDA_FILES_DICT.keys()), self.course_dir / "static")
|
||||
add_temp_files_from_dict(TILDA_FILES_DICT, self.course_dir / "static")
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import importlib
|
||||
import os
|
||||
import unittest
|
||||
from uuid import uuid4
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import mock
|
||||
import six
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from path import Path as path
|
||||
@@ -23,10 +23,7 @@ from xmodule.modulestore.xml_importer import StaticContentImporter, _update_and_
|
||||
from xmodule.tests import DATA_DIR
|
||||
from xmodule.x_module import XModuleMixin
|
||||
|
||||
if six.PY2:
|
||||
OPEN_BUILTIN = '__builtin__.open'
|
||||
else:
|
||||
OPEN_BUILTIN = 'builtins.open'
|
||||
OPEN_BUILTIN = 'builtins.open'
|
||||
|
||||
|
||||
class ModuleStoreNoSettings(unittest.TestCase):
|
||||
@@ -72,7 +69,7 @@ class ModuleStoreNoSettings(unittest.TestCase):
|
||||
Add cleanups
|
||||
"""
|
||||
self.addCleanup(self.cleanup_modulestore)
|
||||
super(ModuleStoreNoSettings, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
|
||||
#===========================================
|
||||
@@ -141,7 +138,7 @@ class RemapNamespaceTest(ModuleStoreNoSettings):
|
||||
self.field_data = KvsFieldData(kvs=DictKeyValueStore())
|
||||
self.scope_ids = ScopeIds('Bob', 'stubxblock', '123', 'import')
|
||||
self.xblock = StubXBlock(self.runtime, self.field_data, self.scope_ids)
|
||||
super(RemapNamespaceTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
def test_remap_namespace_native_xblock(self):
|
||||
|
||||
@@ -282,7 +279,7 @@ class UpdateLocationTest(ModuleStoreNoSettings):
|
||||
BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'mutablestubxblock', 'child2'),
|
||||
]
|
||||
|
||||
super(UpdateLocationTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
def _check_explicitly_set(self, block, scope, expected_explicitly_set_fields, should_be_set=False):
|
||||
""" Gets fields that are explicitly set on block and checks if they are marked as explicitly set or not """
|
||||
|
||||
@@ -12,10 +12,8 @@ from tempfile import mkdtemp
|
||||
from unittest import TestCase
|
||||
from uuid import uuid4
|
||||
|
||||
import six
|
||||
from contextlib2 import ExitStack
|
||||
from path import Path as path
|
||||
from six.moves import range, zip
|
||||
|
||||
from xmodule.contentstore.mongo import MongoContentStore
|
||||
from xmodule.modulestore.draft_and_published import ModuleStoreDraftAndPublished
|
||||
@@ -84,10 +82,10 @@ def add_temp_files_from_dict(file_dict, dir): # lint-amnesty, pylint: disable=r
|
||||
Takes in a dict formatted as: { file_name: content }, and adds files to directory
|
||||
"""
|
||||
for file_name in file_dict:
|
||||
with io.open("{}/{}".format(dir, file_name), "w") as opened_file:
|
||||
with open(f"{dir}/{file_name}", "w") as opened_file:
|
||||
content = file_dict[file_name]
|
||||
if content:
|
||||
opened_file.write(six.text_type(content))
|
||||
opened_file.write(str(content))
|
||||
|
||||
|
||||
def remove_temp_files_from_list(file_list, dir): # lint-amnesty, pylint: disable=redefined-builtin
|
||||
@@ -95,7 +93,7 @@ def remove_temp_files_from_list(file_list, dir): # lint-amnesty, pylint: disabl
|
||||
Takes in a list of file names and removes them from dir if they exist
|
||||
"""
|
||||
for file_name in file_list:
|
||||
file_path = "{}/{}".format(dir, file_name)
|
||||
file_path = f"{dir}/{file_name}"
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
@@ -105,7 +103,7 @@ class MixedSplitTestCase(TestCase):
|
||||
Stripped-down version of ModuleStoreTestCase that can be used without Django
|
||||
(i.e. for testing in common/lib/ ). Sets up MixedModuleStore and Split.
|
||||
"""
|
||||
RENDER_TEMPLATE = lambda t_n, d, ctx=None, nsp='main': u'{}: {}, {}'.format(t_n, repr(d), repr(ctx))
|
||||
RENDER_TEMPLATE = lambda t_n, d, ctx=None, nsp='main': '{}: {}, {}'.format(t_n, repr(d), repr(ctx))
|
||||
modulestore_options = {
|
||||
'default_class': 'xmodule.raw_module.RawDescriptor',
|
||||
'fs_root': DATA_DIR,
|
||||
@@ -115,7 +113,7 @@ class MixedSplitTestCase(TestCase):
|
||||
DOC_STORE_CONFIG = {
|
||||
'host': MONGO_HOST,
|
||||
'port': MONGO_PORT_NUM,
|
||||
'db': 'test_mongo_libs_{0}'.format(os.getpid()),
|
||||
'db': f'test_mongo_libs_{os.getpid()}',
|
||||
'collection': 'modulestore',
|
||||
'asset_collection': 'assetstore',
|
||||
}
|
||||
@@ -134,7 +132,7 @@ class MixedSplitTestCase(TestCase):
|
||||
"""
|
||||
Set up requirements for testing: a user ID and a modulestore
|
||||
"""
|
||||
super(MixedSplitTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.user_id = ModuleStoreEnum.UserID.test
|
||||
|
||||
self.store = MixedModuleStore(
|
||||
@@ -162,7 +160,7 @@ class MixedSplitTestCase(TestCase):
|
||||
)
|
||||
|
||||
|
||||
class ProceduralCourseTestMixin(object):
|
||||
class ProceduralCourseTestMixin:
|
||||
"""
|
||||
Contains methods for testing courses generated procedurally
|
||||
"""
|
||||
@@ -193,7 +191,7 @@ class ProceduralCourseTestMixin(object):
|
||||
descend(self.course, ['chapter', 'sequential', 'vertical', 'problem'])
|
||||
|
||||
|
||||
class MemoryCache(object):
|
||||
class MemoryCache:
|
||||
"""
|
||||
This fits the metadata_inheritance_cache_subsystem interface used by
|
||||
the modulestore, and stores the data in a dictionary in memory.
|
||||
@@ -222,7 +220,7 @@ class MemoryCache(object):
|
||||
self.data[key] = value
|
||||
|
||||
|
||||
class MongoContentstoreBuilder(object):
|
||||
class MongoContentstoreBuilder:
|
||||
"""
|
||||
A builder class for a MongoContentStore.
|
||||
"""
|
||||
@@ -233,7 +231,7 @@ class MongoContentstoreBuilder(object):
|
||||
when the context closes.
|
||||
"""
|
||||
contentstore = MongoContentStore(
|
||||
db='contentstore{}'.format(THIS_UUID),
|
||||
db=f'contentstore{THIS_UUID}',
|
||||
collection='content',
|
||||
**COMMON_DOCSTORE_CONFIG
|
||||
)
|
||||
@@ -249,7 +247,7 @@ class MongoContentstoreBuilder(object):
|
||||
return 'MongoContentstoreBuilder()'
|
||||
|
||||
|
||||
class StoreBuilderBase(object):
|
||||
class StoreBuilderBase:
|
||||
"""
|
||||
Base class for all modulestore builders.
|
||||
"""
|
||||
@@ -291,7 +289,7 @@ class MongoModulestoreBuilder(StoreBuilderBase):
|
||||
all of its assets.
|
||||
"""
|
||||
doc_store_config = dict(
|
||||
db='modulestore{}'.format(THIS_UUID),
|
||||
db=f'modulestore{THIS_UUID}',
|
||||
collection='xmodule',
|
||||
asset_collection='asset_metadata',
|
||||
**COMMON_DOCSTORE_CONFIG
|
||||
@@ -339,7 +337,7 @@ class VersioningModulestoreBuilder(StoreBuilderBase):
|
||||
all of its assets.
|
||||
"""
|
||||
doc_store_config = dict(
|
||||
db='modulestore{}'.format(THIS_UUID),
|
||||
db=f'modulestore{THIS_UUID}',
|
||||
collection='split_module',
|
||||
**COMMON_DOCSTORE_CONFIG
|
||||
)
|
||||
@@ -440,7 +438,7 @@ class MixedModulestoreBuilder(StoreBuilderBase):
|
||||
yield self.mixed_modulestore
|
||||
|
||||
def __repr__(self):
|
||||
return 'MixedModulestoreBuilder({!r}, {!r})'.format(self.store_builders, self.mappings)
|
||||
return f'MixedModulestoreBuilder({self.store_builders!r}, {self.mappings!r})'
|
||||
|
||||
def asset_collection(self):
|
||||
"""
|
||||
@@ -519,7 +517,7 @@ class PureModulestoreTestCase(TestCase):
|
||||
MODULESTORE = None
|
||||
|
||||
def setUp(self):
|
||||
super(PureModulestoreTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
builder = self.MODULESTORE.build()
|
||||
self.assets, self.store = builder.__enter__()
|
||||
|
||||
@@ -13,7 +13,6 @@ from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from importlib import import_module
|
||||
|
||||
import six
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from edx_django_utils.monitoring import set_custom_attribute
|
||||
from fs.osfs import OSFS
|
||||
@@ -128,9 +127,9 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # lint-amnesty, pyl
|
||||
# put it in the error tracker--content folks need to see it.
|
||||
|
||||
if tag in need_uniq_names:
|
||||
error_tracker(u"PROBLEM: no name of any kind specified for {tag}. Student "
|
||||
u"state will not be properly tracked for this module. Problem xml:"
|
||||
u" '{xml}...'".format(tag=tag, xml=xml[:100]))
|
||||
error_tracker("PROBLEM: no name of any kind specified for {tag}. Student "
|
||||
"state will not be properly tracked for this module. Problem xml:"
|
||||
" '{xml}...'".format(tag=tag, xml=xml[:100]))
|
||||
else:
|
||||
# TODO (vshnayder): We may want to enable this once course repos are cleaned up.
|
||||
# (or we may want to give up on the requirement for non-state-relevant issues...)
|
||||
@@ -143,8 +142,8 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # lint-amnesty, pyl
|
||||
# doesn't store state, don't complain about things that are
|
||||
# hashed.
|
||||
if tag in need_uniq_names:
|
||||
msg = (u"Non-unique url_name in xml. This may break state tracking for content."
|
||||
u" url_name={0}. Content={1}".format(url_name, xml[:100]))
|
||||
msg = ("Non-unique url_name in xml. This may break state tracking for content."
|
||||
" url_name={}. Content={}".format(url_name, xml[:100]))
|
||||
error_tracker("PROBLEM: " + msg)
|
||||
log.warning(msg)
|
||||
# Just set name to fallback_name--if there are multiple things with the same fallback name,
|
||||
@@ -181,14 +180,14 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # lint-amnesty, pyl
|
||||
msg = "Error loading from xml. %s"
|
||||
log.warning(
|
||||
msg,
|
||||
six.text_type(err)[:200],
|
||||
str(err)[:200],
|
||||
# Normally, we don't want lots of exception traces in our logs from common
|
||||
# content problems. But if you're debugging the xml loading code itself,
|
||||
# uncomment the next line.
|
||||
# exc_info=True
|
||||
)
|
||||
|
||||
msg = msg % (six.text_type(err)[:200])
|
||||
msg = msg % (str(err)[:200])
|
||||
|
||||
self.error_tracker(msg)
|
||||
err_msg = msg + "\n" + exc_info_to_str(sys.exc_info())
|
||||
@@ -221,7 +220,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # lint-amnesty, pyl
|
||||
descriptor.save()
|
||||
return descriptor
|
||||
|
||||
render_template = lambda template, context: u''
|
||||
render_template = lambda template, context: ''
|
||||
|
||||
# TODO (vshnayder): we are somewhat architecturally confused in the loading code:
|
||||
# load_item should actually be get_instance, because it expects the course-specific
|
||||
@@ -234,7 +233,7 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): # lint-amnesty, pyl
|
||||
|
||||
id_manager = CourseImportLocationManager(course_id, target_course_id)
|
||||
|
||||
super(ImportSystem, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(
|
||||
load_item=load_item,
|
||||
resources_fs=resources_fs,
|
||||
render_template=render_template,
|
||||
@@ -258,7 +257,7 @@ class CourseLocationManager(OpaqueKeyReader, AsideKeyGenerator):
|
||||
based within a course
|
||||
"""
|
||||
def __init__(self, course_id):
|
||||
super(CourseLocationManager, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
self.course_id = course_id
|
||||
self.autogen_ids = itertools.count(0)
|
||||
|
||||
@@ -298,7 +297,7 @@ class CourseImportLocationManager(CourseLocationManager):
|
||||
see https://openedx.atlassian.net/browse/MA-417 as a pending TODO.
|
||||
"""
|
||||
def __init__(self, course_id, target_course_id):
|
||||
super(CourseImportLocationManager, self).__init__(course_id=course_id) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(course_id=course_id)
|
||||
self.target_course_id = target_course_id
|
||||
|
||||
|
||||
@@ -326,7 +325,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
source_dirs or course_ids (list of str): If specified, the list of source_dirs or course_ids to load.
|
||||
Otherwise, load all courses. Note, providing both
|
||||
"""
|
||||
super(XMLModuleStore, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.data_dir = path(data_dir)
|
||||
self.modules = defaultdict(dict) # course_id -> dict(location -> XBlock)
|
||||
@@ -378,10 +377,10 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
try:
|
||||
course_descriptor = self.load_course(course_dir, course_ids, errorlog.tracker, target_course_id)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
msg = "ERROR: Failed to load courselike '{0}': {1}".format(
|
||||
course_dir.encode("utf-8"), six.text_type(exc)
|
||||
msg = "ERROR: Failed to load courselike '{}': {}".format(
|
||||
course_dir.encode("utf-8"), str(exc)
|
||||
)
|
||||
set_custom_attribute('course_import_failure', "Courselike load failure: {}".format(msg))
|
||||
set_custom_attribute('course_import_failure', f"Courselike load failure: {msg}")
|
||||
log.exception(msg)
|
||||
errorlog.tracker(msg)
|
||||
self.errored_courses[course_dir] = errorlog
|
||||
@@ -424,8 +423,8 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
try:
|
||||
with open(policy_path) as f:
|
||||
return json.load(f)
|
||||
except (IOError, ValueError) as err:
|
||||
msg = "ERROR: loading courselike policy from {0}".format(policy_path)
|
||||
except (OSError, ValueError) as err:
|
||||
msg = f"ERROR: loading courselike policy from {policy_path}"
|
||||
tracker(msg)
|
||||
log.warning(msg + " " + str(err)) # lint-amnesty, pylint: disable=logging-not-lazy
|
||||
return {}
|
||||
@@ -478,7 +477,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
|
||||
# VS[compat]: remove once courses use the policy dirs.
|
||||
if policy == {}:
|
||||
old_policy_path = self.data_dir / course_dir / 'policies' / '{0}.json'.format(url_name)
|
||||
old_policy_path = self.data_dir / course_dir / 'policies' / f'{url_name}.json'
|
||||
policy = self.load_policy(old_policy_path, tracker)
|
||||
else:
|
||||
policy = {}
|
||||
@@ -608,7 +607,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
with open(file_path) as field_content_file:
|
||||
field_data = json.load(field_content_file)
|
||||
data_content = {field: field_data}
|
||||
except (IOError, ValueError):
|
||||
except (OSError, ValueError):
|
||||
# ignore this exception
|
||||
# only new exported courses which use content fields other than 'metadata' and 'data'
|
||||
# will have this file '{dirname}.{field_name}.json'
|
||||
@@ -627,7 +626,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
if filepath.endswith('~'): # skip *~ files
|
||||
continue
|
||||
|
||||
with io.open(filepath) as f:
|
||||
with open(filepath) as f:
|
||||
try:
|
||||
if filepath.find('.json') != -1:
|
||||
# json file with json data content
|
||||
@@ -638,7 +637,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
try:
|
||||
# get and update data field in xblock runtime
|
||||
module = system.load_item(loc)
|
||||
for key, value in six.iteritems(data_content):
|
||||
for key, value in data_content.items():
|
||||
setattr(module, key, value)
|
||||
module.save()
|
||||
except ItemNotFoundError:
|
||||
@@ -681,8 +680,8 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
self.modules[course_descriptor.id][module.scope_ids.usage_id] = module
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
logging.exception("Failed to load %s. Skipping... \
|
||||
Exception: %s", filepath, six.text_type(exc))
|
||||
system.error_tracker("ERROR: " + six.text_type(exc))
|
||||
Exception: %s", filepath, str(exc))
|
||||
system.error_tracker("ERROR: " + str(exc))
|
||||
|
||||
def has_item(self, usage_key):
|
||||
"""
|
||||
@@ -756,7 +755,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
for fields in [settings, content, qualifiers]
|
||||
)
|
||||
|
||||
for mod_loc, module in six.iteritems(self.modules[course_id]):
|
||||
for mod_loc, module in self.modules[course_id].items():
|
||||
if _block_matches_all(mod_loc, module):
|
||||
items.append(module)
|
||||
|
||||
@@ -796,7 +795,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
Return a dictionary of course_dir -> [(msg, exception_str)], for each
|
||||
course_dir where course loading failed.
|
||||
"""
|
||||
return dict((k, self.errored_courses[k].errors) for k in self.errored_courses)
|
||||
return {k: self.errored_courses[k].errors for k in self.errored_courses}
|
||||
|
||||
def get_orphans(self, course_key, **kwargs):
|
||||
"""
|
||||
@@ -847,7 +846,7 @@ class XMLModuleStore(ModuleStoreReadBase):
|
||||
A context manager for temporarily setting the branch value for the store to the given branch_setting.
|
||||
"""
|
||||
if branch_setting != ModuleStoreEnum.Branch.published_only:
|
||||
raise ValueError(u"Cannot set branch setting to {} on a ReadOnly store".format(branch_setting))
|
||||
raise ValueError(f"Cannot set branch setting to {branch_setting} on a ReadOnly store")
|
||||
yield
|
||||
|
||||
def _find_course_asset(self, asset_key):
|
||||
|
||||
@@ -9,10 +9,8 @@ from abc import abstractmethod
|
||||
from json import dumps
|
||||
|
||||
import lxml.etree
|
||||
import six
|
||||
from fs.osfs import OSFS
|
||||
from opaque_keys.edx.locator import CourseLocator, LibraryLocator
|
||||
from six import text_type
|
||||
from xblock.fields import Reference, ReferenceList, ReferenceValueDict, Scope
|
||||
|
||||
from xmodule.assetstore import AssetMetadata
|
||||
@@ -60,12 +58,12 @@ def _export_drafts(modulestore, course_key, export_fs, xml_centric_course_key):
|
||||
# if module has no parent, set its parent_url to `None`
|
||||
parent_url = None
|
||||
if parent_loc is not None:
|
||||
parent_url = text_type(parent_loc)
|
||||
parent_url = str(parent_loc)
|
||||
|
||||
draft_node = draft_node_constructor(
|
||||
draft_module,
|
||||
location=draft_module.location,
|
||||
url=text_type(draft_module.location),
|
||||
url=str(draft_module.location),
|
||||
parent_location=parent_loc,
|
||||
parent_url=parent_url,
|
||||
)
|
||||
@@ -103,7 +101,7 @@ def _export_drafts(modulestore, course_key, export_fs, xml_centric_course_key):
|
||||
draft_node.module.add_xml_to_node(node)
|
||||
|
||||
|
||||
class ExportManager(object):
|
||||
class ExportManager:
|
||||
"""
|
||||
Manages XML exporting for courselike objects.
|
||||
"""
|
||||
@@ -121,7 +119,7 @@ class ExportManager(object):
|
||||
self.contentstore = contentstore
|
||||
self.courselike_key = courselike_key
|
||||
self.root_dir = root_dir
|
||||
self.target_dir = text_type(target_dir)
|
||||
self.target_dir = str(target_dir)
|
||||
|
||||
@abstractmethod
|
||||
def get_key(self):
|
||||
@@ -200,7 +198,7 @@ class CourseExportManager(ExportManager):
|
||||
return self.modulestore.get_course(self.courselike_key, depth=None, lazy=False)
|
||||
|
||||
def process_root(self, root, export_fs):
|
||||
with export_fs.open(u'course.xml', 'wb') as course_xml:
|
||||
with export_fs.open('course.xml', 'wb') as course_xml:
|
||||
lxml.etree.ElementTree(root).write(course_xml, encoding='utf-8')
|
||||
|
||||
def process_extra(self, root, courselike, root_courselike_dir, xml_centric_courselike_key, export_fs):
|
||||
@@ -242,7 +240,7 @@ class CourseExportManager(ExportManager):
|
||||
output_dir = root_courselike_dir + '/static/images/'
|
||||
if not os.path.isdir(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
with OSFS(output_dir).open(u'course_image.jpg', 'wb') as course_image_file:
|
||||
with OSFS(output_dir).open('course_image.jpg', 'wb') as course_image_file:
|
||||
course_image_file.write(course_image.data)
|
||||
|
||||
# export the static tabs
|
||||
@@ -273,12 +271,12 @@ class CourseExportManager(ExportManager):
|
||||
course_run_policy_dir = policies_dir.makedir(course_policy_dir_name, recreate=True)
|
||||
|
||||
# export the grading policy
|
||||
with course_run_policy_dir.open(u'grading_policy.json', 'wb') as grading_policy:
|
||||
with course_run_policy_dir.open('grading_policy.json', 'wb') as grading_policy:
|
||||
grading_policy.write(dumps(courselike.grading_policy, cls=EdxJSONEncoder,
|
||||
sort_keys=True, indent=4).encode('utf-8'))
|
||||
|
||||
# export all of the course metadata in policy.json
|
||||
with course_run_policy_dir.open(u'policy.json', 'wb') as course_policy:
|
||||
with course_run_policy_dir.open('policy.json', 'wb') as course_policy:
|
||||
policy = {'course/' + courselike.location.run: own_metadata(courselike)}
|
||||
course_policy.write(dumps(policy, cls=EdxJSONEncoder, sort_keys=True, indent=4).encode('utf-8'))
|
||||
|
||||
@@ -357,7 +355,7 @@ def adapt_references(subtree, destination_course_key, export_fs):
|
||||
Map every reference in the subtree into destination_course_key and set it back into the xblock fields
|
||||
"""
|
||||
subtree.runtime.export_fs = export_fs # ensure everything knows where it's going!
|
||||
for field_name, field in six.iteritems(subtree.fields):
|
||||
for field_name, field in subtree.fields.items():
|
||||
if field.is_set_on(subtree):
|
||||
if isinstance(field, Reference):
|
||||
value = field.read_from(subtree)
|
||||
@@ -374,7 +372,7 @@ def adapt_references(subtree, destination_course_key, export_fs):
|
||||
elif isinstance(field, ReferenceValueDict):
|
||||
field.write_to(
|
||||
subtree, {
|
||||
key: ele.map_into_course(destination_course_key) for key, ele in six.iteritems(field.read_from(subtree)) # lint-amnesty, pylint: disable=line-too-long
|
||||
key: ele.map_into_course(destination_course_key) for key, ele in field.read_from(subtree).items() # lint-amnesty, pylint: disable=line-too-long
|
||||
}
|
||||
)
|
||||
|
||||
@@ -388,7 +386,7 @@ def _export_field_content(xblock_item, item_dir):
|
||||
for field_name in module_data:
|
||||
if field_name not in DEFAULT_CONTENT_FIELDS:
|
||||
# filename format: {dirname}.{field_name}.json
|
||||
with item_dir.open(u'{0}.{1}.{2}'.format(xblock_item.location.block_id, field_name, 'json'),
|
||||
with item_dir.open('{}.{}.{}'.format(xblock_item.location.block_id, field_name, 'json'),
|
||||
'wb') as field_content_file:
|
||||
field_content_file.write(dumps(module_data.get(field_name, {}), cls=EdxJSONEncoder,
|
||||
sort_keys=True, indent=4).encode('utf-8'))
|
||||
|
||||
@@ -30,7 +30,6 @@ import os
|
||||
import re
|
||||
from abc import abstractmethod
|
||||
|
||||
import six
|
||||
import xblock
|
||||
from edx_django_utils.monitoring import set_custom_attribute
|
||||
from lxml import etree
|
||||
@@ -92,7 +91,7 @@ class StaticContentImporter: # lint-amnesty, pylint: disable=missing-class-docs
|
||||
try:
|
||||
with open(course_data_path / 'policies/assets.json') as f:
|
||||
self.policy = json.load(f)
|
||||
except (IOError, ValueError) as err: # lint-amnesty, pylint: disable=unused-variable
|
||||
except (OSError, ValueError) as err: # lint-amnesty, pylint: disable=unused-variable
|
||||
# xml backed courses won't have this file, only exported courses;
|
||||
# so, its absence is not really an exception.
|
||||
self.policy = {}
|
||||
@@ -132,7 +131,7 @@ class StaticContentImporter: # lint-amnesty, pylint: disable=missing-class-docs
|
||||
try:
|
||||
with open(full_file_path, 'rb') as f:
|
||||
data = f.read()
|
||||
except IOError:
|
||||
except OSError:
|
||||
# OS X "companion files". See
|
||||
# http://www.diigo.com/annotated/0c936fda5da4aa1159c189cea227e174
|
||||
if filename.startswith('._'):
|
||||
@@ -174,14 +173,14 @@ class StaticContentImporter: # lint-amnesty, pylint: disable=missing-class-docs
|
||||
try:
|
||||
self.static_content_store.save(content)
|
||||
except Exception as err: # lint-amnesty, pylint: disable=broad-except
|
||||
msg = "Error importing {0}, error={1}".format(file_subpath, err)
|
||||
msg = f"Error importing {file_subpath}, error={err}"
|
||||
log.exception(msg)
|
||||
set_custom_attribute('course_import_failure', "Static Content Save Failure: {}".format(msg))
|
||||
set_custom_attribute('course_import_failure', f"Static Content Save Failure: {msg}")
|
||||
|
||||
return file_subpath, asset_key
|
||||
|
||||
|
||||
class ImportManager(object):
|
||||
class ImportManager:
|
||||
"""
|
||||
Import xml-based courselikes from data_dir into modulestore.
|
||||
|
||||
@@ -352,7 +351,7 @@ class ImportManager(object):
|
||||
asset_md = AssetMetadata(asset_key)
|
||||
asset_md.from_xml(asset)
|
||||
all_assets.append(asset_md)
|
||||
except IOError:
|
||||
except OSError:
|
||||
logging.info('No %s file is present with asset metadata.', assets_filename)
|
||||
return
|
||||
except Exception: # pylint: disable=W0703
|
||||
@@ -378,7 +377,7 @@ class ImportManager(object):
|
||||
# first into the store
|
||||
course_data_path = path(self.data_dir) / source_courselike.data_dir
|
||||
|
||||
log.debug(u'======> IMPORTING courselike %s', courselike_key)
|
||||
log.debug('======> IMPORTING courselike %s', courselike_key)
|
||||
|
||||
if not self.do_import_static:
|
||||
# for old-style xblock where this was actually linked to kvs
|
||||
@@ -494,9 +493,9 @@ class ImportManager(object):
|
||||
runtime=courselike.runtime,
|
||||
)
|
||||
except Exception:
|
||||
msg = 'failed to import module location {}'.format(leftover)
|
||||
msg = f'failed to import module location {leftover}'
|
||||
log.error(msg)
|
||||
set_custom_attribute('course_import_failure', "Module Load failure: {}".format(msg))
|
||||
set_custom_attribute('course_import_failure', f"Module Load failure: {msg}")
|
||||
raise
|
||||
|
||||
def run_imports(self):
|
||||
@@ -601,13 +600,13 @@ class CourseImportManager(ImportManager):
|
||||
# If we are importing into a course with a different course_id and wiki_slug is equal to either of these default
|
||||
# values then remap it so that the wiki does not point to the old wiki.
|
||||
if courselike_key != course.id:
|
||||
original_unique_wiki_slug = u'{0}.{1}.{2}'.format(
|
||||
original_unique_wiki_slug = '{}.{}.{}'.format(
|
||||
courselike_key.org,
|
||||
courselike_key.course,
|
||||
courselike_key.run
|
||||
)
|
||||
if course.wiki_slug == original_unique_wiki_slug or course.wiki_slug == courselike_key.course:
|
||||
course.wiki_slug = u'{0}.{1}.{2}'.format(
|
||||
course.wiki_slug = '{}.{}.{}'.format(
|
||||
course.id.org,
|
||||
course.id.course,
|
||||
course.id.run,
|
||||
@@ -750,7 +749,7 @@ def _update_and_import_module(
|
||||
Update all the module reference fields to the destination course id,
|
||||
then import the module into the destination course.
|
||||
"""
|
||||
logging.debug(u'processing import of module %s...', six.text_type(module.location))
|
||||
logging.debug('processing import of module %s...', str(module.location))
|
||||
|
||||
def _update_module_references(module, source_course_id, dest_course_id):
|
||||
"""
|
||||
@@ -770,7 +769,7 @@ def _update_and_import_module(
|
||||
return reference
|
||||
|
||||
fields = {}
|
||||
for field_name, field in six.iteritems(module.fields):
|
||||
for field_name, field in module.fields.items():
|
||||
if field.scope != Scope.parent and field.is_set_on(module):
|
||||
if isinstance(field, Reference):
|
||||
value = field.read_from(module)
|
||||
@@ -786,7 +785,7 @@ def _update_and_import_module(
|
||||
fields[field_name] = {
|
||||
key: _convert_ref_fields_to_new_namespace(reference)
|
||||
for key, reference
|
||||
in six.iteritems(reference_dict)
|
||||
in reference_dict.items()
|
||||
}
|
||||
elif field_name == 'xml_attributes':
|
||||
value = field.read_from(module)
|
||||
@@ -962,7 +961,7 @@ def _import_course_draft(
|
||||
# Skip any OSX quarantine files, prefixed with a '._'.
|
||||
continue
|
||||
module_path = os.path.join(rootdir, filename)
|
||||
with io.open(module_path, 'r') as f:
|
||||
with open(module_path, 'r') as f:
|
||||
try:
|
||||
xml = f.read()
|
||||
|
||||
@@ -984,7 +983,7 @@ def _import_course_draft(
|
||||
|
||||
index = index_in_children_list(descriptor)
|
||||
parent_url = get_parent_url(descriptor, xml)
|
||||
draft_url = six.text_type(descriptor.location)
|
||||
draft_url = str(descriptor.location)
|
||||
|
||||
draft = draft_node_constructor(
|
||||
module=descriptor, url=draft_url, parent_url=parent_url, index=index
|
||||
@@ -1033,7 +1032,7 @@ def check_module_metadata_editability(module):
|
||||
print(
|
||||
": found non-editable metadata on {url}. "
|
||||
"These metadata keys are not supported = {keys}".format(
|
||||
url=six.text_type(module.location), keys=illegal_keys
|
||||
url=str(module.location), keys=illegal_keys
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1079,7 +1078,7 @@ def create_xml_attributes(module, xml):
|
||||
Make up for modules which don't define xml_attributes by creating them here and populating
|
||||
"""
|
||||
xml_attrs = {}
|
||||
for attr, val in six.iteritems(xml.attrib):
|
||||
for attr, val in xml.attrib.items():
|
||||
if attr not in module.fields:
|
||||
# translate obsolete attr
|
||||
if attr == 'parent_sequential_url':
|
||||
@@ -1106,7 +1105,7 @@ def validate_category_hierarchy( # lint-amnesty, pylint: disable=missing-functi
|
||||
|
||||
parents = []
|
||||
# get all modules of parent_category
|
||||
for module in six.itervalues(module_store.modules[course_id]):
|
||||
for module in module_store.modules[course_id].values():
|
||||
if module.location.block_type == parent_category:
|
||||
parents.append(module)
|
||||
|
||||
@@ -1162,7 +1161,7 @@ def validate_course_policy(module_store, course_id):
|
||||
"""
|
||||
# is there a reliable way to get the module location just given the course_id?
|
||||
warn_cnt = 0
|
||||
for module in six.itervalues(module_store.modules[course_id]):
|
||||
for module in module_store.modules[course_id].values():
|
||||
if module.location.block_type == 'course':
|
||||
if not module._field_data.has(module, 'rerandomize'): # lint-amnesty, pylint: disable=protected-access
|
||||
warn_cnt += 1
|
||||
@@ -1204,7 +1203,7 @@ def perform_xlint( # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
warn_cnt += _warn_cnt
|
||||
|
||||
# first count all errors and warnings as part of the XMLModuleStore import
|
||||
for err_log in six.itervalues(module_store._course_errors): # pylint: disable=protected-access
|
||||
for err_log in module_store._course_errors.values(): # pylint: disable=protected-access
|
||||
for err_log_entry in err_log.errors:
|
||||
msg = err_log_entry[0]
|
||||
if msg.startswith('ERROR:'):
|
||||
@@ -1213,7 +1212,7 @@ def perform_xlint( # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
warn_cnt += 1
|
||||
|
||||
# then count outright all courses that failed to load at all
|
||||
for err_log in six.itervalues(module_store.errored_courses):
|
||||
for err_log in module_store.errored_courses.values():
|
||||
for err_log_entry in err_log.errors:
|
||||
msg = err_log_entry[0]
|
||||
print(msg)
|
||||
|
||||
@@ -4,7 +4,6 @@ openedx.dynamic_partition plugin.
|
||||
"""
|
||||
import logging
|
||||
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
@@ -34,21 +33,21 @@ def create_enrollment_track_partition(course):
|
||||
log.warning("No 'enrollment_track' scheme registered, EnrollmentTrackUserPartition will not be created.")
|
||||
return None
|
||||
|
||||
used_ids = set(p.id for p in course.user_partitions)
|
||||
used_ids = {p.id for p in course.user_partitions}
|
||||
if ENROLLMENT_TRACK_PARTITION_ID in used_ids:
|
||||
log.warning(
|
||||
"Can't add 'enrollment_track' partition, as ID {id} is assigned to {partition} in course {course}.".format(
|
||||
id=ENROLLMENT_TRACK_PARTITION_ID,
|
||||
partition=get_partition_from_id(course.user_partitions, ENROLLMENT_TRACK_PARTITION_ID).name,
|
||||
course=six.text_type(course.id)
|
||||
course=str(course.id)
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
partition = enrollment_track_scheme.create_user_partition(
|
||||
id=ENROLLMENT_TRACK_PARTITION_ID,
|
||||
name=_(u"Enrollment Track Groups"),
|
||||
description=_(u"Partition for segmenting users by enrollment track"),
|
||||
parameters={"course_id": six.text_type(course.id)}
|
||||
name=_("Enrollment Track Groups"),
|
||||
description=_("Partition for segmenting users by enrollment track"),
|
||||
parameters={"course_id": str(course.id)}
|
||||
)
|
||||
return partition
|
||||
|
||||
@@ -58,7 +58,7 @@ class Group(namedtuple("Group", "id name")):
|
||||
VERSION = 1
|
||||
|
||||
def __new__(cls, id, name):
|
||||
return super(Group, cls).__new__(cls, int(id), name)
|
||||
return super().__new__(cls, int(id), name)
|
||||
|
||||
def to_json(self):
|
||||
"""
|
||||
@@ -88,11 +88,11 @@ class Group(namedtuple("Group", "id name")):
|
||||
|
||||
for key in ("id", "name", "version"):
|
||||
if key not in value:
|
||||
raise TypeError("Group dict {0} missing value key '{1}'".format(
|
||||
raise TypeError("Group dict {} missing value key '{}'".format(
|
||||
value, key))
|
||||
|
||||
if value["version"] != Group.VERSION:
|
||||
raise TypeError("Group dict {0} has unexpected version".format(
|
||||
raise TypeError("Group dict {} has unexpected version".format(
|
||||
value))
|
||||
|
||||
return Group(value["id"], value["name"])
|
||||
@@ -133,7 +133,7 @@ class UserPartition(namedtuple("UserPartition", "id name description groups sche
|
||||
if parameters is None:
|
||||
parameters = {}
|
||||
|
||||
return super(UserPartition, cls).__new__(cls, int(id), name, description, groups, scheme, parameters, active)
|
||||
return super().__new__(cls, int(id), name, description, groups, scheme, parameters, active)
|
||||
|
||||
@staticmethod
|
||||
def get_scheme(name):
|
||||
@@ -147,7 +147,7 @@ class UserPartition(namedtuple("UserPartition", "id name description groups sche
|
||||
try:
|
||||
scheme = UserPartition.scheme_extensions[name].plugin # lint-amnesty, pylint: disable=unsubscriptable-object
|
||||
except KeyError:
|
||||
raise UserPartitionError("Unrecognized scheme '{0}'".format(name)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise UserPartitionError(f"Unrecognized scheme '{name}'") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
scheme.name = name
|
||||
return scheme
|
||||
|
||||
@@ -184,7 +184,7 @@ class UserPartition(namedtuple("UserPartition", "id name description groups sche
|
||||
|
||||
for key in ("id", "name", "description", "version", "groups"):
|
||||
if key not in value:
|
||||
raise TypeError("UserPartition dict {0} missing value key '{1}'".format(value, key))
|
||||
raise TypeError(f"UserPartition dict {value} missing value key '{key}'")
|
||||
|
||||
if value["version"] == 1:
|
||||
# If no scheme was provided, set it to the default ('random')
|
||||
@@ -195,21 +195,21 @@ class UserPartition(namedtuple("UserPartition", "id name description groups sche
|
||||
# version, we should try to read it rather than raising an exception.
|
||||
elif value["version"] >= 2:
|
||||
if "scheme" not in value:
|
||||
raise TypeError("UserPartition dict {0} missing value key 'scheme'".format(value))
|
||||
raise TypeError(f"UserPartition dict {value} missing value key 'scheme'")
|
||||
|
||||
scheme_id = value["scheme"]
|
||||
else:
|
||||
raise TypeError("UserPartition dict {0} has unexpected version".format(value))
|
||||
raise TypeError(f"UserPartition dict {value} has unexpected version")
|
||||
|
||||
parameters = value.get("parameters", {})
|
||||
active = value.get("active", True)
|
||||
groups = [Group.from_json(g) for g in value["groups"]]
|
||||
scheme = UserPartition.get_scheme(scheme_id)
|
||||
if not scheme:
|
||||
raise TypeError("UserPartition dict {0} has unrecognized scheme {1}".format(value, scheme_id))
|
||||
raise TypeError(f"UserPartition dict {value} has unrecognized scheme {scheme_id}")
|
||||
|
||||
if getattr(scheme, 'read_only', False):
|
||||
raise ReadOnlyUserPartitionError("UserPartition dict {0} uses scheme {1} which is read only".format(value, scheme_id)) # lint-amnesty, pylint: disable=line-too-long
|
||||
raise ReadOnlyUserPartitionError(f"UserPartition dict {value} uses scheme {scheme_id} which is read only") # lint-amnesty, pylint: disable=line-too-long
|
||||
|
||||
if hasattr(scheme, "create_user_partition"):
|
||||
return scheme.create_user_partition(
|
||||
|
||||
@@ -81,7 +81,7 @@ def _get_dynamic_partitions(course):
|
||||
return generated_partitions
|
||||
|
||||
|
||||
class PartitionService(object):
|
||||
class PartitionService:
|
||||
"""
|
||||
This is an XBlock service that returns information about the user partitions associated
|
||||
with a given course.
|
||||
@@ -136,8 +136,8 @@ class PartitionService(object):
|
||||
user_partition = self.get_user_partition(user_partition_id)
|
||||
if user_partition is None:
|
||||
raise ValueError(
|
||||
"Configuration problem! No user_partition with id {0} "
|
||||
"in course {1}".format(user_partition_id, self._course_id)
|
||||
"Configuration problem! No user_partition with id {} "
|
||||
"in course {}".format(user_partition_id, self._course_id)
|
||||
)
|
||||
|
||||
group = self.get_group(user, user_partition)
|
||||
|
||||
@@ -5,11 +5,10 @@ Test the partitions and partitions service
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
import six
|
||||
from django.test import TestCase
|
||||
from mock import Mock # lint-amnesty, pylint: disable=unused-import
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from stevedore.extension import Extension, ExtensionManager
|
||||
|
||||
@@ -96,12 +95,12 @@ class TestGroup(TestCase):
|
||||
assert 'programmer' not in group.to_json()
|
||||
|
||||
|
||||
class MockUserPartitionScheme(object):
|
||||
class MockUserPartitionScheme:
|
||||
"""
|
||||
Mock user partition scheme
|
||||
"""
|
||||
def __init__(self, name="mock", current_group=None, **kwargs):
|
||||
super(MockUserPartitionScheme, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
self.name = name
|
||||
self.current_group = current_group
|
||||
|
||||
@@ -137,7 +136,7 @@ class PartitionTestCase(TestCase):
|
||||
ENROLLMENT_TRACK_SCHEME_NAME = "enrollment_track"
|
||||
|
||||
def setUp(self):
|
||||
super(PartitionTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
# Set up two user partition schemes: mock and random
|
||||
self.non_random_scheme = MockUserPartitionScheme(self.TEST_SCHEME_NAME)
|
||||
self.random_scheme = MockUserPartitionScheme("random")
|
||||
@@ -419,7 +418,7 @@ class MockPartitionService(PartitionService):
|
||||
Mock PartitionService for testing.
|
||||
"""
|
||||
def __init__(self, course, **kwargs):
|
||||
super(MockPartitionService, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
self._course = course
|
||||
|
||||
def get_course(self):
|
||||
@@ -432,7 +431,7 @@ class PartitionServiceBaseClass(PartitionTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(PartitionServiceBaseClass, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
ContentTypeGatingConfig.objects.create(
|
||||
enabled=True,
|
||||
@@ -448,7 +447,7 @@ class PartitionServiceBaseClass(PartitionTestCase):
|
||||
# extra param to this method. Just has to be unique per user.
|
||||
user_id = abs(hash(username))
|
||||
self.user = Mock(
|
||||
username=username, email='{}@edx.org'.format(username), is_staff=False, is_active=True, id=user_id
|
||||
username=username, email=f'{username}@edx.org', is_staff=False, is_active=True, id=user_id
|
||||
)
|
||||
self.course.user_partitions = [self.user_partition]
|
||||
|
||||
@@ -543,7 +542,7 @@ class TestGetCourseUserPartitions(PartitionServiceBaseClass):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestGetCourseUserPartitions, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
TestGetCourseUserPartitions._enable_enrollment_track_partition(True)
|
||||
|
||||
@staticmethod
|
||||
@@ -562,7 +561,7 @@ class TestGetCourseUserPartitions(PartitionServiceBaseClass):
|
||||
assert self.TEST_SCHEME_NAME == all_partitions[0].scheme.name
|
||||
enrollment_track_partition = all_partitions[1]
|
||||
assert self.ENROLLMENT_TRACK_SCHEME_NAME == enrollment_track_partition.scheme.name
|
||||
assert six.text_type(self.course.id) == enrollment_track_partition.parameters['course_id']
|
||||
assert str(self.course.id) == enrollment_track_partition.parameters['course_id']
|
||||
assert ENROLLMENT_TRACK_PARTITION_ID == enrollment_track_partition.id
|
||||
|
||||
def test_enrollment_track_partition_not_added_if_conflict(self):
|
||||
|
||||
@@ -9,7 +9,7 @@ frac() and __str__().
|
||||
import numbers
|
||||
|
||||
|
||||
class Progress(object):
|
||||
class Progress: # pylint: disable=eq-without-hash
|
||||
'''Represents a progress of a/b (a out of b done)
|
||||
|
||||
a and b must be numeric, but not necessarily integer, with
|
||||
@@ -29,7 +29,7 @@ class Progress(object):
|
||||
# Want to do all checking at construction time, so explicitly check types
|
||||
if not (isinstance(a, numbers.Number) and
|
||||
isinstance(b, numbers.Number)):
|
||||
raise TypeError('a and b must be numbers. Passed {0}/{1}'.format(a, b))
|
||||
raise TypeError(f'a and b must be numbers. Passed {a}/{b}')
|
||||
|
||||
if a > b:
|
||||
a = b
|
||||
@@ -38,7 +38,7 @@ class Progress(object):
|
||||
a = 0
|
||||
|
||||
if b <= 0:
|
||||
raise ValueError('fraction a/b = {0}/{1} must have b > 0'.format(a, b))
|
||||
raise ValueError(f'fraction a/b = {a}/{b} must have b > 0')
|
||||
|
||||
self._a = a
|
||||
self._b = b
|
||||
@@ -116,8 +116,8 @@ class Progress(object):
|
||||
|
||||
'''
|
||||
(a, b) = self.frac()
|
||||
display = lambda n: '{:.2f}'.format(n).rstrip('0').rstrip('.')
|
||||
return "{0}/{1}".format(display(a), display(b))
|
||||
display = lambda n: f'{n:.2f}'.rstrip('0').rstrip('.')
|
||||
return "{}/{}".format(display(a), display(b))
|
||||
|
||||
@staticmethod
|
||||
def add_counts(a, b):
|
||||
|
||||
@@ -102,7 +102,7 @@ class RandomizeBlock(
|
||||
"""
|
||||
if self.child is None:
|
||||
# raise error instead? In fact, could complain on descriptor load...
|
||||
return Fragment(content=u"<div>Nothing to randomize between</div>")
|
||||
return Fragment(content="<div>Nothing to randomize between</div>")
|
||||
|
||||
return self.child.render(STUDENT_VIEW, context)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ log = logging.getLogger(__name__)
|
||||
PRE_TAG_REGEX = re.compile(r'<pre\b[^>]*>(?:(?=([^<]+))\1|<(?!pre\b[^>]*>))*?</pre>')
|
||||
|
||||
|
||||
class RawMixin(object):
|
||||
class RawMixin:
|
||||
"""
|
||||
Common code between RawDescriptor and XBlocks converted from XModules.
|
||||
"""
|
||||
@@ -68,8 +68,8 @@ class RawMixin(object):
|
||||
lines = self.data.split('\n')
|
||||
line, offset = err.position
|
||||
msg = (
|
||||
u"Unable to create xml for module {loc}. "
|
||||
u"Context: '{context}'"
|
||||
"Unable to create xml for module {loc}. "
|
||||
"Context: '{context}'"
|
||||
).format(
|
||||
context=lines[line - 1][offset - 40:offset + 40],
|
||||
loc=self.location,
|
||||
@@ -92,9 +92,9 @@ class RawMixin(object):
|
||||
node.remove(child)
|
||||
# Get attributes, if any, via normal parse_xml.
|
||||
try:
|
||||
block = super(RawMixin, cls).parse_xml_new_runtime(node, runtime, keys)
|
||||
block = super().parse_xml_new_runtime(node, runtime, keys)
|
||||
except AttributeError:
|
||||
block = super(RawMixin, cls).parse_xml(node, runtime, keys, id_generator=None)
|
||||
block = super().parse_xml(node, runtime, keys, id_generator=None)
|
||||
block.data = data_field_value
|
||||
return block
|
||||
|
||||
@@ -107,7 +107,7 @@ class RawDescriptor(RawMixin, XmlDescriptor, XMLEditingDescriptor):
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
|
||||
class EmptyDataRawMixin(object):
|
||||
class EmptyDataRawMixin:
|
||||
"""
|
||||
Common code between EmptyDataRawDescriptor and XBlocks converted from XModules.
|
||||
"""
|
||||
|
||||
@@ -11,12 +11,10 @@ import logging
|
||||
from datetime import datetime
|
||||
from functools import reduce
|
||||
|
||||
import six
|
||||
from lxml import etree
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from pkg_resources import resource_string
|
||||
from pytz import UTC
|
||||
from six import text_type
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.completable import XBlockCompletionMode
|
||||
from xblock.core import XBlock
|
||||
@@ -61,12 +59,12 @@ _ = lambda text: text
|
||||
|
||||
TIMED_EXAM_GATING_WAFFLE_FLAG = LegacyWaffleFlag(
|
||||
waffle_namespace="xmodule",
|
||||
flag_name=u'rev_1377_rollout',
|
||||
flag_name='rev_1377_rollout',
|
||||
module_name=__name__,
|
||||
)
|
||||
|
||||
|
||||
class SequenceFields(object): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
class SequenceFields: # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
has_children = True
|
||||
completion_mode = XBlockCompletionMode.AGGREGATOR
|
||||
|
||||
@@ -138,7 +136,7 @@ class SequenceMixin(SequenceFields):
|
||||
return xblock_body
|
||||
|
||||
|
||||
class ProctoringFields(object):
|
||||
class ProctoringFields:
|
||||
"""
|
||||
Fields that are specific to Proctored or Timed Exams
|
||||
"""
|
||||
@@ -315,7 +313,7 @@ class SequenceBlock(
|
||||
if dispatch == 'goto_position':
|
||||
# set position to default value if either 'position' argument not
|
||||
# found in request or it is a non-positive integer
|
||||
position = data.get('position', u'1')
|
||||
position = data.get('position', '1')
|
||||
if position.isdigit() and int(position) > 0:
|
||||
self.position = int(position)
|
||||
else:
|
||||
@@ -516,7 +514,7 @@ class SequenceBlock(
|
||||
params = {
|
||||
'items': items,
|
||||
'element_id': self.location.html_id(),
|
||||
'item_id': text_type(self.location),
|
||||
'item_id': str(self.location),
|
||||
'is_time_limited': self.is_time_limited,
|
||||
'position': self.position,
|
||||
'tag': self.location.block_type,
|
||||
@@ -742,7 +740,7 @@ class SequenceBlock(
|
||||
'content': content,
|
||||
'page_title': getattr(item, 'tooltip_title', ''),
|
||||
'type': item_type,
|
||||
'id': text_type(usage_id),
|
||||
'id': str(usage_id),
|
||||
'bookmarked': is_bookmarked,
|
||||
'path': " > ".join(display_names + [item.display_name_with_default]),
|
||||
'graded': item.graded,
|
||||
@@ -783,7 +781,7 @@ class SequenceBlock(
|
||||
"""
|
||||
if not newrelic:
|
||||
return
|
||||
newrelic.agent.add_custom_parameter('seq.block_id', six.text_type(self.location))
|
||||
newrelic.agent.add_custom_parameter('seq.block_id', str(self.location))
|
||||
newrelic.agent.add_custom_parameter('seq.display_name', self.display_name or '')
|
||||
newrelic.agent.add_custom_parameter('seq.position', self.position)
|
||||
newrelic.agent.add_custom_parameter('seq.is_time_limited', self.is_time_limited)
|
||||
@@ -808,7 +806,7 @@ class SequenceBlock(
|
||||
# Count of all modules by block_type (e.g. "video": 2, "discussion": 4)
|
||||
block_counts = collections.Counter(usage_key.block_type for usage_key in all_item_keys)
|
||||
for block_type, count in block_counts.items():
|
||||
newrelic.agent.add_custom_parameter('seq.block_counts.{}'.format(block_type), count)
|
||||
newrelic.agent.add_custom_parameter(f'seq.block_counts.{block_type}', count)
|
||||
|
||||
def _capture_current_unit_metrics(self, display_items):
|
||||
"""
|
||||
@@ -822,7 +820,7 @@ class SequenceBlock(
|
||||
if 1 <= self.position <= len(display_items):
|
||||
# Basic info about the Unit...
|
||||
current = display_items[self.position - 1]
|
||||
newrelic.agent.add_custom_parameter('seq.current.block_id', six.text_type(current.location))
|
||||
newrelic.agent.add_custom_parameter('seq.current.block_id', str(current.location))
|
||||
newrelic.agent.add_custom_parameter('seq.current.display_name', current.display_name or '')
|
||||
|
||||
# Examining all items inside the Unit (or split_test, conditional, etc.)
|
||||
@@ -830,7 +828,7 @@ class SequenceBlock(
|
||||
newrelic.agent.add_custom_parameter('seq.current.num_items', len(child_locs))
|
||||
curr_block_counts = collections.Counter(usage_key.block_type for usage_key in child_locs)
|
||||
for block_type, count in curr_block_counts.items():
|
||||
newrelic.agent.add_custom_parameter('seq.current.block_counts.{}'.format(block_type), count)
|
||||
newrelic.agent.add_custom_parameter(f'seq.current.block_counts.{block_type}', count)
|
||||
|
||||
def _time_limited_student_view(self):
|
||||
"""
|
||||
@@ -906,8 +904,8 @@ class SequenceBlock(
|
||||
return view_html
|
||||
|
||||
def get_icon_class(self):
|
||||
child_classes = set(child.get_icon_class()
|
||||
for child in self.get_children())
|
||||
child_classes = {child.get_icon_class()
|
||||
for child in self.get_children()}
|
||||
new_class = 'other'
|
||||
for c in class_priority:
|
||||
if c in child_classes:
|
||||
@@ -930,7 +928,7 @@ class SequenceBlock(
|
||||
return non_editable_fields
|
||||
|
||||
|
||||
class HighlightsFields(object):
|
||||
class HighlightsFields:
|
||||
"""Only Sections have summaries now, but we may expand that later."""
|
||||
highlights = List(
|
||||
help=_("A list summarizing what students should look forward to in this section."),
|
||||
|
||||
@@ -10,7 +10,7 @@ from django.conf import settings
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
|
||||
class SettingsService(object):
|
||||
class SettingsService:
|
||||
"""
|
||||
Allows server-wide configuration of XBlocks on a per-type basis
|
||||
|
||||
@@ -61,7 +61,7 @@ class SettingsService(object):
|
||||
def get_settings_bucket(self, block, default=None):
|
||||
""" Gets xblock settings dictionary from settings. """
|
||||
if not block:
|
||||
raise ValueError("Expected XBlock instance, got {0} of type {1}".format(block, type(block)))
|
||||
raise ValueError("Expected XBlock instance, got {} of type {}".format(block, type(block)))
|
||||
|
||||
actual_default = default if default is not None else {}
|
||||
xblock_settings_bucket = getattr(block, self.xblock_settings_bucket_selector, block.unmixed_class.__name__)
|
||||
@@ -71,7 +71,7 @@ class SettingsService(object):
|
||||
|
||||
# TODO: ConfigurationService and its usage will be removed as a part of EDUCATOR-121
|
||||
# reference: https://openedx.atlassian.net/browse/EDUCATOR-121
|
||||
class ConfigurationService(object):
|
||||
class ConfigurationService:
|
||||
"""
|
||||
An XBlock service to talk with the Configuration Models. This service should provide
|
||||
a pathway to Configuration Model which is designed to configure the corresponding XBlock.
|
||||
@@ -89,7 +89,7 @@ class ConfigurationService(object):
|
||||
"""
|
||||
if not (inspect.isclass(configuration_model) and issubclass(configuration_model, ConfigurationModel)):
|
||||
raise ValueError(
|
||||
"Expected ConfigurationModel got {0} of type {1}".format(
|
||||
"Expected ConfigurationModel got {} of type {}".format(
|
||||
configuration_model,
|
||||
type(configuration_model)
|
||||
)
|
||||
@@ -98,7 +98,7 @@ class ConfigurationService(object):
|
||||
self.configuration = configuration_model
|
||||
|
||||
|
||||
class TeamsConfigurationService(object):
|
||||
class TeamsConfigurationService:
|
||||
"""
|
||||
An XBlock service that returns the teams_configuration object for a course.
|
||||
"""
|
||||
|
||||
@@ -10,11 +10,9 @@ from functools import reduce
|
||||
from operator import itemgetter
|
||||
from uuid import uuid4
|
||||
|
||||
import six
|
||||
from django.utils.functional import cached_property
|
||||
from lxml import etree
|
||||
from pkg_resources import resource_string
|
||||
from six import text_type
|
||||
from web_fragments.fragment import Fragment
|
||||
from webob import Response
|
||||
from xblock.core import XBlock
|
||||
@@ -43,7 +41,7 @@ log = logging.getLogger('edx.' + __name__)
|
||||
# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file
|
||||
_ = lambda text: text
|
||||
|
||||
DEFAULT_GROUP_NAME = _(u'Group ID {group_id}')
|
||||
DEFAULT_GROUP_NAME = _('Group ID {group_id}')
|
||||
|
||||
|
||||
class UserPartitionValues(threading.local):
|
||||
@@ -75,7 +73,7 @@ class UserPartitionValues(threading.local):
|
||||
user_partition_values = UserPartitionValues()
|
||||
|
||||
|
||||
class SplitTestFields(object):
|
||||
class SplitTestFields:
|
||||
"""Fields needed for split test module"""
|
||||
has_children = True
|
||||
|
||||
@@ -284,8 +282,8 @@ class SplitTestBlock(
|
||||
group_name = child.display_name
|
||||
updated_group_id = [g_id for g_id, loc in self.group_id_to_child.items() if loc == child_location][0]
|
||||
inactive_contents.append({
|
||||
'group_name': _(u'{group_name} (inactive)').format(group_name=group_name),
|
||||
'id': text_type(child.location),
|
||||
'group_name': _('{group_name} (inactive)').format(group_name=group_name),
|
||||
'id': str(child.location),
|
||||
'content': rendered_child.content,
|
||||
'group_id': updated_group_id,
|
||||
})
|
||||
@@ -293,7 +291,7 @@ class SplitTestBlock(
|
||||
|
||||
active_contents.append({
|
||||
'group_name': group_name,
|
||||
'id': text_type(child.location),
|
||||
'id': str(child.location),
|
||||
'content': rendered_child.content,
|
||||
'group_id': updated_group_id,
|
||||
})
|
||||
@@ -382,7 +380,7 @@ class SplitTestBlock(
|
||||
"""
|
||||
if self.child is None:
|
||||
# raise error instead? In fact, could complain on descriptor load...
|
||||
return Fragment(content=u"<div>Nothing here. Move along.</div>")
|
||||
return Fragment(content="<div>Nothing here. Move along.</div>")
|
||||
|
||||
if self.system.user_is_staff:
|
||||
return self._staff_view(context)
|
||||
@@ -404,11 +402,11 @@ class SplitTestBlock(
|
||||
"""
|
||||
# TODO: use publish instead, when publish is wired to the tracking logs
|
||||
try:
|
||||
child_id = text_type(self.child.scope_ids.usage_id)
|
||||
child_id = str(self.child.scope_ids.usage_id)
|
||||
except Exception:
|
||||
log.info(
|
||||
"Can't get usage_id of Nonetype object in course {course_key}".format(
|
||||
course_key=six.text_type(self.location.course_key)
|
||||
course_key=str(self.location.course_key)
|
||||
)
|
||||
)
|
||||
raise
|
||||
@@ -432,7 +430,7 @@ class SplitTestBlock(
|
||||
user_partition = self.get_selected_partition()
|
||||
if user_partition:
|
||||
for group in user_partition.groups:
|
||||
group_id = six.text_type(group.id)
|
||||
group_id = str(group.id)
|
||||
child_location = self.group_id_to_child.get(group_id, None)
|
||||
if child_location == vertical.location:
|
||||
return (group.name, group.id)
|
||||
@@ -447,7 +445,7 @@ class SplitTestBlock(
|
||||
renderable_groups = {}
|
||||
# json.dumps doesn't know how to handle Location objects
|
||||
for group in self.group_id_to_child:
|
||||
renderable_groups[group] = text_type(self.group_id_to_child[group])
|
||||
renderable_groups[group] = str(self.group_id_to_child[group])
|
||||
xml_object.set('group_id_to_child', json.dumps(renderable_groups))
|
||||
xml_object.set('user_partition_id', str(self.user_partition_id))
|
||||
for child in self.get_children():
|
||||
@@ -577,7 +575,7 @@ class SplitTestBlock(
|
||||
# Compute the active children in the order specified by the user partition
|
||||
active_children = []
|
||||
for group in user_partition.groups:
|
||||
group_id = six.text_type(group.id)
|
||||
group_id = str(group.id)
|
||||
child_location = self.group_id_to_child.get(group_id, None)
|
||||
child = get_child_descriptor(child_location)
|
||||
if child:
|
||||
@@ -626,9 +624,9 @@ class SplitTestBlock(
|
||||
split_validation.add(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.NOT_CONFIGURED,
|
||||
_(u"The experiment is not associated with a group configuration."),
|
||||
_("The experiment is not associated with a group configuration."),
|
||||
action_class='edit-button',
|
||||
action_label=_(u"Select a Group Configuration")
|
||||
action_label=_("Select a Group Configuration")
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -637,7 +635,7 @@ class SplitTestBlock(
|
||||
split_validation.add(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.ERROR,
|
||||
_(u"The experiment uses a deleted group configuration. Select a valid group configuration or delete this experiment.") # lint-amnesty, pylint: disable=line-too-long
|
||||
_("The experiment uses a deleted group configuration. Select a valid group configuration or delete this experiment.") # lint-amnesty, pylint: disable=line-too-long
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -647,8 +645,8 @@ class SplitTestBlock(
|
||||
split_validation.add(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.ERROR,
|
||||
_(u"The experiment uses a group configuration that is not supported for experiments. "
|
||||
u"Select a valid group configuration or delete this experiment.")
|
||||
_("The experiment uses a group configuration that is not supported for experiments. "
|
||||
"Select a valid group configuration or delete this experiment.")
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -657,17 +655,17 @@ class SplitTestBlock(
|
||||
split_validation.add(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.ERROR,
|
||||
_(u"The experiment does not contain all of the groups in the configuration."),
|
||||
_("The experiment does not contain all of the groups in the configuration."),
|
||||
action_runtime_event='add-missing-groups',
|
||||
action_label=_(u"Add Missing Groups")
|
||||
action_label=_("Add Missing Groups")
|
||||
)
|
||||
)
|
||||
if len(inactive_children) > 0:
|
||||
split_validation.add(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.WARNING,
|
||||
_(u"The experiment has an inactive group. "
|
||||
u"Move content into active groups, then delete the inactive group.")
|
||||
_("The experiment has an inactive group. "
|
||||
"Move content into active groups, then delete the inactive group.")
|
||||
)
|
||||
)
|
||||
return split_validation
|
||||
@@ -685,7 +683,7 @@ class SplitTestBlock(
|
||||
has_error = any(message.type == StudioValidationMessage.ERROR for message in validation.messages)
|
||||
return StudioValidationMessage(
|
||||
StudioValidationMessage.ERROR if has_error else StudioValidationMessage.WARNING,
|
||||
_(u"This content experiment has issues that affect content visibility.")
|
||||
_("This content experiment has issues that affect content visibility.")
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -700,7 +698,7 @@ class SplitTestBlock(
|
||||
|
||||
changed = False
|
||||
for group in user_partition.groups:
|
||||
str_group_id = six.text_type(group.id)
|
||||
str_group_id = str(group.id)
|
||||
if str_group_id not in self.group_id_to_child:
|
||||
user_id = self.runtime.service(self, 'user').get_current_user().opt_attrs['edx-platform.user_id']
|
||||
self._create_vertical_for_group(group, user_id)
|
||||
@@ -722,7 +720,7 @@ class SplitTestBlock(
|
||||
user_partition = self.get_selected_partition()
|
||||
if user_partition:
|
||||
group_configuration_url = "{url}#{configuration_id}".format(
|
||||
url='/group_configurations/' + six.text_type(self.location.course_key),
|
||||
url='/group_configurations/' + str(self.location.course_key),
|
||||
configuration_id=str(user_partition.id)
|
||||
)
|
||||
|
||||
@@ -751,4 +749,4 @@ class SplitTestBlock(
|
||||
runtime=self.system,
|
||||
)
|
||||
self.children.append(dest_usage_key) # pylint: disable=no-member
|
||||
self.group_id_to_child[six.text_type(group.id)] = dest_usage_key
|
||||
self.group_id_to_child[str(group.id)] = dest_usage_key
|
||||
|
||||
@@ -16,7 +16,6 @@ from collections import defaultdict
|
||||
from pkg_resources import resource_string
|
||||
|
||||
import django
|
||||
import six
|
||||
from docopt import docopt
|
||||
from path import Path as path
|
||||
|
||||
@@ -179,7 +178,7 @@ def _write_styles(selector, output_root, classes, css_attribute):
|
||||
module_styles_lines.append("""{selector}.xmodule_{class_} {{""".format(
|
||||
class_=class_, selector=selector
|
||||
))
|
||||
module_styles_lines.extend(' @import "{0}";'.format(name) for name in fragment_names)
|
||||
module_styles_lines.extend(f' @import "{name}";' for name in fragment_names)
|
||||
module_styles_lines.append('}')
|
||||
|
||||
contents['_module-styles.scss'] = '\n'.join(module_styles_lines)
|
||||
@@ -236,7 +235,7 @@ def _write_files(output_root, contents, generated_suffix_map=None):
|
||||
will be ignored
|
||||
"""
|
||||
_ensure_dir(output_root)
|
||||
to_delete = set(file.basename() for file in output_root.files()) - set(contents.keys())
|
||||
to_delete = {file.basename() for file in output_root.files()} - set(contents.keys())
|
||||
|
||||
if generated_suffix_map:
|
||||
for output_file in contents.keys():
|
||||
@@ -247,7 +246,7 @@ def _write_files(output_root, contents, generated_suffix_map=None):
|
||||
for extra_file in to_delete:
|
||||
(output_root / extra_file).remove_p()
|
||||
|
||||
for filename, file_content in six.iteritems(contents):
|
||||
for filename, file_content in contents.items():
|
||||
output_file = output_root / filename
|
||||
|
||||
not_file = not output_file.isfile()
|
||||
@@ -255,7 +254,7 @@ def _write_files(output_root, contents, generated_suffix_map=None):
|
||||
# Sometimes content is already unicode and sometimes it's not
|
||||
# so we add this conditional here to make sure that below we're
|
||||
# always working with streams of bytes.
|
||||
if not isinstance(file_content, six.binary_type):
|
||||
if not isinstance(file_content, bytes):
|
||||
file_content = file_content.encode('utf-8')
|
||||
|
||||
# not_file is included to short-circuit this check, because
|
||||
@@ -280,7 +279,7 @@ def write_webpack(output_file, module_files, descriptor_files):
|
||||
'entry': {}
|
||||
}
|
||||
for (owner, files) in list(module_files.items()) + list(descriptor_files.items()):
|
||||
unique_files = sorted(set('./{}'.format(file) for file in files))
|
||||
unique_files = sorted({f'./{file}' for file in files})
|
||||
if len(unique_files) == 1:
|
||||
unique_files = unique_files[0]
|
||||
config['entry'][owner] = unique_files
|
||||
@@ -289,7 +288,7 @@ def write_webpack(output_file, module_files, descriptor_files):
|
||||
|
||||
with output_file.open('w') as outfile:
|
||||
outfile.write(
|
||||
textwrap.dedent(u"""\
|
||||
textwrap.dedent("""\
|
||||
module.exports = {config_json};
|
||||
""").format(
|
||||
config_json=json.dumps(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# lint-amnesty, pylint: disable=missing-module-docstring
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from lxml import etree
|
||||
|
||||
@@ -28,4 +27,4 @@ def stringify_children(node):
|
||||
parts.append(etree.tostring(c, with_tail=True, encoding='unicode'))
|
||||
|
||||
# filter removes possible Nones in texts and tails
|
||||
return u''.join([part for part in parts if part])
|
||||
return ''.join([part for part in parts if part])
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"""
|
||||
Mixin to support editing in Studio.
|
||||
"""
|
||||
|
||||
|
||||
import six
|
||||
from xmodule.x_module import AUTHOR_VIEW, STUDENT_VIEW, module_attr
|
||||
|
||||
|
||||
class StudioEditableBlock(object):
|
||||
class StudioEditableBlock:
|
||||
"""
|
||||
Helper methods for supporting Studio editing of XBlocks.
|
||||
|
||||
@@ -30,7 +27,7 @@ class StudioEditableBlock(object):
|
||||
fragment.add_fragment_resources(rendered_child)
|
||||
|
||||
contents.append({
|
||||
'id': six.text_type(child.location),
|
||||
'id': str(child.location),
|
||||
'content': rendered_child.content
|
||||
})
|
||||
|
||||
@@ -52,7 +49,7 @@ class StudioEditableBlock(object):
|
||||
StudioEditableModule = StudioEditableBlock
|
||||
|
||||
|
||||
class StudioEditableDescriptor(object):
|
||||
class StudioEditableDescriptor:
|
||||
"""
|
||||
Helper mixin for supporting Studio editing of xmodules.
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ Implement CourseTab
|
||||
import logging
|
||||
from abc import ABCMeta
|
||||
|
||||
import six
|
||||
from django.core.files.storage import get_storage_class
|
||||
from six import text_type
|
||||
from xblock.fields import List
|
||||
|
||||
from edx_django_utils.plugins import PluginError
|
||||
@@ -23,7 +21,7 @@ _ = lambda text: text
|
||||
READ_ONLY_COURSE_TAB_ATTRIBUTES = ['type']
|
||||
|
||||
|
||||
class CourseTab(six.with_metaclass(ABCMeta, object)):
|
||||
class CourseTab(metaclass=ABCMeta):
|
||||
"""
|
||||
The Course Tab class is a data abstraction for all tabs (i.e., course navigation links) within a course.
|
||||
It is an abstract class - to be inherited by various tab types.
|
||||
@@ -85,7 +83,7 @@ class CourseTab(six.with_metaclass(ABCMeta, object)):
|
||||
Args:
|
||||
tab_dict (dict) - a dictionary of parameters used to build the tab.
|
||||
"""
|
||||
super(CourseTab, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
self.name = tab_dict.get('name', self.title)
|
||||
self.tab_id = tab_dict.get('tab_id', getattr(self, 'tab_id', self.type))
|
||||
self.course_staff_only = tab_dict.get('course_staff_only', False)
|
||||
@@ -129,7 +127,7 @@ class CourseTab(six.with_metaclass(ABCMeta, object)):
|
||||
if hasattr(self, key):
|
||||
return getattr(self, key, None)
|
||||
else:
|
||||
raise KeyError('Key {0} not present in tab {1}'.format(key, self.to_json()))
|
||||
raise KeyError(f'Key {key} not present in tab {self.to_json()}')
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""
|
||||
@@ -141,7 +139,7 @@ class CourseTab(six.with_metaclass(ABCMeta, object)):
|
||||
if hasattr(self, key) and key not in READ_ONLY_COURSE_TAB_ATTRIBUTES:
|
||||
setattr(self, key, value)
|
||||
else:
|
||||
raise KeyError('Key {0} cannot be set in tab {1}'.format(key, self.to_json()))
|
||||
raise KeyError(f'Key {key} cannot be set in tab {self.to_json()}')
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
@@ -245,14 +243,14 @@ class CourseTab(six.with_metaclass(ABCMeta, object)):
|
||||
return tab_type(tab_dict=tab_dict)
|
||||
|
||||
|
||||
class TabFragmentViewMixin(object):
|
||||
class TabFragmentViewMixin:
|
||||
"""
|
||||
A mixin for tabs that render themselves as web fragments.
|
||||
"""
|
||||
fragment_view_name = None
|
||||
|
||||
def __init__(self, tab_dict):
|
||||
super(TabFragmentViewMixin, self).__init__(tab_dict) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(tab_dict)
|
||||
self._fragment_view = None
|
||||
|
||||
@property
|
||||
@@ -261,12 +259,12 @@ class TabFragmentViewMixin(object):
|
||||
|
||||
# If a view_name is specified, then use the default link function
|
||||
if self.view_name:
|
||||
return super(TabFragmentViewMixin, self).link_func # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().link_func
|
||||
|
||||
# If not, then use the generic course tab URL
|
||||
def link_func(course, reverse_func):
|
||||
""" Returns a function that returns the course tab's URL. """
|
||||
return reverse_func("course_tab_view", args=[text_type(course.id), self.type])
|
||||
return reverse_func("course_tab_view", args=[str(course.id), self.type])
|
||||
|
||||
return link_func
|
||||
|
||||
@@ -290,7 +288,7 @@ class TabFragmentViewMixin(object):
|
||||
"""
|
||||
Renders this tab to a web fragment.
|
||||
"""
|
||||
return self.fragment_view.render_to_fragment(request, course_id=six.text_type(course.id), **kwargs)
|
||||
return self.fragment_view.render_to_fragment(request, course_id=str(course.id), **kwargs)
|
||||
|
||||
def __hash__(self):
|
||||
""" Return a hash representation of Tab Object. """
|
||||
@@ -308,7 +306,7 @@ class StaticTab(CourseTab):
|
||||
def __init__(self, tab_dict=None, name=None, url_slug=None):
|
||||
def link_func(course, reverse_func):
|
||||
""" Returns a function that returns the static tab's URL. """
|
||||
return reverse_func(self.type, args=[text_type(course.id), self.url_slug])
|
||||
return reverse_func(self.type, args=[str(course.id), self.url_slug])
|
||||
|
||||
self.url_slug = tab_dict.get('url_slug') if tab_dict else url_slug
|
||||
|
||||
@@ -319,9 +317,9 @@ class StaticTab(CourseTab):
|
||||
tab_dict['name'] = name
|
||||
|
||||
tab_dict['link_func'] = link_func
|
||||
tab_dict['tab_id'] = 'static_tab_{0}'.format(self.url_slug)
|
||||
tab_dict['tab_id'] = f'static_tab_{self.url_slug}'
|
||||
|
||||
super(StaticTab, self).__init__(tab_dict) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(tab_dict)
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, course, user=None):
|
||||
@@ -335,29 +333,29 @@ class StaticTab(CourseTab):
|
||||
"""
|
||||
Ensures that the specified tab_dict is valid.
|
||||
"""
|
||||
return (super(StaticTab, cls).validate(tab_dict, raise_error)
|
||||
return (super().validate(tab_dict, raise_error)
|
||||
and key_checker(['name', 'url_slug'])(tab_dict, raise_error))
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == 'url_slug':
|
||||
return self.url_slug
|
||||
else:
|
||||
return super(StaticTab, self).__getitem__(key) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().__getitem__(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key == 'url_slug':
|
||||
self.url_slug = value
|
||||
else:
|
||||
super(StaticTab, self).__setitem__(key, value) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__setitem__(key, value)
|
||||
|
||||
def to_json(self):
|
||||
""" Return a dictionary representation of this tab. """
|
||||
to_json_val = super(StaticTab, self).to_json() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
to_json_val = super().to_json()
|
||||
to_json_val.update({'url_slug': self.url_slug})
|
||||
return to_json_val
|
||||
|
||||
def __eq__(self, other):
|
||||
if not super(StaticTab, self).__eq__(other): # lint-amnesty, pylint: disable=super-with-arguments
|
||||
if not super().__eq__(other):
|
||||
return False
|
||||
return self.url_slug == other.get('url_slug')
|
||||
|
||||
@@ -461,8 +459,7 @@ class CourseTabList(List):
|
||||
# If rendering inline that add each item in the collection,
|
||||
# else just show the tab itself as long as it is not empty.
|
||||
if inline_collections:
|
||||
for item in tab.items(course):
|
||||
yield item
|
||||
yield from tab.items(course)
|
||||
elif len(list(tab.items(course))) > 0:
|
||||
yield tab
|
||||
else:
|
||||
@@ -496,15 +493,15 @@ class CourseTabList(List):
|
||||
return
|
||||
|
||||
if len(tabs) < 2:
|
||||
raise InvalidTabsException("Expected at least two tabs. tabs: '{0}'".format(tabs))
|
||||
raise InvalidTabsException(f"Expected at least two tabs. tabs: '{tabs}'")
|
||||
|
||||
if tabs[0].get('type') != 'course_info':
|
||||
raise InvalidTabsException(
|
||||
"Expected first tab to have type 'course_info'. tabs: '{0}'".format(tabs))
|
||||
f"Expected first tab to have type 'course_info'. tabs: '{tabs}'")
|
||||
|
||||
if tabs[1].get('type') != 'courseware':
|
||||
raise InvalidTabsException(
|
||||
"Expected second tab to have type 'courseware'. tabs: '{0}'".format(tabs))
|
||||
f"Expected second tab to have type 'courseware'. tabs: '{tabs}'")
|
||||
|
||||
# the following tabs should appear only once
|
||||
# TODO: don't import openedx capabilities from common
|
||||
@@ -574,7 +571,7 @@ def key_checker(expected_keys):
|
||||
return True
|
||||
if raise_error: # lint-amnesty, pylint: disable=no-else-raise
|
||||
raise InvalidTabsException(
|
||||
"Expected keys '{0}' are not present in the given dict: {1}".format(expected_keys, actual_dict)
|
||||
f"Expected keys '{expected_keys}' are not present in the given dict: {actual_dict}"
|
||||
)
|
||||
else:
|
||||
return False
|
||||
@@ -620,7 +617,7 @@ def course_reverse_func_from_name_func(reverse_name_func):
|
||||
"""
|
||||
return lambda course, reverse_url_func: reverse_url_func(
|
||||
reverse_name_func(course),
|
||||
args=[text_type(course.id)]
|
||||
args=[str(course.id)]
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class CustomTagBlock(
|
||||
template_name = child_impl.text
|
||||
else:
|
||||
# TODO (vshnayder): better exception type
|
||||
raise Exception("Could not find impl attribute in customtag {0}"
|
||||
raise Exception("Could not find impl attribute in customtag {}"
|
||||
.format(self.location))
|
||||
|
||||
params = dict(list(xmltree.items()))
|
||||
|
||||
@@ -17,14 +17,12 @@ import traceback
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from functools import wraps
|
||||
from unittest.mock import Mock
|
||||
|
||||
import six
|
||||
from django.test import TestCase
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from path import Path as path
|
||||
from six import text_type
|
||||
from xblock.core import XBlock
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import Reference, ReferenceList, ReferenceValueDict, ScopeIds
|
||||
@@ -53,11 +51,11 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
|
||||
kwargs.setdefault('id_reader', id_manager)
|
||||
kwargs.setdefault('id_generator', id_manager)
|
||||
kwargs.setdefault('services', {}).setdefault('field-data', DictFieldData({}))
|
||||
super(TestModuleSystem, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def handler_url(self, block, handler, suffix='', query='', thirdparty=False): # lint-amnesty, pylint: disable=arguments-differ
|
||||
return '{usage_id}/{handler}{suffix}?{query}'.format(
|
||||
usage_id=six.text_type(block.scope_ids.usage_id),
|
||||
usage_id=str(block.scope_ids.usage_id),
|
||||
handler=handler,
|
||||
suffix=suffix,
|
||||
query=query,
|
||||
@@ -65,7 +63,7 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
|
||||
|
||||
def local_resource_url(self, block, uri):
|
||||
return 'resource/{usage_id}/{uri}'.format(
|
||||
usage_id=six.text_type(block.scope_ids.usage_id),
|
||||
usage_id=str(block.scope_ids.usage_id),
|
||||
uri=uri,
|
||||
)
|
||||
|
||||
@@ -84,7 +82,7 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
|
||||
if hasattr(self, '_view_name'):
|
||||
orig_view_name = self._view_name
|
||||
self._view_name = None
|
||||
rt_repr = super(TestModuleSystem, self).__repr__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
rt_repr = super().__repr__()
|
||||
self._view_name = orig_view_name
|
||||
return rt_repr
|
||||
|
||||
@@ -202,12 +200,12 @@ def map_references(value, field, actual_course_key):
|
||||
if isinstance(field, ReferenceList):
|
||||
return [sub.map_into_course(actual_course_key) for sub in value]
|
||||
if isinstance(field, ReferenceValueDict):
|
||||
return {key: ele.map_into_course(actual_course_key) for key, ele in six.iteritems(value)}
|
||||
return {key: ele.map_into_course(actual_course_key) for key, ele in value.items()}
|
||||
return value
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class LazyFormat(object):
|
||||
class LazyFormat:
|
||||
"""
|
||||
An stringy object that delays formatting until it's put into a string context.
|
||||
"""
|
||||
@@ -225,13 +223,13 @@ class LazyFormat(object):
|
||||
return self._message
|
||||
|
||||
def __repr__(self):
|
||||
return six.text_type(self)
|
||||
return str(self)
|
||||
|
||||
def __len__(self):
|
||||
return len(six.text_type(self))
|
||||
return len(str(self))
|
||||
|
||||
def __getitem__(self, index):
|
||||
return six.text_type(self)[index]
|
||||
return str(self)[index]
|
||||
|
||||
|
||||
class CourseComparisonTest(TestCase):
|
||||
@@ -240,7 +238,7 @@ class CourseComparisonTest(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(CourseComparisonTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.field_exclusions = set()
|
||||
self.ignored_asset_keys = set()
|
||||
|
||||
@@ -283,8 +281,8 @@ class CourseComparisonTest(TestCase):
|
||||
expected = [extract_key(key) for key in expected]
|
||||
actual = [extract_key(key) for key in actual]
|
||||
elif isinstance(reference_field, ReferenceValueDict):
|
||||
expected = {key: extract_key(val) for (key, val) in six.iteritems(expected)}
|
||||
actual = {key: extract_key(val) for (key, val) in six.iteritems(actual)}
|
||||
expected = {key: extract_key(val) for (key, val) in expected.items()}
|
||||
actual = {key: extract_key(val) for (key, val) in actual.items()}
|
||||
assert expected == actual,\
|
||||
LazyFormat("Field {} doesn't match between usages {} and {}: {!r} != {!r}",
|
||||
reference_field.name,
|
||||
@@ -365,8 +363,7 @@ class CourseComparisonTest(TestCase):
|
||||
}
|
||||
# Split Mongo and Old-Mongo disagree about what the block_id of courses is, so skip those in
|
||||
# this comparison
|
||||
six.assertCountEqual(
|
||||
self,
|
||||
self.assertCountEqual(
|
||||
[map_key(item.location) for item in expected_items if item.scope_ids.block_type != 'course'],
|
||||
[key for key in actual_item_map.keys() if key[0] != 'course'],
|
||||
)
|
||||
@@ -387,7 +384,7 @@ class CourseComparisonTest(TestCase):
|
||||
continue
|
||||
# compare fields
|
||||
assert expected_item.fields == actual_item.fields
|
||||
for field_name, field in six.iteritems(expected_item.fields):
|
||||
for field_name, field in expected_item.fields.items():
|
||||
if (expected_item.scope_ids.usage_id, field_name) in self.field_exclusions:
|
||||
continue
|
||||
if (None, field_name) in self.field_exclusions:
|
||||
@@ -428,8 +425,8 @@ class CourseComparisonTest(TestCase):
|
||||
|
||||
expected_filename = expected_asset.pop('filename')
|
||||
actual_filename = actual_asset.pop('filename')
|
||||
assert text_type(expected_key) == expected_filename
|
||||
assert text_type(actual_key) == actual_filename
|
||||
assert str(expected_key) == expected_filename
|
||||
assert str(actual_key) == actual_filename
|
||||
assert expected_asset == actual_asset
|
||||
|
||||
def _assertAssetsEqual(self, expected_course_key, expected_assets, actual_course_key, actual_assets): # pylint: disable=invalid-name
|
||||
|
||||
@@ -36,7 +36,7 @@ class StubUserService(UserService):
|
||||
|
||||
def __init__(self, is_anonymous=False, **kwargs):
|
||||
self.is_anonymous = is_anonymous
|
||||
super(StubUserService, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def get_current_user(self):
|
||||
"""
|
||||
|
||||
@@ -68,7 +68,7 @@ class AnnotatableBlockTestCase(unittest.TestCase): # lint-amnesty, pylint: disa
|
||||
|
||||
for color in self.annotatable.HIGHLIGHT_COLORS:
|
||||
el = etree.fromstring(xml.format(highlight=color))
|
||||
value = 'annotatable-span highlight highlight-{highlight}'.format(highlight=color)
|
||||
value = f'annotatable-span highlight highlight-{color}'
|
||||
|
||||
expected_attr = {
|
||||
'class': {
|
||||
@@ -126,7 +126,7 @@ class AnnotatableBlockTestCase(unittest.TestCase): # lint-amnesty, pylint: disa
|
||||
def test_extract_instructions(self):
|
||||
xmltree = etree.fromstring(self.sample_xml)
|
||||
|
||||
expected_xml = u"<div>Read the text.</div>"
|
||||
expected_xml = "<div>Read the text.</div>"
|
||||
actual_xml = self.annotatable._extract_instructions(xmltree) # lint-amnesty, pylint: disable=protected-access
|
||||
assert actual_xml is not None
|
||||
assert expected_xml.strip() == actual_xml.strip()
|
||||
|
||||
@@ -30,7 +30,7 @@ class HelperFunctionTest(unittest.TestCase):
|
||||
"""
|
||||
xmltree = etree.fromstring(self.sample_xml)
|
||||
|
||||
expected_xml = u"<div><p>Helper Test Instructions.</p></div>"
|
||||
expected_xml = "<div><p>Helper Test Instructions.</p></div>"
|
||||
actual_xml = get_instructions(xmltree)
|
||||
assert actual_xml is not None
|
||||
assert expected_xml.strip() == actual_xml.strip()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests of the Capa XModule
|
||||
"""
|
||||
@@ -11,19 +10,17 @@ import os
|
||||
import random
|
||||
import textwrap
|
||||
import unittest
|
||||
from unittest.mock import DEFAULT, Mock, patch
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
import requests
|
||||
import six
|
||||
import webob
|
||||
from django.utils.encoding import smart_text
|
||||
from edx_user_state_client.interface import XBlockUserState
|
||||
from lxml import etree
|
||||
from mock import DEFAULT, Mock, patch
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from pytz import UTC
|
||||
from six.moves import range, zip
|
||||
from webob.multidict import MultiDict
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
@@ -41,7 +38,7 @@ from ..capa_base import RANDOMIZATION, SHOWANSWER
|
||||
from . import get_test_system
|
||||
|
||||
|
||||
class CapaFactory(object):
|
||||
class CapaFactory:
|
||||
"""
|
||||
A helper class to create problem modules with various parameters for testing.
|
||||
"""
|
||||
@@ -101,7 +98,7 @@ class CapaFactory(object):
|
||||
location = BlockUsageLocator(
|
||||
CourseLocator("edX", "capa_test", "2012_Fall", deprecated=True),
|
||||
"problem",
|
||||
"SampleProblem{0}".format(cls.next_num()),
|
||||
f"SampleProblem{cls.next_num()}",
|
||||
deprecated=True,
|
||||
)
|
||||
if xml is None:
|
||||
@@ -182,7 +179,7 @@ if submission[0] == '':
|
||||
class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
|
||||
|
||||
def setUp(self):
|
||||
super(ProblemBlockTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
now = datetime.datetime.now(UTC)
|
||||
day_delta = datetime.timedelta(days=1)
|
||||
@@ -861,8 +858,8 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
|
||||
assert xqueue_interface._http_post.call_count == 1
|
||||
_, kwargs = xqueue_interface._http_post.call_args
|
||||
six.assertCountEqual(self, fpaths, list(kwargs['files'].keys()))
|
||||
for fpath, fileobj in six.iteritems(kwargs['files']):
|
||||
self.assertCountEqual(fpaths, list(kwargs['files'].keys()))
|
||||
for fpath, fileobj in kwargs['files'].items():
|
||||
assert fpath == fileobj.name
|
||||
|
||||
def test_submit_problem_with_files_as_xblock(self):
|
||||
@@ -894,8 +891,8 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
|
||||
assert xqueue_interface._http_post.call_count == 1
|
||||
_, kwargs = xqueue_interface._http_post.call_args
|
||||
six.assertCountEqual(self, fnames, list(kwargs['files'].keys()))
|
||||
for fpath, fileobj in six.iteritems(kwargs['files']):
|
||||
self.assertCountEqual(fnames, list(kwargs['files'].keys()))
|
||||
for fpath, fileobj in kwargs['files'].items():
|
||||
assert fpath == fileobj.name
|
||||
|
||||
def test_submit_problem_error(self):
|
||||
@@ -947,7 +944,7 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
' File "<string>", line 65, in check_func\\n'
|
||||
'Exception: Couldn\'t execute jailed code\\n\' with status code: 1', )
|
||||
except ResponseError as err:
|
||||
mock_grade.side_effect = exception_class(six.text_type(err))
|
||||
mock_grade.side_effect = exception_class(str(err))
|
||||
get_request_dict = {CapaFactory.input_key(): '3.14'}
|
||||
result = module.submit_problem(get_request_dict)
|
||||
|
||||
@@ -974,7 +971,7 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
|
||||
# Simulate answering a problem that raises the exception
|
||||
with patch('capa.capa_problem.LoncapaProblem.grade_answers') as mock_grade:
|
||||
error_msg = u"Superterrible error happened: ☠"
|
||||
error_msg = "Superterrible error happened: ☠"
|
||||
mock_grade.side_effect = Exception(error_msg)
|
||||
|
||||
get_request_dict = {CapaFactory.input_key(): '3.14'}
|
||||
@@ -1009,13 +1006,13 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
|
||||
# Simulate answering a problem that raises the exception
|
||||
with patch('capa.capa_problem.LoncapaProblem.grade_answers') as mock_grade:
|
||||
mock_grade.side_effect = exception_class(u"ȧƈƈḗƞŧḗḓ ŧḗẋŧ ƒǿř ŧḗşŧīƞɠ")
|
||||
mock_grade.side_effect = exception_class("ȧƈƈḗƞŧḗḓ ŧḗẋŧ ƒǿř ŧḗşŧīƞɠ")
|
||||
|
||||
get_request_dict = {CapaFactory.input_key(): '3.14'}
|
||||
result = module.submit_problem(get_request_dict)
|
||||
|
||||
# Expect an AJAX alert message in 'success'
|
||||
expected_msg = u'ȧƈƈḗƞŧḗḓ ŧḗẋŧ ƒǿř ŧḗşŧīƞɠ'
|
||||
expected_msg = 'ȧƈƈḗƞŧḗḓ ŧḗẋŧ ƒǿř ŧḗşŧīƞɠ'
|
||||
|
||||
assert expected_msg == result['success']
|
||||
|
||||
@@ -1241,7 +1238,7 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
|
||||
# Simulate answering a problem that raises the exception
|
||||
with patch('capa.capa_problem.LoncapaProblem.get_grade_from_current_answers') as mock_rescore:
|
||||
mock_rescore.side_effect = exception_class(u'test error \u03a9')
|
||||
mock_rescore.side_effect = exception_class('test error \u03a9')
|
||||
with pytest.raises(exception_class):
|
||||
module.rescore(only_if_higher=False)
|
||||
|
||||
@@ -1710,7 +1707,7 @@ class ProblemBlockTest(unittest.TestCase): # lint-amnesty, pylint: disable=miss
|
||||
|
||||
# Simulate throwing an exception when the capa problem
|
||||
# is asked to render itself as HTML
|
||||
error_msg = u"Superterrible error happened: ☠"
|
||||
error_msg = "Superterrible error happened: ☠"
|
||||
module.lcp.get_html = Mock(side_effect=Exception(error_msg))
|
||||
|
||||
# Stub out the get_test_system rendering function
|
||||
@@ -2527,7 +2524,7 @@ class ProblemBlockXMLTest(unittest.TestCase): # lint-amnesty, pylint: disable=m
|
||||
self.assertDictEqual(
|
||||
indexing_result, {
|
||||
'content_type': ProblemBlock.INDEX_CONTENT_TYPE,
|
||||
'problem_types': set(["optionresponse", "multiplechoiceresponse"]),
|
||||
'problem_types': {"optionresponse", "multiplechoiceresponse"},
|
||||
'content': {
|
||||
'display_name': name,
|
||||
'capa_content': " Label Some comment Donut Buggy '1','2' "
|
||||
@@ -2569,7 +2566,7 @@ class ProblemBlockXMLTest(unittest.TestCase): # lint-amnesty, pylint: disable=m
|
||||
def test_indexing_checkboxes(self):
|
||||
name = "Checkboxes"
|
||||
descriptor = self._create_descriptor(self.sample_checkbox_problem_xml, name=name)
|
||||
capa_content = textwrap.dedent(u"""
|
||||
capa_content = textwrap.dedent("""
|
||||
Title
|
||||
Description
|
||||
Example
|
||||
@@ -2856,7 +2853,7 @@ class ProblemCheckTrackingTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(ProblemCheckTrackingTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.maxDiff = None
|
||||
|
||||
def test_choice_answer_text(self):
|
||||
@@ -2909,14 +2906,14 @@ class ProblemCheckTrackingTest(unittest.TestCase):
|
||||
'group_label': '',
|
||||
'variant': ''},
|
||||
factory.answer_key(3): {'question': 'Which piece of furniture is built for sitting?',
|
||||
'answer': u'<text>a table</text>',
|
||||
'answer': '<text>a table</text>',
|
||||
'response_type': 'multiplechoiceresponse',
|
||||
'input_type': 'choicegroup',
|
||||
'correct': False,
|
||||
'group_label': '',
|
||||
'variant': ''},
|
||||
factory.answer_key(4): {'question': 'Which of the following are musical instruments?',
|
||||
'answer': [u'a piano', u'a tree'],
|
||||
'answer': ['a piano', 'a tree'],
|
||||
'response_type': 'choiceresponse',
|
||||
'input_type': 'checkboxgroup',
|
||||
'correct': False,
|
||||
@@ -3160,14 +3157,14 @@ class ProblemBlockReportGenerationTest(unittest.TestCase):
|
||||
|
||||
def _mock_user_state_generator(self, user_count=1, response_count=10):
|
||||
for uid in range(user_count):
|
||||
yield self._user_state(username='user{}'.format(uid), response_count=response_count)
|
||||
yield self._user_state(username=f'user{uid}', response_count=response_count)
|
||||
|
||||
def _user_state(self, username='testuser', response_count=10, suffix=''):
|
||||
return XBlockUserState(
|
||||
username=username,
|
||||
state={
|
||||
'student_answers': {
|
||||
'{}_answerid_{}{}'.format(username, aid, suffix): '{}_answer_{}'.format(username, aid)
|
||||
f'{username}_answerid_{aid}{suffix}': f'{username}_answer_{aid}'
|
||||
for aid in range(response_count)
|
||||
},
|
||||
'seed': 1,
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
import json
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from fs.memoryfs import MemoryFS
|
||||
from lxml import etree
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from six import text_type
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
@@ -33,7 +32,7 @@ class DummySystem(ImportSystem): # lint-amnesty, pylint: disable=abstract-metho
|
||||
|
||||
xmlstore = XMLModuleStore("data_dir", source_dirs=[], load_error_modules=load_error_modules)
|
||||
|
||||
super(DummySystem, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(
|
||||
xmlstore=xmlstore,
|
||||
course_id=CourseKey.from_string('/'.join([ORG, COURSE, 'test_run'])),
|
||||
course_dir='test_dir',
|
||||
@@ -52,7 +51,7 @@ class ConditionalBlockFactory(xml.XmlImportFactory):
|
||||
tag = 'conditional'
|
||||
|
||||
|
||||
class ConditionalFactory(object):
|
||||
class ConditionalFactory:
|
||||
"""
|
||||
A helper class to create a conditional module and associated source and child modules
|
||||
to allow for testing.
|
||||
@@ -89,7 +88,7 @@ class ConditionalFactory(object):
|
||||
# construct other descriptors:
|
||||
child_descriptor = Mock(name='child_descriptor')
|
||||
child_descriptor.visible_to_staff_only = False
|
||||
child_descriptor._xmodule.student_view.return_value = Fragment(content=u'<p>This is a secret</p>') # lint-amnesty, pylint: disable=protected-access
|
||||
child_descriptor._xmodule.student_view.return_value = Fragment(content='<p>This is a secret</p>') # lint-amnesty, pylint: disable=protected-access
|
||||
child_descriptor.student_view = child_descriptor._xmodule.student_view # lint-amnesty, pylint: disable=protected-access
|
||||
child_descriptor.displayable_items.return_value = [child_descriptor]
|
||||
child_descriptor.runtime = descriptor_system
|
||||
@@ -149,7 +148,7 @@ class ConditionalBlockBasicTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(ConditionalBlockBasicTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_system = get_test_system()
|
||||
|
||||
def test_icon_class(self):
|
||||
@@ -168,8 +167,8 @@ class ConditionalBlockBasicTest(unittest.TestCase):
|
||||
html = modules['cond_module'].render(STUDENT_VIEW).content
|
||||
expected = modules['cond_module'].xmodule_runtime.render_template('conditional_ajax.html', {
|
||||
'ajax_url': modules['cond_module'].ajax_url,
|
||||
'element_id': u'i4x-edX-conditional_test-conditional-SampleConditional',
|
||||
'depends': u'i4x-edX-conditional_test-problem-SampleProblem',
|
||||
'element_id': 'i4x-edX-conditional_test-conditional-SampleConditional',
|
||||
'depends': 'i4x-edX-conditional_test-problem-SampleProblem',
|
||||
})
|
||||
assert expected == html
|
||||
|
||||
@@ -226,12 +225,12 @@ class ConditionalBlockXmlTest(unittest.TestCase):
|
||||
return DummySystem(load_error_modules)
|
||||
|
||||
def setUp(self):
|
||||
super(ConditionalBlockXmlTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.test_system = get_test_system()
|
||||
|
||||
def get_course(self, name):
|
||||
"""Get a test course by directory name. If there's more than one, error."""
|
||||
print("Importing {0}".format(name))
|
||||
print(f"Importing {name}")
|
||||
|
||||
modulestore = XMLModuleStore(DATA_DIR, source_dirs=[name])
|
||||
courses = modulestore.get_courses()
|
||||
@@ -280,9 +279,9 @@ class ConditionalBlockXmlTest(unittest.TestCase):
|
||||
'conditional_ajax.html',
|
||||
{
|
||||
# Test ajax url is just usage-id / handler_name
|
||||
'ajax_url': '{}/xmodule_handler'.format(text_type(location)),
|
||||
'element_id': u'i4x-HarvardX-ER22x-conditional-condone',
|
||||
'depends': u'i4x-HarvardX-ER22x-problem-choiceprob'
|
||||
'ajax_url': '{}/xmodule_handler'.format(str(location)),
|
||||
'element_id': 'i4x-HarvardX-ER22x-conditional-condone',
|
||||
'depends': 'i4x-HarvardX-ER22x-problem-choiceprob'
|
||||
}
|
||||
)
|
||||
assert html == html_expect
|
||||
@@ -443,7 +442,7 @@ class ConditionalBlockStudioTest(XModuleXmlImportTest):
|
||||
"""
|
||||
self.conditional.sources_list = None
|
||||
validation = self.conditional.validate()
|
||||
assert validation.summary.text == u'This component has no source components configured yet.'
|
||||
assert validation.summary.text == 'This component has no source components configured yet.'
|
||||
assert validation.summary.type == StudioValidationMessage.NOT_CONFIGURED
|
||||
assert validation.summary.action_class == 'edit-button'
|
||||
assert validation.summary.action_label == u'Configure list of sources'
|
||||
assert validation.summary.action_label == 'Configure list of sources'
|
||||
|
||||
Reference in New Issue
Block a user