From 29548459fafd3f8984f4ddfe0a62d39c96ccedcb Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 23 Jun 2021 18:24:06 -0400 Subject: [PATCH] refactor: Remove PyContracts usage. (#27887) * refactor: Remove PyContracts usage. We have not used PyContracts in a while and it is overhead we don't need in edx-platform. https://openedx.atlassian.net/browse/DEPR-147 * chore: Updating Python Requirements (#28018) Co-authored-by: edX requirements bot <49161187+edx-requirements-bot@users.noreply.github.com> --- cms/conftest.py | 4 -- cms/wsgi.py | 4 -- .../xmodule/xmodule/assetstore/__init__.py | 21 ----------- .../xmodule/xmodule/assetstore/assetmgr.py | 7 ---- .../assetstore/tests/test_asset_xml.py | 20 ---------- common/lib/xmodule/xmodule/graders.py | 4 -- .../xmodule/xmodule/modulestore/__init__.py | 21 ----------- .../lib/xmodule/xmodule/modulestore/mixed.py | 18 +-------- .../xmodule/xmodule/modulestore/mongo/base.py | 28 +------------- .../modulestore/split_mongo/__init__.py | 3 -- .../split_mongo/caching_descriptor_system.py | 21 +---------- .../split_mongo/mongo_connection.py | 14 ------- .../xmodule/modulestore/split_mongo/split.py | 29 --------------- .../modulestore/split_mongo/split_draft.py | 2 - .../split_mongo/split_mongo_kvs.py | 4 -- .../tests/test_split_modulestore.py | 7 ---- common/lib/xmodule/xmodule/x_module.py | 5 --- docs/guides/conf.py | 1 - lms/djangoapps/courseware/model_data.py | 23 ------------ lms/wsgi.py | 4 -- manage.py | 17 --------- .../djangoapps/django_comment_common/tests.py | 3 -- .../djangoapps/django_comment_common/utils.py | 4 -- openedx/core/lib/xblock_utils/__init__.py | 2 - pavelib/paver_tests/test_servers.py | 12 ++---- pavelib/servers.py | 13 +------ requirements/edx-sandbox/py38.txt | 2 +- requirements/edx/base.in | 1 - requirements/edx/base.txt | 25 +++++-------- requirements/edx/development.txt | 37 ++++++++----------- requirements/edx/pip-tools.txt | 5 ++- requirements/edx/testing.txt | 29 +++++---------- 32 files changed, 47 insertions(+), 343 deletions(-) diff --git a/cms/conftest.py b/cms/conftest.py index ca29e3fb5f..5f37a45775 100644 --- a/cms/conftest.py +++ b/cms/conftest.py @@ -11,7 +11,6 @@ import importlib import logging import os -import contracts import pytest from openedx.core.pytest_hooks import DeferPlugin @@ -32,9 +31,6 @@ def pytest_configure(config): if config.getoption('help'): return - enable_contracts = os.environ.get('ENABLE_CONTRACTS', False) - if not enable_contracts: - contracts.disable_all() settings_module = os.environ.get('DJANGO_SETTINGS_MODULE') startup_module = 'cms.startup' if settings_module.startswith('cms') else 'lms.startup' startup = importlib.import_module(startup_module) diff --git a/cms/wsgi.py b/cms/wsgi.py index b046ae780f..d69eb8a7b9 100644 --- a/cms/wsgi.py +++ b/cms/wsgi.py @@ -15,10 +15,6 @@ isort:skip_file from safe_lxml import defuse_xml_libs defuse_xml_libs() -# Disable PyContract contract checking when running as a webserver -import contracts # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position -contracts.disable_all() - import os # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.envs.aws") diff --git a/common/lib/xmodule/xmodule/assetstore/__init__.py b/common/lib/xmodule/xmodule/assetstore/__init__.py index 3b05be7c61..2a102a7dc5 100644 --- a/common/lib/xmodule/xmodule/assetstore/__init__.py +++ b/common/lib/xmodule/xmodule/assetstore/__init__.py @@ -8,18 +8,9 @@ from datetime import datetime import dateutil.parser import pytz -from contracts import contract, new_contract from lxml import etree from opaque_keys.edx.keys import AssetKey, CourseKey -new_contract('AssetKey', AssetKey) -new_contract('CourseKey', CourseKey) -new_contract('datetime', datetime) -new_contract('basestring', (str,)[0]) -new_contract('long', int) -new_contract('AssetElement', lambda x: isinstance(x, etree._Element) and x.tag == "asset") # pylint: disable=protected-access -new_contract('AssetsElement', lambda x: isinstance(x, etree._Element) and x.tag == "assets") # pylint: disable=protected-access - class AssetMetadata: """ @@ -51,13 +42,6 @@ class AssetMetadata: # Filename of all asset metadata exported as XML. EXPORTED_ASSET_FILENAME = 'assets.xml' - @contract(asset_id='AssetKey', - pathname='str|None', internal_name='str|None', - locked='bool|None', contenttype='str|None', - thumbnail='str|None', fields='dict|None', - curr_version='str|None', prev_version='str|None', - created_by='int|long|None', created_by_email='str|None', created_on='datetime|None', - edited_by='int|long|None', edited_by_email='str|None', edited_on='datetime|None') def __init__(self, asset_id, pathname=None, internal_name=None, locked=None, contenttype=None, @@ -155,7 +139,6 @@ class AssetMetadata: } } - @contract(asset_doc='dict|None') def from_storable(self, asset_doc): """ Fill in all metadata fields from a MongoDB document. @@ -179,7 +162,6 @@ class AssetMetadata: self.edited_by_email = asset_doc['edit_info']['edited_by_email'] self.edited_on = asset_doc['edit_info']['edited_on'] - @contract(node='AssetElement') def from_xml(self, node): """ Walk the etree XML node and fill in the asset metadata. @@ -210,7 +192,6 @@ class AssetMetadata: value = json.loads(value) setattr(self, tag, value) - @contract(node='AssetElement') def to_xml(self, node): """ Add the asset data as XML to the passed-in node. @@ -238,7 +219,6 @@ class AssetMetadata: child.text = value @staticmethod - @contract(node='AssetsElement', assets=list) def add_all_assets_as_xml(node, assets): """ Take a list of AssetMetadata objects. Add them all to the node. @@ -253,7 +233,6 @@ class CourseAssetsFromStorage: """ Wrapper class for asset metadata lists returned from modulestore storage. """ - @contract(course_id='CourseKey', asset_md=dict) def __init__(self, course_id, doc_id, asset_md): """ Params: diff --git a/common/lib/xmodule/xmodule/assetstore/assetmgr.py b/common/lib/xmodule/xmodule/assetstore/assetmgr.py index 8f579989df..fb5e830bb2 100644 --- a/common/lib/xmodule/xmodule/assetstore/assetmgr.py +++ b/common/lib/xmodule/xmodule/assetstore/assetmgr.py @@ -13,14 +13,8 @@ Note: Hotfix (PLAT-734) No asset calls find_asset_metadata, and directly accesse """ - -from contracts import contract, new_contract -from opaque_keys.edx.keys import AssetKey - from xmodule.contentstore.django import contentstore -new_contract('AssetKey', AssetKey) - class AssetException(Exception): """ @@ -48,7 +42,6 @@ class AssetManager: Manager for saving/loading course assets. """ @staticmethod - @contract(asset_key='AssetKey', throw_on_not_found='bool', as_stream='bool') def find(asset_key, throw_on_not_found=True, as_stream=False): """ Finds course asset in the deprecated contentstore. diff --git a/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py b/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py index eeaa762718..dc6089af34 100644 --- a/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py +++ b/common/lib/xmodule/xmodule/assetstore/tests/test_asset_xml.py @@ -4,9 +4,7 @@ Test for asset XML generation / parsing. import unittest -import pytest -from contracts import ContractNotRespected from lxml import etree from opaque_keys.edx.locator import CourseLocator from path import Path as path @@ -81,21 +79,3 @@ class TestAssetXml(unittest.TestCase): AssetMetadata.add_all_assets_as_xml(root, self.course_assets) # If this line does *not* raise, the XML is valid. etree.fromstring(etree.tostring(root), self.xmlparser) - - def test_wrong_node_type_all(self): - """ - Ensure full asset sections with the wrong tag are detected. - """ - root = etree.Element("glassets") - with pytest.raises(ContractNotRespected): - AssetMetadata.add_all_assets_as_xml(root, self.course_assets) - - def test_wrong_node_type_single(self): - """ - Ensure single asset blocks with the wrong tag are detected. - """ - asset_md = self.course_assets[0] - root = etree.Element("assets") - asset = etree.SubElement(root, "smashset") - with pytest.raises(ContractNotRespected): - asset_md.to_xml(asset) diff --git a/common/lib/xmodule/xmodule/graders.py b/common/lib/xmodule/xmodule/graders.py index 1dfead90d1..152676e955 100644 --- a/common/lib/xmodule/xmodule/graders.py +++ b/common/lib/xmodule/xmodule/graders.py @@ -11,7 +11,6 @@ import sys from collections import OrderedDict from datetime import datetime -from contracts import contract from pytz import UTC from django.utils.translation import ugettext_lazy as _ @@ -26,7 +25,6 @@ class ScoreBase(metaclass=abc.ABCMeta): # pylint: disable=eq-without-hash Abstract base class for encapsulating fields of values scores. """ - @contract(graded="bool", first_attempted="datetime|None") def __init__(self, graded, first_attempted): """ Fields common to all scores include: @@ -56,7 +54,6 @@ class ProblemScore(ScoreBase): """ Encapsulates the fields of a Problem's score. """ - @contract def __init__(self, raw_earned, raw_possible, weighted_earned, weighted_possible, weight, *args, **kwargs): """ In addition to the fields in ScoreBase, arguments include: @@ -88,7 +85,6 @@ class AggregatedScore(ScoreBase): """ Encapsulates the fields of a Subsection's score. """ - @contract def __init__(self, tw_earned, tw_possible, *args, **kwargs): """ In addition to the fields in ScoreBase, also includes: diff --git a/common/lib/xmodule/xmodule/modulestore/__init__.py b/common/lib/xmodule/xmodule/modulestore/__init__.py index 517918b5cc..caff9a6aa3 100644 --- a/common/lib/xmodule/xmodule/modulestore/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/__init__.py @@ -13,7 +13,6 @@ from collections import defaultdict from contextlib import contextmanager from operator import itemgetter -from contracts import contract, new_contract from opaque_keys.edx.keys import AssetKey, CourseKey from opaque_keys.edx.locations import Location # For import backwards compatibility from pytz import UTC @@ -32,11 +31,6 @@ from .exceptions import InsufficientSpecificationError, InvalidLocationError log = logging.getLogger('edx.modulestore') -new_contract('CourseKey', CourseKey) -new_contract('AssetKey', AssetKey) -new_contract('AssetMetadata', AssetMetadata) -new_contract('XBlock', XBlock) - LIBRARY_ROOT = 'library.xml' COURSE_ROOT = 'course.xml' @@ -497,9 +491,6 @@ class BlockData: # pylint: disable=eq-without-hash return not self == block_data -new_contract('BlockData', BlockData) - - class IncorrectlySortedList(Exception): """ Thrown when calling find() on a SortedAssetList not sorted by filename. @@ -519,7 +510,6 @@ class SortedAssetList(SortedKeyList): # lint-amnesty, pylint: disable=abstract- self.filename_sort = True super().__init__(**kwargs) - @contract(asset_id=AssetKey) def find(self, asset_id): """ Find the index of a particular asset in the list. This method is only functional for lists @@ -540,7 +530,6 @@ class SortedAssetList(SortedKeyList): # lint-amnesty, pylint: disable=abstract- idx = idx_left return idx - @contract(asset_md=AssetMetadata) def insert_or_update(self, asset_md): """ Insert asset metadata if asset is not present. Update asset metadata if asset is already present. @@ -577,7 +566,6 @@ class ModuleStoreAssetBase: return course_assets, idx - @contract(asset_key='AssetKey') def find_asset_metadata(self, asset_key, **kwargs): """ Find the metadata for a particular course asset. @@ -597,10 +585,6 @@ class ModuleStoreAssetBase: mdata.from_storable(all_assets[asset_idx]) return mdata - @contract( - course_key='CourseKey', asset_type='None | str', - start='int | None', maxresults='int | None', sort='tuple(str,int) | None' - ) def get_all_asset_metadata(self, course_key, asset_type, start=0, maxresults=-1, sort=None, **kwargs): # lint-amnesty, pylint: disable=unused-argument """ Returns a list of asset metadata for all assets of the given asset_type in the course. @@ -699,7 +683,6 @@ class ModuleStoreAssetWriteInterface(ModuleStoreAssetBase): all_assets.insert_or_update(asset_md) return assets_by_type - @contract(asset_metadata='AssetMetadata') def save_asset_metadata(self, asset_metadata, user_id, import_only): """ Saves the asset metadata for a particular course's asset. @@ -714,7 +697,6 @@ class ModuleStoreAssetWriteInterface(ModuleStoreAssetBase): """ raise NotImplementedError() - @contract(asset_metadata_list='list(AssetMetadata)') def save_asset_metadata_list(self, asset_metadata_list, user_id, import_only): """ Saves a list of asset metadata for a particular course's asset. @@ -741,7 +723,6 @@ class ModuleStoreAssetWriteInterface(ModuleStoreAssetBase): """ raise NotImplementedError() - @contract(asset_key='AssetKey', attr=str) def set_asset_metadata_attr(self, asset_key, attr, value, user_id): """ Add/set the given attr on the asset at the given location. Value can be any type which pymongo accepts. @@ -758,7 +739,6 @@ class ModuleStoreAssetWriteInterface(ModuleStoreAssetBase): """ return self.set_asset_metadata_attrs(asset_key, {attr: value}, user_id) - @contract(source_course_key='CourseKey', dest_course_key='CourseKey') def copy_all_asset_metadata(self, source_course_key, dest_course_key, user_id): """ Copy all the course assets from source_course_key to dest_course_key. @@ -832,7 +812,6 @@ class ModuleStoreRead(ModuleStoreAssetBase, metaclass=ABCMeta): """ pass # lint-amnesty, pylint: disable=unnecessary-pass - @contract(block='XBlock | BlockData | dict', qualifiers=dict) def _block_matches(self, block, qualifiers): """ Return True or False depending on whether the field value (block contents) diff --git a/common/lib/xmodule/xmodule/modulestore/mixed.py b/common/lib/xmodule/xmodule/modulestore/mixed.py index 8450032de8..f7f538f6a9 100644 --- a/common/lib/xmodule/xmodule/modulestore/mixed.py +++ b/common/lib/xmodule/xmodule/modulestore/mixed.py @@ -11,9 +11,8 @@ import itertools import logging from contextlib import contextmanager -from contracts import contract, new_contract from opaque_keys import InvalidKeyError -from opaque_keys.edx.keys import AssetKey, CourseKey +from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import LibraryLocator from xmodule.assetstore import AssetMetadata @@ -23,12 +22,6 @@ from .draft_and_published import ModuleStoreDraftAndPublished from .exceptions import DuplicateCourseError, ItemNotFoundError from .split_migrator import SplitMigrator -new_contract('CourseKey', CourseKey) -new_contract('AssetKey', AssetKey) -new_contract('AssetMetadata', AssetMetadata) -new_contract('LibraryLocator', LibraryLocator) -new_contract('long', int) - log = logging.getLogger(__name__) @@ -415,7 +408,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): return None @strip_key - @contract(library_key='LibraryLocator') def get_library(self, library_key, depth=0, **kwargs): """ returns the library block associated with the given key. If no such library exists, @@ -456,7 +448,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): store = self._get_modulestore_for_courselike(course_key) return store.delete_course(course_key, user_id) - @contract(asset_metadata='AssetMetadata', user_id='int|long', import_only=bool) def save_asset_metadata(self, asset_metadata, user_id, import_only=False): """ Saves the asset metadata for a particular course's asset. @@ -472,7 +463,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): store = self._get_modulestore_for_courselike(asset_metadata.asset_id.course_key) return store.save_asset_metadata(asset_metadata, user_id, import_only) - @contract(asset_metadata_list='list(AssetMetadata)', user_id='int|long', import_only=bool) def save_asset_metadata_list(self, asset_metadata_list, user_id, import_only=False): """ Saves the asset metadata for each asset in a list of asset metadata. @@ -492,7 +482,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): return store.save_asset_metadata_list(asset_metadata_list, user_id, import_only) @strip_key - @contract(asset_key='AssetKey') def find_asset_metadata(self, asset_key, **kwargs): """ Find the metadata for a particular course asset. @@ -507,7 +496,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): return store.find_asset_metadata(asset_key, **kwargs) @strip_key - @contract(course_key='CourseKey', asset_type='None | str', start=int, maxresults=int, sort='tuple|None') def get_all_asset_metadata(self, course_key, asset_type, start=0, maxresults=-1, sort=None, **kwargs): """ Returns a list of static assets for a course. @@ -529,7 +517,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): store = self._get_modulestore_for_courselike(course_key) return store.get_all_asset_metadata(course_key, asset_type, start, maxresults, sort, **kwargs) - @contract(asset_key='AssetKey', user_id='int|long') def delete_asset_metadata(self, asset_key, user_id): """ Deletes a single asset's metadata. @@ -544,7 +531,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): store = self._get_modulestore_for_courselike(asset_key.course_key) return store.delete_asset_metadata(asset_key, user_id) - @contract(source_course_key='CourseKey', dest_course_key='CourseKey', user_id='int|long') def copy_all_asset_metadata(self, source_course_key, dest_course_key, user_id): """ Copy all the course assets from source_course_key to dest_course_key. @@ -570,7 +556,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): # Courses in the same modulestore can be handled by the modulestore itself. source_store.copy_all_asset_metadata(source_course_key, dest_course_key, user_id) - @contract(asset_key='AssetKey', attr=str, user_id='int|long') def set_asset_metadata_attr(self, asset_key, attr, value, user_id): """ Add/set the given attr on the asset at the given location. Value can be any type which pymongo accepts. @@ -588,7 +573,6 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase): store = self._get_modulestore_for_courselike(asset_key.course_key) return store.set_asset_metadata_attrs(asset_key, {attr: value}, user_id) - @contract(asset_key='AssetKey', attr_dict=dict, user_id='int|long') def set_asset_metadata_attrs(self, asset_key, attr_dict, user_id): # lint-amnesty, pylint: disable=arguments-differ """ Add/set the given dict of attrs on the asset at the given location. Value can be any type which pymongo accepts. diff --git a/common/lib/xmodule/xmodule/modulestore/mongo/base.py b/common/lib/xmodule/xmodule/modulestore/mongo/base.py index 6c5bd61178..bb4644ef74 100644 --- a/common/lib/xmodule/xmodule/modulestore/mongo/base.py +++ b/common/lib/xmodule/xmodule/modulestore/mongo/base.py @@ -23,10 +23,9 @@ from uuid import uuid4 import pymongo from bson.son import SON -from contracts import contract, new_contract from fs.osfs import OSFS from mongodb_proxy import autoretry_read -from opaque_keys.edx.keys import AssetKey, CourseKey, UsageKey +from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator from path import Path as path from pytz import UTC @@ -54,12 +53,6 @@ from xmodule.services import SettingsService log = logging.getLogger(__name__) -new_contract('CourseKey', CourseKey) -new_contract('AssetKey', AssetKey) -new_contract('AssetMetadata', AssetMetadata) -new_contract('long', int) -new_contract('BlockUsageLocator', BlockUsageLocator) - # sort order that returns DRAFT items first SORT_REVISION_FAVOR_DRAFT = ('_id.revision', pymongo.DESCENDING) @@ -422,9 +415,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li return [] -new_contract('CachingDescriptorSystem', CachingDescriptorSystem) - - # The only thing using this w/ wildcards is contentstore.mongo for asset retrieval def location_to_query(location, wildcard=True, tag='i4x'): """ @@ -512,15 +502,12 @@ class ParentLocationCache(dict): Dict-based object augmented with a more cache-like interface, for internal use. """ - @contract(key=str) def has(self, key): return key in self - @contract(key=str, value="BlockUsageLocator | None") def set(self, key, value): self[key] = value - @contract(value="BlockUsageLocator") def delete_by_value(self, value): keys_to_delete = [k for k, v in self.items() if v == value] for key in keys_to_delete: @@ -874,12 +861,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo return data - @contract( - course_key=CourseKey, - item=dict, - apply_cached_metadata=bool, - using_descriptor_system="None|CachingDescriptorSystem" - ) def _load_item(self, course_key, item, data_cache, apply_cached_metadata=True, using_descriptor_system=None, for_parent=None): """ @@ -1831,7 +1812,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo """ return f'assets.{asset_type}' - @contract(asset_metadata_list='list(AssetMetadata)', user_id='int|long') def _save_asset_metadata_list(self, asset_metadata_list, user_id, import_only): """ Internal; saves the info for a particular course's asset. @@ -1857,7 +1837,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ) return True - @contract(asset_metadata='AssetMetadata', user_id='int|long') def save_asset_metadata(self, asset_metadata, user_id, import_only=False): """ Saves the info for a particular course's asset. @@ -1872,7 +1851,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo """ return self._save_asset_metadata_list([asset_metadata, ], user_id, import_only) - @contract(asset_metadata_list='list(AssetMetadata)', user_id='int|long') def save_asset_metadata_list(self, asset_metadata_list, user_id, import_only=False): """ Saves the asset metadata for each asset in a list of asset metadata. @@ -1888,7 +1866,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo """ return self._save_asset_metadata_list(asset_metadata_list, user_id, import_only) - @contract(source_course_key='CourseKey', dest_course_key='CourseKey', user_id='int|long') def copy_all_asset_metadata(self, source_course_key, dest_course_key, user_id): """ Copy all the course assets from source_course_key to dest_course_key. @@ -1905,7 +1882,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo # Update the document. self.asset_collection.insert_one(dest_assets) - @contract(asset_key='AssetKey', attr_dict=dict, user_id='int|long') def set_asset_metadata_attrs(self, asset_key, attr_dict, user_id): # lint-amnesty, pylint: disable=arguments-differ """ Add/set the given dict of attrs on the asset at the given location. Value can be any type which pymongo accepts. @@ -1936,7 +1912,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo {"$set": {self._make_mongo_asset_key(asset_key.asset_type): all_assets}} ) - @contract(asset_key='AssetKey', user_id='int|long') def delete_asset_metadata(self, asset_key, user_id): """ Internal; deletes a single asset's metadata. @@ -1961,7 +1936,6 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo ) return 1 - @contract(course_key='CourseKey', user_id='int|long') def delete_all_asset_metadata(self, course_key, user_id): # lint-amnesty, pylint: disable=unused-argument """ Delete all of the assets which use this course_key as an identifier. diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py index 0ac90314c3..d7664b6faa 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/__init__.py @@ -5,19 +5,16 @@ General utilities from collections import namedtuple -from contracts import check, contract from opaque_keys.edx.locator import BlockUsageLocator class BlockKey(namedtuple('BlockKey', 'type id')): # lint-amnesty, pylint: disable=missing-class-docstring __slots__ = () - @contract(type="string[>0]") def __new__(cls, type, id): # lint-amnesty, pylint: disable=redefined-builtin return super().__new__(cls, type, id) @classmethod - @contract(usage_key=BlockUsageLocator) def from_usage_key(cls, usage_key): return cls(usage_key.block_type, usage_key.block_id) diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py index 6775b239e8..90fe9a4e58 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/caching_descriptor_system.py @@ -3,11 +3,9 @@ import logging import sys -from contracts import contract, new_contract from fs.osfs import OSFS from lazy import lazy -from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, DefinitionLocator, LibraryLocator, LocalId -from xblock.core import XBlock +from opaque_keys.edx.locator import BlockUsageLocator, DefinitionLocator, LocalId from xblock.fields import ScopeIds from xblock.runtime import KeyValueStore, KvsFieldData @@ -15,7 +13,6 @@ from xmodule.error_module import ErrorBlock from xmodule.errortracker import exc_info_to_str from xmodule.library_tools import LibraryToolsService from xmodule.mako_module import MakoDescriptorSystem -from xmodule.modulestore import BlockData from xmodule.modulestore.edit_info import EditInfoRuntimeMixin from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.inheritance import InheritanceMixin, inheriting_field_data @@ -27,14 +24,6 @@ from xmodule.x_module import XModuleMixin log = logging.getLogger(__name__) -new_contract('BlockUsageLocator', BlockUsageLocator) -new_contract('CourseLocator', CourseLocator) -new_contract('LibraryLocator', LibraryLocator) -new_contract('BlockKey', BlockKey) -new_contract('BlockData', BlockData) -new_contract('CourseEnvelope', CourseEnvelope) -new_contract('XBlock', XBlock) - class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # lint-amnesty, pylint: disable=abstract-method """ @@ -43,7 +32,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li Computes the settings (nee 'metadata') inheritance upon creation. """ - @contract(course_entry=CourseEnvelope) def __init__(self, modulestore, course_entry, default_class, module_data, lazy, **kwargs): # lint-amnesty, pylint: disable=redefined-outer-name """ Computes the settings inheritance and sets up the cache. @@ -87,7 +75,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li self._services['library_tools'] = LibraryToolsService(modulestore, user_id=None) @lazy - @contract(returns="dict(BlockKey: BlockKey)") def _parent_map(self): # lint-amnesty, pylint: disable=missing-function-docstring parent_map = {} for block_key, block in self.course_entry.structure['blocks'].items(): @@ -95,7 +82,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li parent_map[child] = block_key return parent_map - @contract(usage_key="BlockUsageLocator | BlockKey", course_entry_override="CourseEnvelope | None") def _load_item(self, usage_key, course_entry_override=None, **kwargs): """ Instantiate the xblock fetching it either from the cache or from the structure @@ -143,7 +129,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li self.modulestore.cache_block(course_key, version_guid, block_key, block) return block - @contract(block_key=BlockKey, course_key="CourseLocator | LibraryLocator") def get_module_data(self, block_key, course_key): """ Get block from module_data adding it to module_data if it's not already there but is in the structure @@ -172,7 +157,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li # low; thus, the course_entry is most likely correct. If the thread is looking at > 1 named container # pointing to the same structure, the access is likely to be chunky enough that the last known container # is the intended one when not given a course_entry_override; thus, the caching of the last branch/course id. - @contract(block_key="BlockKey | None") def xblock_from_json(self, class_, course_key, block_key, block_data, course_entry_override=None, **kwargs): """ Load and return block info. @@ -299,7 +283,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li """ return xblock._edited_on # lint-amnesty, pylint: disable=protected-access - @contract(xblock='XBlock') def get_subtree_edited_by(self, xblock): """ See :class: cms.lib.xblock.runtime.EditInfoRuntimeMixin @@ -315,7 +298,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li return xblock._subtree_edited_by - @contract(xblock='XBlock') def get_subtree_edited_on(self, xblock): """ See :class: cms.lib.xblock.runtime.EditInfoRuntimeMixin @@ -349,7 +331,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin): # li return getattr(xblock, '_published_on', None) - @contract(block_data='BlockData') def _compute_subtree_edited_internal(self, block_data, course_key): """ Recurse the subtree finding the max edited_on date and its corresponding edited_by. Cache it. diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py index 3cefdbe8ac..5df3c38662 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py @@ -14,7 +14,6 @@ from time import time import pymongo import pytz -from contracts import check, new_contract from mongodb_proxy import autoretry_read # Import this just to export it from pymongo.errors import DuplicateKeyError # pylint: disable=unused-import @@ -29,7 +28,6 @@ try: except ImportError: DJANGO_AVAILABLE = False -new_contract('BlockData', BlockData) log = logging.getLogger(__name__) @@ -156,12 +154,6 @@ def structure_from_mongo(structure, course_context=None): with TIMER.timer('structure_from_mongo', course_context) as tagger: tagger.measure('blocks', len(structure['blocks'])) - check('seq[2]', structure['root']) - check('list(dict)', structure['blocks']) - for block in structure['blocks']: - if 'children' in block['fields']: - check('list(list[2])', block['fields']['children']) - structure['root'] = BlockKey(*structure['root']) new_blocks = {} for block in structure['blocks']: @@ -184,12 +176,6 @@ def structure_to_mongo(structure, course_context=None): with TIMER.timer('structure_to_mongo', course_context) as tagger: tagger.measure('blocks', len(structure['blocks'])) - check('BlockKey', structure['root']) - check('dict(BlockKey: BlockData)', structure['blocks']) - for block in structure['blocks'].values(): - if 'children' in block.fields: - check('list(BlockKey)', block.fields['children']) - new_structure = dict(structure) new_structure['blocks'] = [] diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py index ac5674742f..7649ad2ced 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split.py @@ -64,7 +64,6 @@ from importlib import import_module from bson.objectid import ObjectId from ccx_keys.locator import CCXBlockUsageLocator, CCXLocator -from contracts import contract, new_contract from mongodb_proxy import autoretry_read from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import ( @@ -134,11 +133,6 @@ log = logging.getLogger(__name__) EXCLUDE_ALL = '*' -new_contract('BlockUsageLocator', BlockUsageLocator) -new_contract('BlockKey', BlockKey) -new_contract('XBlock', XBlock) - - class SplitBulkWriteRecord(BulkOpsRecord): # lint-amnesty, pylint: disable=missing-class-docstring def __init__(self): super().__init__() @@ -815,7 +809,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): system.module_data.update(new_module_data) return system.module_data - @contract(course_entry=CourseEnvelope, block_keys="list(BlockKey)", depth="int | None") def _load_items(self, course_entry, block_keys, depth=0, **kwargs): """ Load & cache the given blocks from the course. May return the blocks in any order. @@ -1224,7 +1217,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): return self._get_block_from_structure(course_structure, BlockKey.from_usage_key(usage_key)) is not None - @contract(returns='XBlock') def get_item(self, usage_key, depth=0, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ depth (int): An argument that some module stores may use to prefetch @@ -1724,7 +1716,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): return potential_key serial += 1 - @contract(returns='XBlock') def create_item(self, user_id, course_key, block_type, block_id=None, definition_locator=None, fields=None, # lint-amnesty, pylint: disable=arguments-differ asides=None, force=False, **kwargs): """ @@ -2491,7 +2482,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): self.update_structure(destination_course, destination_structure) self._update_head(destination_course, index_entry, destination_course.branch, destination_structure['_id']) - @contract(source_keys="list(BlockUsageLocator)", dest_usage=BlockUsageLocator) def copy_from_template(self, source_keys, dest_usage, user_id, head_validation=True): """ Flexible mechanism for inheriting content from an external course/library/etc. @@ -2723,7 +2713,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): return result - @contract(root_block_key=BlockKey, blocks='dict(BlockKey: BlockData)') def _remove_subtree(self, root_block_key, blocks): """ Remove the subtree rooted at root_block_key @@ -2772,7 +2761,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): self._emit_course_deleted_signal(course_key) - @contract(block_map="dict(BlockKey: dict)", block_key=BlockKey) def inherit_settings( self, block_map, block_key, inherited_settings_map, inheriting_settings=None, inherited_from=None ): @@ -2927,7 +2915,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): """ return self.save_asset_metadata_list([asset_metadata, ], user_id, import_only) - @contract(asset_key='AssetKey', attr_dict=dict) def set_asset_metadata_attrs(self, asset_key, attr_dict, user_id): # lint-amnesty, pylint: disable=arguments-differ """ Add/set the given dict of attrs on the asset at the given location. Value can be any type which pymongo accepts. @@ -2958,7 +2945,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): self._update_course_assets(user_id, asset_key, _internal_method) - @contract(asset_key='AssetKey') def delete_asset_metadata(self, asset_key, user_id): """ Internal; deletes a single asset's metadata. @@ -2985,7 +2971,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): except ItemNotFoundError: return 0 - @contract(source_course_key='CourseKey', dest_course_key='CourseKey') def copy_all_asset_metadata(self, source_course_key, dest_course_key, user_id): """ Copy all the course assets from source_course_key to dest_course_key. @@ -3037,7 +3022,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): and converting them. :param jsonfields: the serialized copy of the xblock's fields """ - @contract(block_key="BlockUsageLocator | seq[2]") def robust_usage_key(block_key): """ create a course_key relative usage key for the block_key. If the block_key is in blocks, @@ -3220,7 +3204,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): 'schema_version': self.SCHEMA_VERSION, } - @contract(block_key=BlockKey) def _get_parents_from_structure(self, block_key, structure): """ Given a structure, find block_key's parent in that structure. Note returns @@ -3247,12 +3230,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): destination_parent.fields['children'] = destination_reordered return orphans - @contract( - block_key=BlockKey, - source_blocks="dict(BlockKey: *)", - destination_blocks="dict(BlockKey: *)", - blacklist="list(BlockKey) | str", - ) def _copy_subdag(self, user_id, destination_version, block_key, source_blocks, destination_blocks, blacklist): """ Update destination_blocks for the sub-dag rooted at block_key to be like the one in @@ -3321,7 +3298,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): destination_blocks[block_key] = destination_block return orphans - @contract(blacklist='list(BlockKey) | str') def _filter_blacklist(self, fields, blacklist): """ Filter out blacklist from the children field in fields. Will construct a new list for children; @@ -3333,7 +3309,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): fields['children'] = [child for child in fields.get('children', []) if BlockKey(*child) not in blacklist] return fields - @contract(orphan=BlockKey) def _delete_if_true_orphan(self, orphan, structure): """ Delete the orphan and any of its descendants which no longer have parents. @@ -3343,7 +3318,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): for child in orphan_data.fields.get('children', []): self._delete_if_true_orphan(BlockKey(*child), structure) - @contract(returns=BlockData) def _new_block(self, user_id, category, block_fields, definition_id, new_id, raw=False, asides=None, block_defaults=None): """ @@ -3375,7 +3349,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): document['defaults'] = block_defaults return BlockData(**document) - @contract(block_key=BlockKey, returns='BlockData | None') def _get_block_from_structure(self, structure, block_key): """ Encodes the block key before retrieving it from the structure to ensure it can @@ -3383,7 +3356,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): """ return structure['blocks'].get(block_key) - @contract(block_key=BlockKey) def _get_asides_to_update_from_structure(self, structure, block_key, asides): """ Get list of aside fields that should be updated/inserted @@ -3415,7 +3387,6 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase): else: return block.asides, False - @contract(block_key=BlockKey, content=BlockData) def _update_block_in_structure(self, structure, block_key, content): """ Encodes the block key before accessing it in the structure to ensure it can diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py index 0f83cde52b..a51a1eb371 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_draft.py @@ -3,7 +3,6 @@ Module for the dual-branch fall-back Draft->Published Versioning ModuleStore """ -from contracts import contract from opaque_keys.edx.locator import CourseLocator, LibraryLocator, LibraryUsageLocator from xmodule.exceptions import InvalidVersionError @@ -612,7 +611,6 @@ class DraftVersioningModuleStore(SplitMongoModuleStore, ModuleStoreDraftAndPubli xblock._published_by = published_block.edit_info.edited_by xblock._published_on = published_block.edit_info.edited_on - @contract(asset_key='AssetKey') def find_asset_metadata(self, asset_key, **kwargs): return super().find_asset_metadata( self._map_revision_to_branch(asset_key), **kwargs diff --git a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py index 0d3bf16f8c..0c32123207 100644 --- a/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py +++ b/common/lib/xmodule/xmodule/modulestore/split_mongo/split_mongo_kvs.py @@ -3,8 +3,6 @@ import copy from collections import namedtuple -from contracts import contract, new_contract -from opaque_keys.edx.locator import BlockUsageLocator from xblock.core import XBlockAside from xblock.exceptions import InvalidScopeError from xblock.fields import Scope @@ -15,7 +13,6 @@ from .definition_lazy_loader import DefinitionLazyLoader # id is a BlockUsageLocator, def_id is the definition's guid SplitMongoKVSid = namedtuple('SplitMongoKVSid', 'id, def_id') -new_contract('BlockUsageLocator', BlockUsageLocator) class SplitMongoKVS(InheritanceKeyValueStore): @@ -26,7 +23,6 @@ class SplitMongoKVS(InheritanceKeyValueStore): VALID_SCOPES = (Scope.parent, Scope.children, Scope.settings, Scope.content) - @contract(parent="BlockUsageLocator | None") def __init__(self, definition, initial_values, default_values, parent, aside_fields=None, field_decorator=None): """ diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py index fb4768ee6b..b134be64e3 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_split_modulestore.py @@ -14,7 +14,6 @@ from unittest.mock import patch import pytest import ddt from ccx_keys.locator import CCXBlockUsageLocator -from contracts import contract from django.core.cache import InvalidCacheBackendError, caches from opaque_keys.edx.locator import BlockUsageLocator, CourseKey, CourseLocator, LocalId, VersionTree from path import Path as path @@ -2125,7 +2124,6 @@ class TestPublish(SplitModuleTest): ] self._check_course(source_course, dest_course, expected, [BlockKey("chapter", "chapter2"), BlockKey("problem", "problem3_2")]) # lint-amnesty, pylint: disable=line-too-long - @contract(expected_blocks="list(BlockKey)", unexpected_blocks="list(BlockKey)") def _check_course(self, source_course_loc, dest_course_loc, expected_blocks, unexpected_blocks): """ Check that the course has the expected blocks and does not have the unexpected blocks @@ -2164,11 +2162,6 @@ class TestPublish(SplitModuleTest): actual = {key: BlockKey.from_usage_key(val) for (key, val) in actual} assert expected == actual - @contract( - source_children="list(BlockUsageLocator)", - dest_children="list(BlockUsageLocator)", - unexpected="list(BlockKey)" - ) def _compare_children(self, source_children, dest_children, unexpected): """ Ensure dest_children == source_children minus unexpected diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index e38c17c691..b814a3a695 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -8,7 +8,6 @@ from collections import namedtuple from functools import partial import yaml -from contracts import contract, new_contract from django.utils.encoding import python_2_unicode_compatible from lazy import lazy from lxml import etree @@ -1618,9 +1617,6 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): return service -new_contract('DescriptorSystem', DescriptorSystem) - - class XMLParsingSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstract-method, missing-class-docstring def __init__(self, process_xml, **kwargs): """ @@ -1750,7 +1746,6 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime): and user, or other environment-specific info. """ - @contract(descriptor_runtime='DescriptorSystem') def __init__( self, static_url, track_function, get_module, render_template, replace_urls, descriptor_runtime, user=None, filestore=None, diff --git a/docs/guides/conf.py b/docs/guides/conf.py index 372284e3e4..58441b5c1b 100644 --- a/docs/guides/conf.py +++ b/docs/guides/conf.py @@ -214,7 +214,6 @@ intersphinx_mapping = { # Mock out these external modules during code import to avoid errors autodoc_mock_imports = [ 'MySQLdb', - 'contracts', 'django_mysql', 'pymongo', ] diff --git a/lms/djangoapps/courseware/model_data.py b/lms/djangoapps/courseware/model_data.py index 79627d03d9..7db24f20f5 100644 --- a/lms/djangoapps/courseware/model_data.py +++ b/lms/djangoapps/courseware/model_data.py @@ -27,7 +27,6 @@ import logging from abc import ABCMeta, abstractmethod from collections import defaultdict, namedtuple -from contracts import contract, new_contract from django.db import DatabaseError, IntegrityError, transaction from opaque_keys.edx.asides import AsideUsageKeyV1, AsideUsageKeyV2 from opaque_keys.edx.block_types import BlockTypeKeyV1 @@ -149,10 +148,6 @@ class DjangoKeyValueStore(KeyValueStore): raise InvalidScopeError(key, self._allowed_scopes) -new_contract("DjangoKeyValueStore", DjangoKeyValueStore) -new_contract("DjangoKeyValueStore_Key", DjangoKeyValueStore.Key) - - class DjangoOrmFieldCache(metaclass=ABCMeta): """ Baseclass for Scope-specific field cache objects that are based on @@ -175,7 +170,6 @@ class DjangoOrmFieldCache(metaclass=ABCMeta): for field_object in self._read_objects(fields, xblocks, aside_types): self._cache[self._cache_key_for_field_object(field_object)] = field_object - @contract(kvs_key=DjangoKeyValueStore.Key) def get(self, kvs_key): """ Return the django model object specified by `kvs_key` from @@ -194,7 +188,6 @@ class DjangoOrmFieldCache(metaclass=ABCMeta): return json.loads(field_object.value) - @contract(kvs_key=DjangoKeyValueStore.Key) def set(self, kvs_key, value): """ Set the specified `kvs_key` to the field value `value`. @@ -205,7 +198,6 @@ class DjangoOrmFieldCache(metaclass=ABCMeta): """ self.set_many({kvs_key: value}) - @contract(kv_dict="dict(DjangoKeyValueStore_Key: *)") def set_many(self, kv_dict): """ Set the specified fields to the supplied values. @@ -241,7 +233,6 @@ class DjangoOrmFieldCache(metaclass=ABCMeta): finally: saved_fields.append(kvs_key.field_name) - @contract(kvs_key=DjangoKeyValueStore.Key) def delete(self, kvs_key): """ Delete the value specified by `kvs_key`. @@ -260,7 +251,6 @@ class DjangoOrmFieldCache(metaclass=ABCMeta): field_object.delete() del self._cache[cache_key] - @contract(kvs_key=DjangoKeyValueStore.Key, returns=bool) def has(self, kvs_key): """ Return whether the specified `kvs_key` is set. @@ -272,7 +262,6 @@ class DjangoOrmFieldCache(metaclass=ABCMeta): """ return self._cache_key_for_kvs_key(kvs_key) in self._cache - @contract(kvs_key=DjangoKeyValueStore.Key, returns="datetime|None") def last_modified(self, kvs_key): """ Return when the supplied field was changed. @@ -368,7 +357,6 @@ class UserStateCache: for user_state in block_field_state: self._cache[user_state.block_key] = user_state.state - @contract(kvs_key=DjangoKeyValueStore.Key) def set(self, kvs_key, value): """ Set the specified `kvs_key` to the field value `value`. @@ -379,7 +367,6 @@ class UserStateCache: """ self.set_many({kvs_key: value}) - @contract(kvs_key=DjangoKeyValueStore.Key, returns="datetime|None") def last_modified(self, kvs_key): """ Return when the supplied field was changed. @@ -398,7 +385,6 @@ class UserStateCache: except self._client.DoesNotExist: return None - @contract(kv_dict="dict(DjangoKeyValueStore_Key: *)") def set_many(self, kv_dict): """ Set the specified fields to the supplied values. @@ -424,7 +410,6 @@ class UserStateCache: finally: self._cache.update(pending_updates) - @contract(kvs_key=DjangoKeyValueStore.Key) def get(self, kvs_key): """ Return the django model object specified by `kvs_key` from @@ -441,7 +426,6 @@ class UserStateCache: return self._cache[cache_key][kvs_key.field_name] - @contract(kvs_key=DjangoKeyValueStore.Key) def delete(self, kvs_key): """ Delete the value specified by `kvs_key`. @@ -463,7 +447,6 @@ class UserStateCache: self._client.delete(self.user.username, cache_key, fields=[kvs_key.field_name]) del field_state[kvs_key.field_name] - @contract(kvs_key=DjangoKeyValueStore.Key, returns=bool) def has(self, kvs_key): """ Return whether the specified `kvs_key` is set. @@ -803,7 +786,6 @@ class FieldDataCache: scope_map[field.scope].add(field) return scope_map - @contract(key=DjangoKeyValueStore.Key) def get(self, key): """ Load the field value specified by `key`. @@ -825,7 +807,6 @@ class FieldDataCache: return self.cache[key.scope].get(key) - @contract(kv_dict="dict(DjangoKeyValueStore_Key: *)") def set_many(self, kv_dict): """ Set all of the fields specified by the keys of `kv_dict` to the values @@ -862,7 +843,6 @@ class FieldDataCache: log.exception('Error saving fields %r', [key.field_name for key in set_many_data]) raise KeyValueMultiSaveError(saved_fields + exc.saved_field_names) # lint-amnesty, pylint: disable=raise-missing-from - @contract(key=DjangoKeyValueStore.Key) def delete(self, key): """ Delete the value specified by `key`. @@ -885,7 +865,6 @@ class FieldDataCache: self.cache[key.scope].delete(key) - @contract(key=DjangoKeyValueStore.Key, returns=bool) def has(self, key): """ Return whether the specified `key` is set. @@ -906,7 +885,6 @@ class FieldDataCache: return self.cache[key.scope].has(key) - @contract(key=DjangoKeyValueStore.Key, returns="datetime|None") def last_modified(self, key): """ Return when the supplied field was changed. @@ -989,7 +967,6 @@ class ScoresClient: return client -# @contract(user_id=int, usage_key=UsageKey, score="number|None", max_score="number|None") def set_score(user_id, usage_key, score, max_score): """ Set the score and max_score for the specified user and xblock usage. diff --git a/lms/wsgi.py b/lms/wsgi.py index 4a8cb67049..f0cf08305a 100644 --- a/lms/wsgi.py +++ b/lms/wsgi.py @@ -12,10 +12,6 @@ It exposes a module-level variable named ``application``. Django's from safe_lxml import defuse_xml_libs defuse_xml_libs() -# Disable PyContract contract checking when running as a webserver -import contracts # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position -contracts.disable_all() - import os # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.envs.aws") diff --git a/manage.py b/manage.py index 66ca09ac78..f8ca29b1d3 100755 --- a/manage.py +++ b/manage.py @@ -25,8 +25,6 @@ import os import sys from argparse import ArgumentParser -import contracts - def parse_args(): """Parse edx specific arguments to manage.py""" @@ -49,11 +47,6 @@ def parse_args(): choices=['lms', 'lms-xml', 'lms-preview'], default='lms', help='Which service variant to run, when using the production environment') - lms.add_argument( - '--contracts', - action='store_true', - default=False, - help='Turn on pycontracts for local development') lms.set_defaults( help_string=lms.format_help(), settings_base='lms/envs', @@ -72,11 +65,6 @@ def parse_args(): help="Which django settings module to use under cms.envs. If not provided, the DJANGO_SETTINGS_MODULE " "environment variable will be used if it is set, otherwise it will default to cms.envs.devstack_docker") cms.add_argument('-h', '--help', action='store_true', help='show this help message and exit') - cms.add_argument( - '--contracts', - action='store_true', - default=False, - help='Turn on pycontracts for local development') cms.set_defaults( help_string=cms.format_help(), settings_base='cms/envs', @@ -106,11 +94,6 @@ if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", edx_args.default_settings) os.environ.setdefault("SERVICE_VARIANT", edx_args.service_variant) - enable_contracts = os.environ.get('ENABLE_CONTRACTS', False) - # can override with '--contracts' argument - if not enable_contracts and not edx_args.contracts: - contracts.disable_all() - if edx_args.help: print("Django:") # This will trigger django-admin.py to print out its help diff --git a/openedx/core/djangoapps/django_comment_common/tests.py b/openedx/core/djangoapps/django_comment_common/tests.py index 6eb45cd2c9..b9d00195d6 100644 --- a/openedx/core/djangoapps/django_comment_common/tests.py +++ b/openedx/core/djangoapps/django_comment_common/tests.py @@ -2,7 +2,6 @@ import pytest -from contracts import new_contract from django.test import TestCase from opaque_keys.edx.locator import CourseLocator @@ -14,8 +13,6 @@ from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory -new_contract('basestring', str) - class RoleAssignmentTest(TestCase): """ diff --git a/openedx/core/djangoapps/django_comment_common/utils.py b/openedx/core/djangoapps/django_comment_common/utils.py index 4b1ce71c06..fd3a918bcb 100644 --- a/openedx/core/djangoapps/django_comment_common/utils.py +++ b/openedx/core/djangoapps/django_comment_common/utils.py @@ -2,8 +2,6 @@ """ Common comment client utility functions. """ -from contracts import new_contract - from openedx.core.djangoapps.django_comment_common.models import ( FORUM_ROLE_ADMINISTRATOR, FORUM_ROLE_COMMUNITY_TA, @@ -13,8 +11,6 @@ from openedx.core.djangoapps.django_comment_common.models import ( Role ) -new_contract('basestring', str) - class ThreadContext: """ An enumeration that represents the context of a thread. Used primarily by the comments service. """ diff --git a/openedx/core/lib/xblock_utils/__init__.py b/openedx/core/lib/xblock_utils/__init__.py index 460c2c8034..1a320f0504 100644 --- a/openedx/core/lib/xblock_utils/__init__.py +++ b/openedx/core/lib/xblock_utils/__init__.py @@ -12,7 +12,6 @@ import uuid import markupsafe import webpack_loader.utils -from contracts import contract from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.contrib.staticfiles.storage import staticfiles_storage @@ -309,7 +308,6 @@ def sanitize_html_id(html_id): return sanitized_html_id -@contract(user=User, block=XBlock, view=(str,)[0], frag=Fragment, context="dict|None") def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context): # pylint: disable=unused-argument """ Updates the supplied module with a new get_html function that wraps diff --git a/pavelib/paver_tests/test_servers.py b/pavelib/paver_tests/test_servers.py index 26fa5c6463..af60d247de 100644 --- a/pavelib/paver_tests/test_servers.py +++ b/pavelib/paver_tests/test_servers.py @@ -100,7 +100,6 @@ class TestPaverServerTasks(PaverTestCase): [{"fast": True}], [{"optimized": True}], [{"optimized": True, "fast": True}], - [{"no-contracts": True}], ) @ddt.unpack def test_devstack(self, server_options): @@ -119,7 +118,7 @@ class TestPaverServerTasks(PaverTestCase): settings=expected_settings, ) ] - self.verify_server_task("devstack", options, contracts_default=True) + self.verify_server_task("devstack", options) # Then test with Studio options["system"] = "cms" @@ -129,7 +128,7 @@ class TestPaverServerTasks(PaverTestCase): settings=expected_settings, ) ] - self.verify_server_task("devstack", options, contracts_default=True) + self.verify_server_task("devstack", options) @ddt.data( [{}], @@ -193,7 +192,7 @@ class TestPaverServerTasks(PaverTestCase): ["echo 'import {system}.envs.{settings}' | python manage.py {system} " "--settings={settings} shell --plain --pythonpath=.".format(system=system, settings=settings)] - def verify_server_task(self, task_name, options, contracts_default=False): + def verify_server_task(self, task_name, options): """ Verify the output of a server task. """ @@ -202,7 +201,6 @@ class TestPaverServerTasks(PaverTestCase): asset_settings = options.get("asset-settings", None) is_optimized = options.get("optimized", False) is_fast = options.get("fast", False) - no_contracts = options.get("no-contracts", not contracts_default) if task_name == "devstack": system = options.get("system") elif task_name == "studio": @@ -221,8 +219,6 @@ class TestPaverServerTasks(PaverTestCase): args.append("--optimized") if is_fast: args.append("--fast") - if no_contracts: - args.append("--no-contracts") call_task("pavelib.servers.devstack", args=args) else: call_task(f"pavelib.servers.{task_name}", options=options) @@ -257,8 +253,6 @@ class TestPaverServerTasks(PaverTestCase): settings=expected_settings, port=port, ) - if not no_contracts: - expected_run_server_command += " --contracts" expected_messages.append(expected_run_server_command) assert self.task_messages == expected_messages diff --git a/pavelib/servers.py b/pavelib/servers.py index cb571a3229..2571a28750 100644 --- a/pavelib/servers.py +++ b/pavelib/servers.py @@ -25,7 +25,7 @@ ASSET_SETTINGS_HELP = ( def run_server( - system, fast=False, settings=None, asset_settings=None, port=None, contracts=False + system, fast=False, settings=None, asset_settings=None, port=None ): """Start the server for LMS or Studio. @@ -35,7 +35,6 @@ def run_server( settings (str): The Django settings module to use; if not provided, use the default. asset_settings (str) The settings to use when generating assets. If not provided, assets are not generated. port (str): The port number to run the server on. If not provided, uses the default port for the system. - contracts (bool) If true then PyContracts is enabled (defaults to False). """ if system not in ['lms', 'studio']: print("System must be either lms or studio", file=sys.stderr) @@ -58,9 +57,6 @@ def run_server( args = [settings, 'runserver', '--traceback', '--pythonpath=.', f'0.0.0.0:{port}'] - if contracts: - args.append("--contracts") - run_process(django_cmd(system, *args)) @@ -127,12 +123,6 @@ def devstack(args): parser.add_argument('--optimized', action='store_true', default=False, help="Run with optimized assets") parser.add_argument('--settings', type=str, default=DEFAULT_SETTINGS, help="Settings file") parser.add_argument('--asset-settings', type=str, default=None, help=ASSET_SETTINGS_HELP) - parser.add_argument( - '--no-contracts', - action='store_true', - default=False, - help="Disable contracts. By default, they're enabled in devstack." - ) args = parser.parse_args(args) settings = args.settings asset_settings = args.asset_settings if args.asset_settings else settings @@ -145,7 +135,6 @@ def devstack(args): fast=args.fast, settings=settings, asset_settings=asset_settings, - contracts=not args.no_contracts, ) diff --git a/requirements/edx-sandbox/py38.txt b/requirements/edx-sandbox/py38.txt index 45ca62c841..5e763dce90 100644 --- a/requirements/edx-sandbox/py38.txt +++ b/requirements/edx-sandbox/py38.txt @@ -48,7 +48,7 @@ nltk==3.6.2 # via # -r requirements/edx-sandbox/py38.in # chem -numpy==1.20.3 +numpy==1.21.0 # via # -r requirements/edx-sandbox/py38.in # chem diff --git a/requirements/edx/base.in b/requirements/edx/base.in index f2d47bb75e..6d5a790474 100644 --- a/requirements/edx/base.in +++ b/requirements/edx/base.in @@ -123,7 +123,6 @@ openedx-calc # Library supporting mathematical calculatio ora2 piexif # Exif image metadata manipulation, used in the profile_images app Pillow # Image manipulation library; used for course assets, profile images, invoice PDFs, etc. -PyContracts pycountry pycryptodomex pygments # Used to support colors in paver command output diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index e86941ce0d..061c9f485f 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -143,8 +143,6 @@ ddt==1.4.2 # via # xblock-drag-and-drop-v2 # xblock-poll -decorator==5.0.9 - # via pycontracts defusedxml==0.7.1 # via # -r requirements/edx/base.in @@ -390,7 +388,7 @@ edx-analytics-data-api-client==0.17.0 # via -r requirements/edx/base.in edx-api-doc-tools==1.4.0 # via -r requirements/edx/base.in -edx-bulk-grades==0.8.11 +edx-bulk-grades==0.8.12 # via # -r requirements/edx/base.in # staff-graded-xblock @@ -486,7 +484,7 @@ edx-toggles==4.1.0 # edx-event-routing-backends # edxval # ora2 -edx-user-state-client==1.3.1 +edx-user-state-client==1.3.2 # via -r requirements/edx/base.in edx-when==2.0.0 # via @@ -494,7 +492,7 @@ edx-when==2.0.0 # edx-proctoring edxval==2.0.3 # via -r requirements/edx/base.in -elasticsearch==7.13.2 +elasticsearch==7.13.1 # via edx-search enmerkar-underscore==2.0.0 # via -r requirements/edx/base.in @@ -521,7 +519,6 @@ future==0.18.2 # django-ses # edx-celeryutils # edx-enterprise - # pycontracts # pyjwkest geoip2==3.0.0 # via @@ -595,7 +592,9 @@ libsass==0.10.0 loremipsum==1.0.5 # via ora2 lti-consumer-xblock==2.10.1 - # via -r requirements/edx/base.in + # via + # -c requirements/edx/../constraints.txt + # -r requirements/edx/base.in lxml==4.5.0 # via # -c requirements/edx/../constraints.txt @@ -651,7 +650,7 @@ mpmath==1.2.1 # via sympy mysqlclient==2.0.3 # via -r requirements/edx/base.in -newrelic==6.4.1.158 +newrelic==6.4.2.159 # via # -r requirements/edx/base.in # edx-django-utils @@ -661,7 +660,7 @@ nltk==3.6.2 # chem nodeenv==1.6.0 # via -r requirements/edx/base.in -numpy==1.20.3 +numpy==1.21.0 # via # chem # openedx-calc @@ -676,7 +675,7 @@ oauthlib==3.0.1 # social-auth-core openedx-calc==2.0.1 # via -r requirements/edx/base.in -ora2==3.6.6 +ora2==3.6.7 # via -r requirements/edx/base.in packaging==20.9 # via @@ -713,10 +712,6 @@ psutil==5.8.0 # via # -r requirements/edx/paver.txt # edx-django-utils -pycontracts==1.8.12 - # via - # -r requirements/edx/base.in - # edx-user-state-client pycountry==20.7.3 # via -r requirements/edx/base.in pycparser==2.20 @@ -761,7 +756,6 @@ pyparsing==2.4.7 # chem # openedx-calc # packaging - # pycontracts pysrt==1.1.2 # via # -r requirements/edx/base.in @@ -919,7 +913,6 @@ six==1.16.0 # isodate # libsass # paver - # pycontracts # pyjwkest # python-dateutil # python-memcached diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 06fda97f0c..c8e721c981 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -195,10 +195,6 @@ ddt==1.4.2 # -r requirements/edx/testing.txt # xblock-drag-and-drop-v2 # xblock-poll -decorator==5.0.9 - # via - # -r requirements/edx/testing.txt - # pycontracts defusedxml==0.7.1 # via # -r requirements/edx/testing.txt @@ -476,7 +472,7 @@ edx-analytics-data-api-client==0.17.0 # via -r requirements/edx/testing.txt edx-api-doc-tools==1.4.0 # via -r requirements/edx/testing.txt -edx-bulk-grades==0.8.11 +edx-bulk-grades==0.8.12 # via # -r requirements/edx/testing.txt # staff-graded-xblock @@ -582,7 +578,7 @@ edx-toggles==4.1.0 # edx-event-routing-backends # edxval # ora2 -edx-user-state-client==1.3.1 +edx-user-state-client==1.3.2 # via -r requirements/edx/testing.txt edx-when==2.0.0 # via @@ -590,7 +586,7 @@ edx-when==2.0.0 # edx-proctoring edxval==2.0.3 # via -r requirements/edx/testing.txt -elasticsearch==7.13.2 +elasticsearch==7.13.1 # via # -r requirements/edx/testing.txt # edx-search @@ -612,7 +608,7 @@ execnet==1.9.0 # pytest-xdist factory-boy==3.2.0 # via -r requirements/edx/testing.txt -faker==8.8.1 +faker==8.8.2 # via # -r requirements/edx/testing.txt # factory-boy @@ -641,7 +637,6 @@ future==0.18.2 # django-ses # edx-celeryutils # edx-enterprise - # pycontracts # pyjwkest geoip2==3.0.0 # via @@ -776,7 +771,9 @@ loremipsum==1.0.5 # -r requirements/edx/testing.txt # ora2 lti-consumer-xblock==2.10.1 - # via -r requirements/edx/testing.txt + # via + # -c requirements/edx/../constraints.txt + # -r requirements/edx/testing.txt lxml==4.5.0 # via # -c requirements/edx/../constraints.txt @@ -852,11 +849,11 @@ mpmath==1.2.1 # sympy mypy-extensions==0.4.3 # via mypy -mypy==0.902 +mypy==0.910 # via -r requirements/edx/development.in mysqlclient==2.0.3 # via -r requirements/edx/testing.txt -newrelic==6.4.1.158 +newrelic==6.4.2.159 # via # -r requirements/edx/testing.txt # edx-django-utils @@ -866,7 +863,7 @@ nltk==3.6.2 # chem nodeenv==1.6.0 # via -r requirements/edx/testing.txt -numpy==1.20.3 +numpy==1.21.0 # via # -r requirements/edx/testing.txt # chem @@ -882,7 +879,7 @@ oauthlib==3.0.1 # social-auth-core openedx-calc==2.0.1 # via -r requirements/edx/testing.txt -ora2==3.6.6 +ora2==3.6.7 # via -r requirements/edx/testing.txt packaging==20.9 # via @@ -922,7 +919,7 @@ pillow==8.2.0 # -r requirements/edx/testing.txt # edx-enterprise # edx-organizations -pip-tools==6.1.0 +pip-tools==6.2.0 # via -r requirements/edx/pip-tools.txt pluggy==0.13.1 # via @@ -947,10 +944,6 @@ py==1.10.0 # tox pycodestyle==2.7.0 # via -r requirements/edx/testing.txt -pycontracts==1.8.12 - # via - # -r requirements/edx/testing.txt - # edx-user-state-client pycountry==20.7.3 # via -r requirements/edx/testing.txt pycparser==2.20 @@ -1025,7 +1018,6 @@ pyparsing==2.4.7 # chem # openedx-calc # packaging - # pycontracts pyquery==1.4.3 # via -r requirements/edx/testing.txt pyrsistent==0.17.3 @@ -1254,7 +1246,6 @@ six==1.16.0 # jsonschema # libsass # paver - # pycontracts # pyjwkest # python-dateutil # python-memcached @@ -1441,6 +1432,10 @@ webob==1.8.7 # -r requirements/edx/testing.txt # xblock # xmodule +wheel==0.36.2 + # via + # -r requirements/edx/pip-tools.txt + # pip-tools wrapt==1.11.2 # via # -c requirements/edx/../constraints.txt diff --git a/requirements/edx/pip-tools.txt b/requirements/edx/pip-tools.txt index ac0b789500..76c71f6bfe 100644 --- a/requirements/edx/pip-tools.txt +++ b/requirements/edx/pip-tools.txt @@ -10,10 +10,13 @@ click==7.1.2 # pip-tools pep517==0.10.0 # via pip-tools -pip-tools==6.1.0 +pip-tools==6.2.0 # via -r requirements/edx/pip-tools.in toml==0.10.2 # via pep517 +wheel==0.36.2 + # via pip-tools # The following packages are considered to be unsafe in a requirements file: # pip +# setuptools diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 32c36f5b6d..eace1905f4 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -188,10 +188,6 @@ ddt==1.4.2 # -r requirements/edx/testing.in # xblock-drag-and-drop-v2 # xblock-poll -decorator==5.0.9 - # via - # -r requirements/edx/base.txt - # pycontracts defusedxml==0.7.1 # via # -r requirements/edx/base.txt @@ -461,7 +457,7 @@ edx-analytics-data-api-client==0.17.0 # via -r requirements/edx/base.txt edx-api-doc-tools==1.4.0 # via -r requirements/edx/base.txt -edx-bulk-grades==0.8.11 +edx-bulk-grades==0.8.12 # via # -r requirements/edx/base.txt # staff-graded-xblock @@ -566,7 +562,7 @@ edx-toggles==4.1.0 # edx-event-routing-backends # edxval # ora2 -edx-user-state-client==1.3.1 +edx-user-state-client==1.3.2 # via -r requirements/edx/base.txt edx-when==2.0.0 # via @@ -574,7 +570,7 @@ edx-when==2.0.0 # edx-proctoring edxval==2.0.3 # via -r requirements/edx/base.txt -elasticsearch==7.13.2 +elasticsearch==7.13.1 # via # -r requirements/edx/base.txt # edx-search @@ -594,7 +590,7 @@ execnet==1.9.0 # via pytest-xdist factory-boy==3.2.0 # via -r requirements/edx/testing.in -faker==8.8.1 +faker==8.8.2 # via factory-boy filelock==3.0.12 # via @@ -620,7 +616,6 @@ future==0.18.2 # django-ses # edx-celeryutils # edx-enterprise - # pycontracts # pyjwkest geoip2==3.0.0 # via @@ -743,7 +738,9 @@ loremipsum==1.0.5 # -r requirements/edx/base.txt # ora2 lti-consumer-xblock==2.10.1 - # via -r requirements/edx/base.txt + # via + # -c requirements/edx/../constraints.txt + # -r requirements/edx/base.txt lxml==4.5.0 # via # -c requirements/edx/../constraints.txt @@ -814,7 +811,7 @@ mpmath==1.2.1 # sympy mysqlclient==2.0.3 # via -r requirements/edx/base.txt -newrelic==6.4.1.158 +newrelic==6.4.2.159 # via # -r requirements/edx/base.txt # edx-django-utils @@ -824,7 +821,7 @@ nltk==3.6.2 # chem nodeenv==1.6.0 # via -r requirements/edx/base.txt -numpy==1.20.3 +numpy==1.21.0 # via # -r requirements/edx/base.txt # chem @@ -840,7 +837,7 @@ oauthlib==3.0.1 # social-auth-core openedx-calc==2.0.1 # via -r requirements/edx/base.txt -ora2==3.6.6 +ora2==3.6.7 # via -r requirements/edx/base.txt packaging==20.9 # via @@ -898,10 +895,6 @@ py==1.10.0 # tox pycodestyle==2.7.0 # via -r requirements/edx/testing.in -pycontracts==1.8.12 - # via - # -r requirements/edx/base.txt - # edx-user-state-client pycountry==20.7.3 # via -r requirements/edx/base.txt pycparser==2.20 @@ -970,7 +963,6 @@ pyparsing==2.4.7 # chem # openedx-calc # packaging - # pycontracts pyquery==1.4.3 # via -r requirements/edx/testing.in pysrt==1.1.2 @@ -1189,7 +1181,6 @@ six==1.16.0 # isodate # libsass # paver - # pycontracts # pyjwkest # python-dateutil # python-memcached