fix: Remove pylint constraint and fix warnings (#28646)

This commit is contained in:
Usama Sadiq
2021-10-20 23:00:13 +05:00
committed by GitHub
parent 4854af6336
commit 9ee8df0980
42 changed files with 95 additions and 84 deletions

View File

@@ -1,7 +1,6 @@
"""
Views related to operations on course objects
"""
# pylint: disable=filter-builtin-not-iterating
import copy

View File

@@ -109,7 +109,7 @@ class TestCourseEntitlementModelHelpers(ModuleStoreTestCase):
)
assert not CourseEnrollment.is_enrolled(user=self.user, course_key=new_course.id)
except AttributeError as error:
self.fail(error.message) # lint-amnesty, pylint: disable=no-member, exception-message-attribute
self.fail(error.message) # lint-amnesty, pylint: disable=no-member
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')

View File

@@ -54,7 +54,7 @@ class Command(BaseCommand): # lint-amnesty, pylint: disable=missing-class-docst
print("Groups", groups)
# Confirm group probabilities add up to 1
total = sum(zip(*groups)[1]) # lint-amnesty, pylint: disable=unsubscriptable-object, zip-builtin-not-iterating
total = sum(zip(*groups)[1]) # lint-amnesty, pylint: disable=unsubscriptable-object
print("Total:", total)
if abs(total - 1) > 0.01:
print("Total not 1")

View File

@@ -94,7 +94,7 @@ class SAMLAuthBackend(SAMLAuth): # pylint: disable=abstract-method
log.warning(
'[THIRD_PARTY_AUTH] Error in SAML authentication flow. '
'Provider: {idp_name}, Message: {message}'.format(
message=ex.message, # lint-amnesty, pylint: disable=no-member, exception-message-attribute
message=ex.message, # lint-amnesty, pylint: disable=no-member
idp_name=response.get('idp_name')
)
)

View File

@@ -35,7 +35,7 @@ def decompress_string(value):
"""
try:
val = value.encode('utf').decode('base64') # pylint: disable=invalid-str-codec
val = value.encode('utf').decode('base64')
zbuf = BytesIO(val)
zfile = gzip.GzipFile(fileobj=zbuf)
ret = zfile.read()
@@ -60,7 +60,7 @@ class CompressedTextField(CreatorMixin, models.TextField):
if isinstance(value, str):
value = value.encode('utf8')
value = compress_string(value)
value = value.encode('base64').decode('utf8') # pylint: disable=invalid-str-codec
value = value.encode('base64').decode('utf8')
return value
def to_python(self, value):

View File

@@ -285,4 +285,4 @@ class CourseAssetsFromStorage:
"""
Iterates over the items of the asset dict. (Python 3 naming convention)
"""
return self.iteritems() # lint-amnesty, pylint: disable=dict-iter-method
return self.iteritems()

View File

@@ -324,7 +324,7 @@ class MongoContentStore(ContentStore):
count = 0
assets = []
try:
result = cursor.next() # lint-amnesty, pylint: disable=next-method-called
result = cursor.next()
if result:
count = result['count']
assets = list(result['results'])

View File

@@ -103,7 +103,7 @@ edx_xml_parser = etree.XMLParser(dtd_validation=False, load_dtd=False,
_cached_toc = {}
class Textbook: # lint-amnesty, pylint: disable=missing-class-docstring, eq-without-hash
class Textbook: # lint-amnesty, pylint: disable=missing-class-docstring
def __init__(self, title, book_url):
self.title = title
self.book_url = book_url

View File

@@ -20,7 +20,7 @@ from xmodule.util.misc import get_short_labeler
log = logging.getLogger("edx.courseware")
class ScoreBase(metaclass=abc.ABCMeta): # pylint: disable=eq-without-hash
class ScoreBase(metaclass=abc.ABCMeta):
"""
Abstract base class for encapsulating fields of values scores.
"""

View File

@@ -328,7 +328,7 @@ class BulkOperationsMixin:
bulk_ops_record.has_library_updated_item = False
class EditInfo: # pylint: disable=eq-without-hash
class EditInfo:
"""
Encapsulates the editing info of a block.
"""
@@ -407,7 +407,7 @@ class EditInfo: # pylint: disable=eq-without-hash
return not self == edit_info
class BlockData: # pylint: disable=eq-without-hash
class BlockData:
"""
Wrap the block data in an object instead of using a straight Python dictionary.
Allows the storing of meta-information about a structure that doesn't persist along with

View File

@@ -1291,7 +1291,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
])
courses = self.collection.find(course_search_location, projection={'_id': True})
try:
course = courses.next() # lint-amnesty, pylint: disable=next-method-called
course = courses.next()
raise DuplicateCourseError(course_id, course['_id'])
except StopIteration:
pass

View File

@@ -78,7 +78,7 @@ def _get_dynamic_partitions(course):
Return the dynamic user partitions for this course.
If none exists, returns an empty array.
"""
dynamic_partition_generators = DynamicPartitionGeneratorsPluginManager.get_available_plugins().values() # lint-amnesty, pylint: disable=dict-values-not-iterating
dynamic_partition_generators = DynamicPartitionGeneratorsPluginManager.get_available_plugins().values()
generated_partitions = []
for generator in dynamic_partition_generators:
generated_partition = generator(course)

View File

@@ -9,7 +9,7 @@ frac() and __str__().
import numbers
class Progress: # pylint: disable=eq-without-hash
class Progress:
'''Represents a progress of a/b (a out of b done)
a and b must be numeric, but not necessarily integer, with

View File

@@ -480,7 +480,7 @@ def _send_course_email(entry_id, email_id, to_list, global_email_context, subtas
task_id,
email_id
)
raise
raise exc
# Exclude optouts (if not a retry):
# Note that we don't have to do the optout logic at all if this is a retry,

View File

@@ -556,7 +556,7 @@ class CourseIdListViewTestCase(CourseApiTestViewMixin, ModuleStoreTestCase):
class LazyPageNumberPaginationTestCase(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def test_lazy_page_number_pagination(self):
number_sequence = range(20) # lint-amnesty, pylint: disable=range-builtin-not-iterating
number_sequence = range(20)
even_numbers_lazy_sequence = LazySequence(
(
number for number in number_sequence
@@ -585,7 +585,7 @@ class LazyPageNumberPaginationTestCase(TestCase): # lint-amnesty, pylint: disab
self.assertDictEqual(expected_response, paginated_response.data)
def test_not_found_error_for_invalid_page(self):
number_sequence = range(20) # lint-amnesty, pylint: disable=range-builtin-not-iterating
number_sequence = range(20)
even_numbers_lazy_sequence = LazySequence(
(
number for number in number_sequence

View File

@@ -8,7 +8,7 @@ from django.utils.translation import ugettext as _
from xmodule.course_metadata_utils import DEFAULT_START_DATE
class AccessResponse: # pylint: disable=eq-without-hash
class AccessResponse:
"""Class that represents a response from a has_access permission check."""
def __init__(self, has_access, error_code=None, developer_message=None, user_message=None,
additional_context_user_message=None, user_fragment=None):

View File

@@ -464,7 +464,7 @@ def get_course_date_blocks(course, user, request=None, include_access=False,
]
blocks.extend([cls(course, user) for cls in default_block_classes])
blocks = filter(lambda b: b.is_allowed and b.date and (include_past_dates or b.is_enabled), blocks) # lint-amnesty, pylint: disable=filter-builtin-not-iterating
blocks = filter(lambda b: b.is_allowed and b.date and (include_past_dates or b.is_enabled), blocks)
return sorted(blocks, key=date_block_key_fn)

View File

@@ -210,7 +210,7 @@ class HtmlTextbookTabs(TextbookTabsBase):
)
class LinkTab(CourseTab): # lint-amnesty, pylint: disable=eq-without-hash
class LinkTab(CourseTab):
"""
Abstract class for tabs that contain external links.
"""

View File

@@ -241,7 +241,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
blocks = get_course_date_blocks(course, user, request, num_assignments=2)
assert len(blocks) == len(expected_blocks)
assert {type(b) for b in blocks} == set(expected_blocks)
assignment_blocks = filter( # pylint: disable=filter-builtin-not-iterating
assignment_blocks = filter(
lambda b: isinstance(b, CourseAssignmentDate), blocks
)
for assignment in assignment_blocks:
@@ -267,7 +267,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
blocks = get_course_date_blocks(course, user, request, include_past_dates=True)
assert len(blocks) == len(expected_blocks)
assert {type(b) for b in blocks} == set(expected_blocks)
assignment_blocks = filter( # pylint: disable=filter-builtin-not-iterating
assignment_blocks = filter(
lambda b: isinstance(b, CourseAssignmentDate), blocks
)
for assignment in assignment_blocks:

View File

@@ -280,7 +280,7 @@ def make_mock_request_impl( # lint-amnesty, pylint: disable=missing-function-do
return mock_request_impl
class StringEndsWithMatcher: # lint-amnesty, pylint: disable=missing-class-docstring,eq-without-hash
class StringEndsWithMatcher: # lint-amnesty, pylint: disable=missing-class-docstring
def __init__(self, suffix):
self.suffix = suffix
@@ -288,7 +288,7 @@ class StringEndsWithMatcher: # lint-amnesty, pylint: disable=missing-class-docs
return other.endswith(self.suffix)
class PartialDictMatcher: # lint-amnesty, pylint: disable=missing-class-docstring,eq-without-hash
class PartialDictMatcher: # lint-amnesty, pylint: disable=missing-class-docstring
def __init__(self, expected_values):
self.expected_values = expected_values
@@ -445,7 +445,7 @@ class SingleThreadTestCase(ForumsEnableMixin, ModuleStoreTestCase): # lint-amne
assert 'This is a private discussion. You do not have permissions to view this discussion' in html
class AllowPlusOrMinusOneInt(int): # pylint: disable=eq-without-hash
class AllowPlusOrMinusOneInt(int):
"""
A workaround for the fact that assertNumQueries doesn't let you
specify a range or any tolerance. An 'int' that is 'equal to' its value,

View File

@@ -796,7 +796,7 @@ class EnrollmentObjects:
self.cea = cea
class SettableEnrollmentState(EmailEnrollmentState): # lint-amnesty, pylint: disable=eq-without-hash
class SettableEnrollmentState(EmailEnrollmentState):
"""
Settable enrollment state.
Used for testing state changes.

View File

@@ -32,7 +32,7 @@ class AlreadyRunningError(Exception):
def __init__(self, message=None):
if not message:
message = self.message # pylint: disable=exception-message-attribute
message = self.message
super().__init__(message)
@@ -44,7 +44,7 @@ class QueueConnectionError(Exception):
def __init__(self, message=None):
if not message:
message = self.message # pylint: disable=exception-message-attribute
message = self.message
super().__init__(message)

View File

@@ -770,7 +770,7 @@ class UserProgramReadOnlyAccessView(DeveloperErrorViewMixin, PaginatedAPIView):
if course_run_program and course_run_program.get('type').lower() == program_type_filter:
program_dict[course_run_program['uuid']] = course_run_program
return program_dict.values() # lint-amnesty, pylint: disable=dict-values-not-iterating
return program_dict.values()
class UserProgramCourseEnrollmentView(

View File

@@ -152,7 +152,7 @@ def course_info_to_ccxcon(course_key):
if resp.status_code >= 400:
log.error("Error creating course on ccxcon. Status: %s, Content: %s", resp.status_code, resp.content)
# this API performs a POST request both for POST and PATCH, but the POST returns 201 and the PATCH returns 200
elif resp.status_code != HTTP_200_OK and resp.status_code != HTTP_201_CREATED:
elif resp.status_code not in (HTTP_200_OK, HTTP_201_CREATED):
log.error('Server returned unexpected status code %s', resp.status_code)
else:
log.debug('Request successful. Status: %s, Content: %s', resp.status_code, resp.content)

View File

@@ -179,7 +179,7 @@ class LibraryBundle:
Get the set of usage keys in this bundle that have no parent.
"""
own_usage_keys = self.get_all_usages()
usage_keys_with_parents = self.get_bundle_includes().keys() # lint-amnesty, pylint: disable=dict-keys-not-iterating
usage_keys_with_parents = self.get_bundle_includes().keys()
return [usage_key for usage_key in own_usage_keys if usage_key not in usage_keys_with_parents]
def get_bundle_includes(self):

View File

@@ -74,7 +74,7 @@ class Features(Enum):
self.feature_support_type = feature_support_type
@property
def value(self):
def value(self): # pylint: disable=invalid-overridden-method
return self.feature_id
@property

View File

@@ -17,7 +17,7 @@ class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): # lint-amnes
# An iterable of day offsets (e.g. -7, -14, -21, -28, ...) that defines the days for
# which emails are sent out, relative to the 'date' parameter
offsets = range(-7, -77, -7) # lint-amnesty, pylint: disable=range-builtin-not-iterating
offsets = range(-7, -77, -7)
def add_arguments(self, parser):
parser.add_argument(
@@ -42,7 +42,7 @@ class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): # lint-amnes
num_weeks = options.get('weeks')
if num_weeks:
num_days = (7 * num_weeks) + 1
self.offsets = range(-7, -num_days, -7) # lint-amnesty, pylint: disable=range-builtin-not-iterating
self.offsets = range(-7, -num_days, -7)
current_date = datetime.datetime(
*[int(x) for x in options['date'].split('-')],

View File

@@ -546,7 +546,7 @@ class DeactivateLogoutView(APIView):
ace.send(notification)
except Exception as exc:
log.exception('Error sending out deletion notification email')
raise
raise exc
# Log the user out.
logout(request)

View File

@@ -145,7 +145,7 @@ class Command(BaseCommand):
return len(contacts)
except (HttpClientError, HttpServerError) as ex:
message = 'An error occurred while syncing batch of contacts for site {domain}, {message}'.format(
domain=site_conf.site.domain, message=ex.message # lint-amnesty, pylint: disable=no-member, exception-message-attribute
domain=site_conf.site.domain, message=ex.message # lint-amnesty, pylint: disable=no-member
)
self.stderr.write(message)
return 0

View File

@@ -423,7 +423,7 @@ class RegistrationFormFactory:
FormDescription.FIELD_TYPE_MAP.get(field.__class__))
if not field_type:
raise ImproperlyConfigured(
u"Field type '{}' not recognized for registration extension field '{}'.".format(
"Field type '{}' not recognized for registration extension field '{}'.".format(
field_type,
field_name
)

View File

@@ -660,12 +660,12 @@ class RegistrationViewTestV1(
self._assert_reg_absent_field(
no_extra_fields_setting,
{
"name": u"favorite_editor",
"type": u"select",
"name": "favorite_editor",
"type": "select",
"required": False,
"label": u"Favorite Editor",
"placeholder": u"cat",
"defaultValue": u"vim",
"label": "Favorite Editor",
"placeholder": "cat",
"defaultValue": "vim",
"errorMessages": {
'required': 'This field is required.',
'invalid_choice': 'Select a valid choice. %(value)s is not one of the available choices.',

View File

@@ -262,7 +262,7 @@ def get_bundle_draft_direct_links_cached(bundle_uuid, draft_name):
cache_key = ('bundle_draft_direct_links', )
result = bundle_cache.get(cache_key)
if result is None:
links = blockstore_api.get_bundle_links(bundle_uuid, use_draft=draft_name).values() # lint-amnesty, pylint: disable=dict-values-not-iterating
links = blockstore_api.get_bundle_links(bundle_uuid, use_draft=draft_name).values()
result = {link.name: link.direct for link in links}
bundle_cache.set(cache_key, result)
return result

View File

@@ -69,7 +69,7 @@ def dump_js_escaped_json(obj, cls=EdxJSONEncoder):
(string) Escaped encoded JSON.
"""
obj = list(obj) if isinstance(obj, type({}.values())) else obj # lint-amnesty, pylint: disable=isinstance-second-argument-not-valid-type, dict-values-not-iterating, line-too-long
obj = list(obj) if isinstance(obj, type({}.values())) else obj # lint-amnesty, pylint: disable=isinstance-second-argument-not-valid-type, line-too-long
json_string = json.dumps(obj, ensure_ascii=True, cls=cls)
json_string = _escape_json_for_js(json_string)
return json_string

View File

@@ -201,7 +201,7 @@ def update_bundle(bundle_uuid, **fields):
if "collection_uuid" in fields:
data["collection_uuid"] = str(fields.pop("collection_uuid"))
if fields:
raise ValueError(f"Unexpected extra fields passed " # pylint: disable=dict-keys-not-iterating
raise ValueError(f"Unexpected extra fields passed "
f"to update_bundle: {fields.keys()}")
result = api_request('patch', api_url('bundles', str(bundle_uuid)), json=data)
return _bundle_from_response(result)
@@ -323,7 +323,7 @@ def get_bundle_files(bundle_uuid, use_draft=None):
"""
Get an iterator over all the files in the specified bundle or draft.
"""
return get_bundle_files_dict(bundle_uuid, use_draft).values() # lint-amnesty, pylint: disable=dict-values-not-iterating
return get_bundle_files_dict(bundle_uuid, use_draft).values()
def get_bundle_links(bundle_uuid, use_draft=None):

View File

@@ -14,7 +14,7 @@ MANAGED_TEAM_MAX_TEAM_SIZE = 200
DEFAULT_COURSE_RUN_MAX_TEAM_SIZE = 50
class TeamsConfig: # pylint: disable=eq-without-hash
class TeamsConfig:
"""
Configuration for the Course Teams feature on a course run.
@@ -155,7 +155,7 @@ class TeamsConfig: # pylint: disable=eq-without-hash
return self.default_max_team_size
class TeamsetConfig: # pylint: disable=eq-without-hash
class TeamsetConfig:
"""
Configuration for a team-set within a course run.

View File

@@ -93,7 +93,7 @@ def get_current_update_for_user(request, course):
return None
update_hash = _calculate_update_hash(updates[0])
if update_hash in dismissed_hashes:
if update_hash in dismissed_hashes: # pylint: disable=unsupported-membership-test
return None
return updates[0]['content']

View File

@@ -60,7 +60,7 @@ def lms_enroll_user_in_course(
)
log.info('The user [%s] has been enrolled in course run [%s].', username, course_id)
return response
except CourseEnrollmentExistsError as error:
except CourseEnrollmentExistsError as error: # pylint: disable=unused-variable
log.warning('An enrollment already exists for user [%s] in course run [%s].', username, course_id)
return None
except CourseEnrollmentError as error:

View File

@@ -72,10 +72,10 @@ persistent = yes
load-plugins = edx_lint.pylint,pylint_django_settings,pylint_django,pylint_celery,pylint_pytest
[MESSAGES CONTROL]
enable =
enable =
blacklisted-name,
line-too-long,
abstract-class-instantiated,
abstract-method,
access-member-before-definition,
@@ -212,7 +212,7 @@ enable =
using-constant-test,
yield-outside-function,
zip-builtin-not-iterating,
astroid-error,
django-not-available-placeholder,
django-not-available,
@@ -220,20 +220,20 @@ enable =
method-check-failed,
parse-error,
raw-checker-failed,
empty-docstring,
invalid-characters-in-docstring,
missing-docstring,
wrong-spelling-in-comment,
wrong-spelling-in-docstring,
unused-argument,
unused-import,
unused-variable,
eval-used,
exec-used,
bad-classmethod-argument,
bad-mcs-classmethod-argument,
bad-mcs-method-argument,
@@ -271,31 +271,31 @@ enable =
unneeded-not,
useless-else-on-loop,
wrong-assert-type,
deprecated-method,
deprecated-module,
too-many-boolean-expressions,
too-many-nested-blocks,
too-many-statements,
wildcard-import,
wrong-import-order,
wrong-import-position,
missing-final-newline,
mixed-indentation,
mixed-line-endings,
trailing-newlines,
trailing-whitespace,
unexpected-line-ending-format,
bad-inline-option,
bad-option-value,
deprecated-pragma,
unrecognized-inline-option,
useless-suppression,
cmp-method,
coerce-method,
delslice-method,
@@ -312,7 +312,7 @@ enable =
rdiv-method,
setslice-method,
using-cmp-argument,
disable =
disable =
bad-continuation,
bad-indentation,
consider-using-f-string,
@@ -342,10 +342,10 @@ disable =
unspecified-encoding,
unused-wildcard-import,
use-maxsplit-arg,
feature-toggle-needs-doc,
illegal-waffle-usage,
apply-builtin,
backtick,
bad-python3-import,
@@ -383,7 +383,7 @@ disable =
unicode-builtin,
unpacking-in-except,
xrange-builtin,
logging-fstring-interpolation,
native-string,
import-outside-toplevel,
@@ -447,7 +447,7 @@ ignore-imports = no
ignore-mixin-members = yes
ignored-classes = SQLObject
unsafe-load-any-extension = yes
generated-members =
generated-members =
REQUEST,
acl_users,
aq_parent,
@@ -473,7 +473,7 @@ generated-members =
[VARIABLES]
init-import = no
dummy-variables-rgx = _|dummy|unused|.*_unused
additional-builtins =
additional-builtins =
[CLASSES]
defining-attr-methods = __init__,__new__,setUp
@@ -494,9 +494,9 @@ max-public-methods = 20
[IMPORTS]
deprecated-modules = regsub,TERMIOS,Bastion,rexec
import-graph =
ext-import-graph =
int-import-graph =
import-graph =
ext-import-graph =
int-import-graph =
[EXCEPTIONS]
overgeneral-exceptions = Exception

View File

@@ -24,6 +24,9 @@ disable+ =
consider-using-enumerate,
no-member,
consider-using-with,
unspecified-encoding,
unused-variable,
unsubscriptable-object,
[BASIC]
attr-rgx = [a-z_][a-z0-9_]{2,40}$

View File

@@ -90,10 +90,6 @@ click<8.0.0
# requirements of edx-platform. Pinning temporarily until this is resolved in edx-val.
edxval<2.1
# pylint>=2.10.0 introduced a lot of new pylint warnings.
# A separate PR will be needed to remove the pin and fix all the pylint warnings
pylint<2.10.0
# At the time of writing this comment, we do not know whether py2neo>=2022
# will support our currently-deployed Neo4j version (3.5).
# Feel free to loosen this constraint if/when it is confirmed that a later

View File

@@ -63,7 +63,7 @@ asgiref==3.4.1
# -r requirements/edx/testing.txt
# django
# uvicorn
astroid==2.6.6
astroid==2.8.0
# via
# -r requirements/edx/testing.txt
# pylint
@@ -969,6 +969,7 @@ pip-tools==6.4.0
platformdirs==2.4.0
# via
# -r requirements/edx/testing.txt
# pylint
# virtualenv
pluggy==1.0.0
# via
@@ -1039,9 +1040,8 @@ pylatexenc==2.10
# via
# -r requirements/edx/testing.txt
# olxcleaner
pylint==2.9.6
pylint==2.11.1
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/testing.txt
# edx-lint
# pylint-celery
@@ -1440,9 +1440,15 @@ typing-extensions==3.10.0.2
# via
# -r requirements/edx/testing.txt
# aiohttp
# astroid
# gitpython
# mypy
# pydantic
# pylint
ua-parser==0.10.0
# via
# -r requirements/edx/testing.txt
# django-cookies-samesite
unicodecsv==0.14.1
# via
# -r requirements/edx/testing.txt

View File

@@ -59,7 +59,7 @@ asgiref==3.4.1
# -r requirements/edx/base.txt
# django
# uvicorn
astroid==2.6.6
astroid==2.8.0
# via
# pylint
# pylint-celery
@@ -909,7 +909,9 @@ pillow==8.4.0
# edx-enterprise
# edx-organizations
platformdirs==2.4.0
# via virtualenv
# via
# pylint
# virtualenv
pluggy==1.0.0
# via
# -r requirements/edx/coverage.txt
@@ -977,9 +979,8 @@ pylatexenc==2.10
# via
# -r requirements/edx/base.txt
# olxcleaner
pylint==2.9.6
pylint==2.11.1
# via
# -c requirements/edx/../constraints.txt
# edx-lint
# pylint-celery
# pylint-django
@@ -1327,8 +1328,14 @@ typing-extensions==3.10.0.2
# via
# -r requirements/edx/base.txt
# aiohttp
# astroid
# gitpython
# pydantic
# pylint
ua-parser==0.10.0
# via
# -r requirements/edx/base.txt
# django-cookies-samesite
unicodecsv==0.14.1
# via
# -r requirements/edx/base.txt