refactor: pyupgrade in common/lib/xmodule (#26780)

This commit is contained in:
M. Zulqarnain
2021-03-10 17:56:07 +05:00
committed by GitHub
parent 8aec103447
commit deb7f5b026
49 changed files with 418 additions and 475 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -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]}
}, [])
]),
]

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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):
"""

View File

@@ -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()

View File

@@ -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})

View File

@@ -5,6 +5,7 @@ Tests for testing the modulestore settings migration code.
import copy
from unittest import TestCase
import pytest
import ddt

View File

@@ -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')

View File

@@ -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)

View File

@@ -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):
"""

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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")

View File

@@ -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 """

View File

@@ -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__()

View File

@@ -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):

View File

@@ -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'))

View File

@@ -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)

View File

@@ -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

View File

@@ -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(

View File

@@ -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)

View File

@@ -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):

View File

@@ -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):

View File

@@ -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)

View File

@@ -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.
"""

View File

@@ -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."),

View File

@@ -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.
"""

View File

@@ -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

View File

@@ -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(

View File

@@ -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])

View File

@@ -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.

View File

@@ -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)]
)

View File

@@ -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()))

View File

@@ -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

View File

@@ -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):
"""

View File

@@ -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()

View File

@@ -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()

View File

@@ -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,

View File

@@ -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'