From f5d8b6c991c0acf1923f95fcebed1458aa46eca0 Mon Sep 17 00:00:00 2001 From: Arslan Ashraf <34372316+arslanashraf7@users.noreply.github.com> Date: Thu, 18 Feb 2021 11:54:26 +0500 Subject: [PATCH] Upgrade Python to 3.9 (#4769) * Upgrade Python to 3.9 & pylint to 2.6.0 * Removed unnecessary pinned requirement cffi --- .github/workflows/ci.yml | 2 +- Dockerfile | 2 +- backends/edxorg.py | 2 +- backends/exceptions.py | 2 +- backends/pipeline_api_test.py | 2 +- backends/utils_test.py | 2 +- cms/models.py | 10 ++++++---- courses/models.py | 4 ++-- courses/models_test.py | 2 +- courses/views.py | 2 +- courses/views_test.py | 4 ++-- dashboard/api_test.py | 2 +- dashboard/apps.py | 2 +- dashboard/models.py | 2 +- dashboard/views_test.py | 4 ++-- discussions/apps.py | 2 +- discussions/views.py | 2 +- ecommerce/models.py | 2 +- ecommerce/views.py | 6 +++--- exams/apps.py | 2 +- exams/utils.py | 2 +- financialaid/admin.py | 4 ++-- financialaid/models.py | 6 +++--- financialaid/tasks.py | 2 +- financialaid/tasks_test.py | 2 +- financialaid/views.py | 4 ++-- grades/admin.py | 8 ++++---- grades/apps.py | 2 +- grades/factories.py | 10 +++++----- grades/models.py | 6 +++--- mail/admin.py | 2 +- mail/api.py | 2 +- mail/views.py | 2 +- profiles/api_test.py | 2 +- profiles/apps.py | 2 +- profiles/models.py | 4 ++-- profiles/permissions.py | 2 +- profiles/permissions_test.py | 2 +- profiles/serializers.py | 3 +-- profiles/util_test.py | 2 +- profiles/views_test.py | 6 +++--- pylintrc | 2 +- requirements.in | 2 +- requirements.txt | 16 ++++++++-------- roles/apps.py | 2 +- roles/models.py | 8 ++++---- roles/models_test.py | 2 +- roles/signals_test.py | 2 +- runtime.txt | 2 +- search/apps.py | 2 +- search/permissions_test.py | 2 +- search/views.py | 2 +- search/views_test.py | 2 +- seed_data/lib.py | 7 +++---- test_requirements.txt | 6 +++--- tox.ini | 2 +- ui/views.py | 6 +++--- ui/views_test.py | 2 +- 58 files changed, 100 insertions(+), 100 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13b8a8be38..72468a6d80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: "3.7" + python-version: "3.9.1" - id: cache uses: actions/cache@v1 diff --git a/Dockerfile b/Dockerfile index fa14b475cb..13fd1cd7b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7-buster +FROM python:3.9-buster LABEL maintainer "ODL DevOps " diff --git a/backends/edxorg.py b/backends/edxorg.py index 8a5b24d230..5f4bec46a6 100644 --- a/backends/edxorg.py +++ b/backends/edxorg.py @@ -103,6 +103,6 @@ def refresh_token(self, token, *args, **kwargs): Returns: dict of information about the user """ - response = super(EdxOrgOAuth2, self).refresh_token(token, *args, **kwargs) + response = super().refresh_token(token, *args, **kwargs) response['updated_at'] = now_in_utc().timestamp() return response diff --git a/backends/exceptions.py b/backends/exceptions.py index 74ef0a5624..43384cbf9d 100644 --- a/backends/exceptions.py +++ b/backends/exceptions.py @@ -6,5 +6,5 @@ class InvalidCredentialStored(Exception): """Custom exception to throw in some specific situations""" def __init__(self, message, http_status_code): - super(InvalidCredentialStored, self).__init__(message) + super().__init__(message) self.http_status_code = http_status_code diff --git a/backends/pipeline_api_test.py b/backends/pipeline_api_test.py index 3ac7318aa0..b0c4df394a 100644 --- a/backends/pipeline_api_test.py +++ b/backends/pipeline_api_test.py @@ -69,7 +69,7 @@ def setUp(self): """ Set up class """ - super(EdxPipelineApiTest, self).setUp() + super().setUp() self.user = UserFactory(username="user_1") self.user.social_auth.create( provider='not_edx', diff --git a/backends/utils_test.py b/backends/utils_test.py index 7df18c7e04..993bdb1ac9 100644 --- a/backends/utils_test.py +++ b/backends/utils_test.py @@ -39,7 +39,7 @@ def setUpTestData(cls): ) def setUp(self): - super(RefreshTest, self).setUp() + super().setUp() self.now = now_in_utc() def update_social_extra_data(self, data): diff --git a/cms/models.py b/cms/models.py index 926dd6370d..3ab4a2d462 100644 --- a/cms/models.py +++ b/cms/models.py @@ -54,7 +54,7 @@ def get_context(self, request, *args, **kwargs): } username = get_social_username(request.user) - context = super(HomePage, self).get_context(request) + context = super().get_context(request) def get_program_page(program): """Return a None if ProgramPage does not exist, to avoid template errors""" @@ -248,7 +248,7 @@ class CourseTeamTabPage(ProgramChildPage): ] def get_context(self, request, *args, **kwargs): - context = super(CourseTeamTabPage, self).get_context(request) + context = super().get_context(request) # this will help us to disable instructors carousel on course team page context['course_team_tab_title'] = self.title @@ -392,6 +392,7 @@ def get_context(self, request, *args, **kwargs): course_team_page = child_pages.type(CourseTeamTabPage).live().first() # the course team tab should always be second, first one is about tab + # pylint: disable=unnecessary-comprehension context['child_pages'] = [course_team_page] + [page for page in child_pages.not_type(CourseTeamTabPage)] context['course_team_page'] = course_team_page @@ -415,6 +416,7 @@ def get_program_page_context(programpage, request): "program": ProgramPageSerializer(programpage).data, } username = get_social_username(request.user) + # pylint: disable=bad-super-call context = super(ProgramPage, programpage).get_context(request) context["is_staff"] = has_role(request.user, [Staff.ROLE_ID, Instructor.ROLE_ID]) @@ -538,7 +540,7 @@ class FrequentlyAskedQuestion(Orderable): answer = RichTextField() slug = models.SlugField(unique=True, default=None, blank=True) - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs if not self.slug: max_length = FrequentlyAskedQuestion._meta.get_field('slug').max_length slug = orig_slug = slugify(self.question)[:max_length] @@ -551,7 +553,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ slug_is_unique = not FrequentlyAskedQuestion.objects.filter(slug=slug).exists() count += 1 self.slug = slug - super(FrequentlyAskedQuestion, self).save(*args, **kwargs) + super().save(*args, **kwargs) panels = [ MultiFieldPanel( diff --git a/courses/models.py b/courses/models.py index f771e24711..4986f76bb8 100644 --- a/courses/models.py +++ b/courses/models.py @@ -204,11 +204,11 @@ class Meta: def __str__(self): return self.title - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """Overridden save method""" if not self.edx_course_key: self.edx_course_key = None - super(CourseRun, self).save(*args, **kwargs) + super().save(*args, **kwargs) @property def is_current(self): diff --git a/courses/models_test.py b/courses/models_test.py index 76bbf4615c..a0e28e7658 100644 --- a/courses/models_test.py +++ b/courses/models_test.py @@ -84,7 +84,7 @@ def setUpTestData(cls): cls.course = CourseFactory.create(title="Title") def setUp(self): - super(CourseModelTests, self).setUp() + super().setUp() self.now = now_in_utc() def create_run(self, course=None, start=None, end=None, diff --git a/courses/views.py b/courses/views.py index d6f31ed94e..053247f395 100644 --- a/courses/views.py +++ b/courses/views.py @@ -55,7 +55,7 @@ class ProgramLearnersView(APIView): ) serializer_class = ProfileImageSerializer - def get(self, request, *args, **kargs): + def get(self, request, *args, **kargs): # pylint: disable=unused-argument """ Get eight random learners with images and the total count of visible learners in the program diff --git a/courses/views_test.py b/courses/views_test.py index 9148734b78..b12c0806d0 100644 --- a/courses/views_test.py +++ b/courses/views_test.py @@ -129,7 +129,7 @@ def setUpTestData(cls): cls.url = reverse('user_program_enrollments') def setUp(self): - super(ProgramEnrollmentTests, self).setUp() + super().setUp() self.default_enrollments = [ ProgramEnrollmentFactory( user=self.user1, @@ -139,7 +139,7 @@ def setUp(self): self.client.force_login(self.user1) def tearDown(self): - super(ProgramEnrollmentTests, self).tearDown() + super().tearDown() ProgramEnrollment.objects.all().delete() def assert_program_enrollments_count(self, expected_count=None): diff --git a/dashboard/api_test.py b/dashboard/api_test.py index 2d31d2cb30..8d5ca52d73 100644 --- a/dashboard/api_test.py +++ b/dashboard/api_test.py @@ -139,7 +139,7 @@ def setUpTestData(cls): cls.user = UserFactory.create() def setUp(self): - super(CourseTests, self).setUp() + super().setUp() self.now = now_in_utc() self.mmtrack = MagicMock(wraps=MMTrack) diff --git a/dashboard/apps.py b/dashboard/apps.py index ade0db9b9e..c516152c2a 100644 --- a/dashboard/apps.py +++ b/dashboard/apps.py @@ -14,4 +14,4 @@ def ready(self): """ Ready handler. Import signals. """ - import dashboard.signals # pylint: disable=unused-variable + import dashboard.signals # pylint: disable=unused-import diff --git a/dashboard/models.py b/dashboard/models.py index 29c5844db1..b806df7965 100644 --- a/dashboard/models.py +++ b/dashboard/models.py @@ -226,7 +226,7 @@ class ProgramEnrollment(Model): class Meta: unique_together = (('user', 'program'), ) - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """Overridden save method""" if not self.hash: self.hash = generate_md5( diff --git a/dashboard/views_test.py b/dashboard/views_test.py index 1be1e18e64..bc57bdabf8 100644 --- a/dashboard/views_test.py +++ b/dashboard/views_test.py @@ -65,7 +65,7 @@ def setUpTestData(cls): cls.url = reverse('dashboard_api', args=[cls.user.social_auth.first().uid]) def setUp(self): - super(DashboardTest, self).setUp() + super().setUp() self.client.force_login(self.user) def test_anonym_access(self): @@ -144,7 +144,7 @@ def setUpTestData(cls): cls.url = reverse('dashboard_api', args=[cls.social_auth.uid]) def setUp(self): - super(DashboardTokensTest, self).setUp() + super().setUp() self.client.force_login(self.user) self.now = now_in_utc() diff --git a/discussions/apps.py b/discussions/apps.py index 9ee2cd36d7..fcfd74d168 100644 --- a/discussions/apps.py +++ b/discussions/apps.py @@ -10,4 +10,4 @@ def ready(self): """ Ready handler. Import signals. """ - import discussions.signals # pylint: disable=unused-variable + import discussions.signals # pylint: disable=unused-import diff --git a/discussions/views.py b/discussions/views.py index b04ed906c1..69e4831363 100644 --- a/discussions/views.py +++ b/discussions/views.py @@ -89,7 +89,7 @@ class ChannelsView(APIView): CanCreateChannel, ) - def post(self, request, *args, **kwargs): + def post(self, request, *args, **kwargs): # pylint: disable=unused-argument """Create a new channel""" serializer = ChannelSerializer(data=request.data, context={'request': request}) serializer.is_valid(raise_exception=True) diff --git a/ecommerce/models.py b/ecommerce/models.py index 8f1cfc951e..feaefec440 100644 --- a/ecommerce/models.py +++ b/ecommerce/models.py @@ -378,7 +378,7 @@ def to_dict(self): """ return serialize_model_object(self) - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """Override save to do certain validations""" self.full_clean() super().save(*args, **kwargs) diff --git a/ecommerce/views.py b/ecommerce/views.py index 33b3630991..d7918a7f24 100644 --- a/ecommerce/views.py +++ b/ecommerce/views.py @@ -59,7 +59,7 @@ class CheckoutView(APIView): ) permission_classes = (IsAuthenticated,) - def post(self, request, *args, **kwargs): + def post(self, request, *args, **kwargs): # pylint: disable=unused-argument """ If the course run is part of a financial aid program, create a new unfulfilled Order and return information used to submit to CyberSource. @@ -142,7 +142,7 @@ class OrderFulfillmentView(APIView): authentication_classes = () permission_classes = (IsSignedByCyberSource, ) - def post(self, request, *args, **kwargs): + def post(self, request, *args, **kwargs): # pylint: disable=unused-argument """ Confirmation from CyberSource which fulfills an existing Order. """ @@ -246,7 +246,7 @@ class UserCouponsView(APIView): TokenAuthentication, ) - def post(self, request, code, *args, **kwargs): + def post(self, request, code, *args, **kwargs): # pylint: disable=unused-argument """Attach a coupon to a user""" with transaction.atomic(): coupon = get_object_or_404(Coupon, coupon_code=code) diff --git a/exams/apps.py b/exams/apps.py index 755462dbc8..db2953394d 100644 --- a/exams/apps.py +++ b/exams/apps.py @@ -14,4 +14,4 @@ def ready(self): """ Ready handler. Import signals. """ - import exams.signals # pylint: disable=unused-variable + import exams.signals # pylint: disable=unused-import diff --git a/exams/utils.py b/exams/utils.py index 80358c4f52..940fae344c 100644 --- a/exams/utils.py +++ b/exams/utils.py @@ -24,7 +24,7 @@ def _match_field(profile, field): pattern = r'^[\u0020-\u00FF]*$' reg = re.compile(pattern) value = getattr(profile, field) - return (True if reg.match(value) else False) if value else False + return bool(reg.match(value)) if value else False def validate_profile(profile): diff --git a/financialaid/admin.py b/financialaid/admin.py index 119722fc96..a9a53fd779 100644 --- a/financialaid/admin.py +++ b/financialaid/admin.py @@ -25,7 +25,7 @@ class FinancialAidAdmin(admin.ModelAdmin): """Admin for FinancialAid""" model = FinancialAid - def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ + def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, signature-differs return False def save_model(self, request, obj, form, change): @@ -43,7 +43,7 @@ class FinancialAidAuditAdmin(admin.ModelAdmin): def has_add_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ return False - def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ + def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, signature-differs return False diff --git a/financialaid/models.py b/financialaid/models.py index 7ac694cf21..31fe7c739e 100644 --- a/financialaid/models.py +++ b/financialaid/models.py @@ -46,14 +46,14 @@ def __str__(self): return 'tier "{0}" for program "{1}"'.format(self.tier.name, self.program.title) @transaction.atomic - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """ Override the save to enforce the existence of only one `current` = True per program and tier """ if self.current: TierProgram.objects.filter(program=self.program, tier=self.tier, current=True).update(current=False) - return super(TierProgram, self).save(*args, **kwargs) + return super().save(*args, **kwargs) class FinancialAid(TimestampedModel, AuditableModel): @@ -77,7 +77,7 @@ class FinancialAid(TimestampedModel, AuditableModel): justification = models.TextField(null=True) country_of_residence = models.TextField() - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """ Override save to make sure only one FinancialAid object exists for a User and the associated Program """ diff --git a/financialaid/tasks.py b/financialaid/tasks.py index c2dc39e986..ff0a600edf 100644 --- a/financialaid/tasks.py +++ b/financialaid/tasks.py @@ -28,7 +28,7 @@ def sync_currency_exchange_rates(): # check specifically if maximum number of api calls per month has been exceeded if resp.status_code == 429: raise ExceededAPICallsException(resp_json["description"]) - elif resp.status_code != 200: # check for other API errors + if resp.status_code != 200: # check for other API errors raise UnexpectedAPIErrorException(resp_json["description"]) latest_rates = resp_json["rates"] update_currency_exchange_rate(latest_rates) diff --git a/financialaid/tasks_test.py b/financialaid/tasks_test.py index 1384b42ab2..0a80b9e8fa 100644 --- a/financialaid/tasks_test.py +++ b/financialaid/tasks_test.py @@ -51,7 +51,7 @@ def setUpTestData(cls): ) def setUp(self): - super(TasksTest, self).setUp() + super().setUp() self.data = { "extraneous information": "blah blah blah", "rates": { diff --git a/financialaid/views.py b/financialaid/views.py index 2bce2d0cce..cff581edca 100644 --- a/financialaid/views.py +++ b/financialaid/views.py @@ -334,7 +334,7 @@ class CoursePriceListView(APIView): ) permission_classes = (IsAuthenticated, CanReadIfStaffOrSelf) - def get(self, request, username, *args, **kwargs): + def get(self, request, username, *args, **kwargs): # pylint: disable=unused-argument """ GET handler """ @@ -370,7 +370,7 @@ class CoursePriceDetailView(APIView): ) permission_classes = (IsAuthenticated, ) - def get(self, request, *args, **kwargs): + def get(self, request, *args, **kwargs): # pylint: disable=unused-argument """ GET handler """ diff --git a/grades/admin.py b/grades/admin.py index 3a17b5a9b6..0e0460d243 100644 --- a/grades/admin.py +++ b/grades/admin.py @@ -18,7 +18,7 @@ class FinalGradeAdmin(admin.ModelAdmin): 'user__username', ) - def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ + def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, signature-differs return False def save_model(self, request, obj, form, change): @@ -39,7 +39,7 @@ class FinalGradeAuditAdmin(admin.ModelAdmin): def has_add_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ return False - def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ + def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, signature-differs return False @@ -60,7 +60,7 @@ class ProctoredExamGradeAdmin(admin.ModelAdmin): 'user__username', ) - def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ + def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, signature-differs return False def save_model(self, request, obj, form, change): @@ -81,7 +81,7 @@ class ProctoredExamGradeAuditAdmin(admin.ModelAdmin): def has_add_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ return False - def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ + def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, signature-differs return False diff --git a/grades/apps.py b/grades/apps.py index dae2c67dd7..911afbdaef 100644 --- a/grades/apps.py +++ b/grades/apps.py @@ -14,4 +14,4 @@ def ready(self): """ Ready handler. Import signals. """ - import grades.signals # pylint: disable=unused-variable + import grades.signals # pylint: disable=unused-import diff --git a/grades/factories.py b/grades/factories.py index d16fb1b581..c7d1f8c7fd 100644 --- a/grades/factories.py +++ b/grades/factories.py @@ -41,7 +41,7 @@ class FinalGradeFactory(DjangoModelFactory): status = FinalGradeStatus.COMPLETE course_run_paid_on_edx = Faker('boolean') - class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods,old-style-class + class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods model = FinalGrade @@ -60,7 +60,7 @@ class ProctoredExamGradeFactory(DjangoModelFactory): passed = Faker('boolean') percentage_grade = FuzzyFloat(low=0, high=1) - class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods,old-style-class + class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods model = ProctoredExamGrade @@ -70,7 +70,7 @@ class MicromastersCourseCertificateFactory(DjangoModelFactory): course = SubFactory(CourseFactory) hash = LazyAttribute(lambda cert: generate_md5('{}|{}'.format(cert.user.id, cert.course.id).encode('utf-8'))) - class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods,old-style-class + class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods model = MicromastersCourseCertificate @@ -80,7 +80,7 @@ class MicromastersProgramCertificateFactory(DjangoModelFactory): user = SubFactory(UserFactory) program = SubFactory(ProgramFactory) - class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods,old-style-class + class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods model = MicromastersProgramCertificate @@ -90,5 +90,5 @@ class MicromastersProgramCommendationFactory(DjangoModelFactory): user = SubFactory(UserFactory) program = SubFactory(ProgramFactory) - class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods,old-style-class + class Meta: # pylint: disable=missing-docstring,no-init,too-few-public-methods model = MicromastersProgramCommendation diff --git a/grades/models.py b/grades/models.py index fd4e9a12c7..53341cbd51 100644 --- a/grades/models.py +++ b/grades/models.py @@ -121,7 +121,7 @@ def __str__(self): course_id=self.course_run.edx_course_key ) - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """Overridden method to run validation""" self.full_clean() return super().save(*args, **kwargs) @@ -152,7 +152,7 @@ class MicromastersCourseCertificate(TimestampedModel): course = models.ForeignKey(Course, models.SET_NULL, null=True) hash = models.CharField(max_length=32, null=False, unique=True) - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """Overridden save method""" if not self.hash: self.hash = generate_md5( @@ -179,7 +179,7 @@ class MicromastersProgramCertificate(TimestampedModel): class Meta: unique_together = ('user', 'program') - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """Overridden save method""" if not self.hash: self.hash = generate_md5( diff --git a/mail/admin.py b/mail/admin.py index f78aae6d19..c525dc925e 100644 --- a/mail/admin.py +++ b/mail/admin.py @@ -16,7 +16,7 @@ class FinancialAidEmailAuditAdmin(admin.ModelAdmin): def has_add_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ return False - def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, arguments-differ + def has_delete_permission(self, *args, **kwargs): # pylint: disable=unused-argument, signature-differs return False diff --git a/mail/api.py b/mail/api.py index 15fc8a8df3..724933c4f2 100644 --- a/mail/api.py +++ b/mail/api.py @@ -143,7 +143,7 @@ def send_batch(cls, subject, body, recipients, # pylint: disable=too-many-argum exception_pairs = [] for chunk in chunks(recipients, chunk_size=chunk_size): - chunk_dict = {email: context for email, context in chunk} + chunk_dict = {email: context for email, context in chunk} # pylint: disable=unnecessary-comprehension emails = list(chunk_dict.keys()) params = { diff --git a/mail/views.py b/mail/views.py index a79137bb48..97996cda2e 100644 --- a/mail/views.py +++ b/mail/views.py @@ -225,7 +225,7 @@ class FinancialAidMailView(GenericAPIView): lookup_url_kwarg = "financial_aid_id" queryset = FinancialAid.objects.all() - def post(self, request, *args, **kwargs): + def post(self, request, *args, **kwargs): # pylint: disable=unused-argument """ Post request to send emails to an individual learner """ diff --git a/profiles/api_test.py b/profiles/api_test.py index 9120d9c14c..02aeaa2c1b 100644 --- a/profiles/api_test.py +++ b/profiles/api_test.py @@ -24,7 +24,7 @@ def setUp(self): """ Create a user with a default social auth """ - super(SocialTests, self).setUp() + super().setUp() profile = SocialProfileFactory.create( agreed_to_terms_of_service=True, diff --git a/profiles/apps.py b/profiles/apps.py index d7645393b9..490d4cf121 100644 --- a/profiles/apps.py +++ b/profiles/apps.py @@ -14,4 +14,4 @@ def ready(self): """ Ready handler. Import signals. """ - import profiles.signals # pylint: disable=unused-variable + import profiles.signals # pylint: disable=unused-import diff --git a/profiles/models.py b/profiles/models.py index 5de2c16580..ab7b5b66e3 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -202,11 +202,11 @@ def save(self, *args, update_image=False, **kwargs): # pylint: disable=argument self.image_small = None self.image_medium = None - super(Profile, self).save(*args, **kwargs) + super().save(*args, **kwargs) # if there is no student id, assign the same number of the primary key if self.student_id is None: self.student_id = self.id - super(Profile, self).save() + super().save() def __str__(self): return 'Profile for "{0}"'.format(self.user.username) diff --git a/profiles/permissions.py b/profiles/permissions.py index 47897217be..d52dd4d6bc 100644 --- a/profiles/permissions.py +++ b/profiles/permissions.py @@ -57,7 +57,7 @@ def has_permission(self, request, view): # private profiles if profile.account_privacy == Profile.PRIVATE: raise Http404 - elif profile.account_privacy == Profile.PUBLIC_TO_MM: + if profile.account_privacy == Profile.PUBLIC_TO_MM: # anonymous user accessing profiles. if request.user.is_anonymous: raise Http404 diff --git a/profiles/permissions_test.py b/profiles/permissions_test.py index a33a9e568b..600a15d7e0 100644 --- a/profiles/permissions_test.py +++ b/profiles/permissions_test.py @@ -73,7 +73,7 @@ class CanSeeIfNotPrivateTests(MockedESTestCase): """ def setUp(self): - super(CanSeeIfNotPrivateTests, self).setUp() + super().setUp() self.user = SocialProfileFactory.create(verified_micromaster_user=False).user self.perm = CanSeeIfNotPrivate() diff --git a/profiles/serializers.py b/profiles/serializers.py index 9ec50dbddb..454497ee2c 100644 --- a/profiles/serializers.py +++ b/profiles/serializers.py @@ -170,8 +170,7 @@ def update(self, instance, validated_data): for attr, value in validated_data.items(): if attr in ('work_history', 'education'): continue - else: - setattr(instance, attr, value) + setattr(instance, attr, value) update_image = 'image' in validated_data instance.save(update_image=update_image) diff --git a/profiles/util_test.py b/profiles/util_test.py index e2f3d70d67..3c2a66063f 100644 --- a/profiles/util_test.py +++ b/profiles/util_test.py @@ -189,7 +189,7 @@ class IsProfileFilledOutTests(DjangoTestCase): Tests for is_profile_filled_out function. """ def setUp(self): - super(IsProfileFilledOutTests, self).setUp() + super().setUp() with mute_signals(post_save): self.profile = ProfileFactory.create() diff --git a/profiles/views_test.py b/profiles/views_test.py index 1211960c0e..8448ecd6f4 100644 --- a/profiles/views_test.py +++ b/profiles/views_test.py @@ -5,7 +5,7 @@ import itertools from unittest.mock import patch -from dateutil.parser import parse +import datetime import ddt from django.urls import resolve, reverse from django.db.models.signals import post_save @@ -334,8 +334,8 @@ def test_patch_own_profile(self): if isinstance(field, (ListSerializer, SerializerMethodField, ReadOnlyField)) or field.read_only is True: # these fields are readonly continue - elif isinstance(field, DateField): - assert getattr(old_profile, key) == parse(value).date() + if isinstance(field, DateField): + assert getattr(old_profile, key) == datetime.datetime.strptime(value, "%Y-%m-%d").date() else: assert getattr(old_profile, key) == value diff --git a/pylintrc b/pylintrc index 74cd192007..264693a717 100644 --- a/pylintrc +++ b/pylintrc @@ -19,4 +19,4 @@ ignored-modules= six.moves, [MESSAGES CONTROL] -disable = no-member, old-style-class, no-init, too-few-public-methods, abstract-method, invalid-name, too-many-ancestors, line-too-long, no-self-use, len-as-condition, no-else-return, unpacking-non-sequence, not-an-iterable, unsupported-membership-test, cyclic-import, duplicate-code, bad-continuation +disable = no-member, old-style-class, no-init, too-few-public-methods, abstract-method, invalid-name, too-many-ancestors, line-too-long, no-self-use, len-as-condition, no-else-return, unpacking-non-sequence, not-an-iterable, unsupported-membership-test, cyclic-import, duplicate-code, bad-continuation, raise-missing-from, import-outside-toplevel diff --git a/requirements.in b/requirements.in index 215f62d1d6..92f264c030 100644 --- a/requirements.in +++ b/requirements.in @@ -29,7 +29,7 @@ jsonpatch==1.16 newrelic open-discussions-client==0.5.0 phonenumbers==8.10.23 -psycopg2==2.7.3.2 +psycopg2==2.8.6 pycountry==16.11.27.1 pysftp==0.2.9 PyNaCl==1.3.0 diff --git a/requirements.txt b/requirements.txt index 3d96956818..148a13eda6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,13 +2,13 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile +# pip-compile requirements.in # amqp==2.5.2 # via kombu backcall==0.1.0 # via ipython -bcrypt==3.1.4 +bcrypt==3.2.0 # via paramiko beautifulsoup4==4.8.2 # via wagtail @@ -118,7 +118,7 @@ html5lib==0.999999999 # wagtail idna==2.7 # via requests -importlib-metadata==1.5.0 +importlib-metadata==3.4.0 # via kombu ipython-genutils==0.2.0 # via traitlets @@ -146,7 +146,7 @@ oauthlib==2.1.0 # social-auth-core open-discussions-client==0.5.0 # via -r requirements.in -paramiko==2.4.2 +paramiko==2.7.2 # via pysftp parso==0.3.1 # via jedi @@ -165,14 +165,12 @@ pillow==7.1.0 # wagtail prompt-toolkit==2.0.5 # via ipython -psycopg2==2.7.3.2 +psycopg2==2.8.6 # via # -r requirements.in # django-server-status ptyprocess==0.6.0 # via pexpect -pyasn1==0.4.4 - # via paramiko pycountry==16.11.27.1 # via -r requirements.in pycparser==2.19 @@ -267,6 +265,8 @@ tornado==5.1.1 # via robohash traitlets==4.3.2 # via ipython +typing-extensions==3.7.4.3 + # via importlib-metadata unidecode==0.4.21 # via wagtail urllib3==1.24.2 @@ -291,7 +291,7 @@ willow==1.3 # via wagtail xlsxwriter==1.3.7 # via wagtail -zipp==3.0.0 +zipp==3.4.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/roles/apps.py b/roles/apps.py index 9e35a3d4cd..07b139eb59 100644 --- a/roles/apps.py +++ b/roles/apps.py @@ -14,4 +14,4 @@ def ready(self): """ Ready handler. Import signals. """ - import roles.signals # pylint: disable=unused-variable + import roles.signals # pylint: disable=unused-import diff --git a/roles/models.py b/roles/models.py index d06279c123..759acc21b7 100644 --- a/roles/models.py +++ b/roles/models.py @@ -64,14 +64,14 @@ class Meta: unique_together = ('user', 'program', 'role',) @transaction.atomic - def save(self, *args, **kwargs): # pylint: disable=arguments-differ + def save(self, *args, **kwargs): # pylint: disable=signature-differs """ Overridden method to run a preventive validation before saving the object. """ self.full_clean() - super(Role, self).save(*args, **kwargs) + super().save(*args, **kwargs) - def full_clean(self, *args, **kwargs): # pylint: disable=arguments-differ + def full_clean(self, *args, **kwargs): # pylint: disable=signature-differs """ Overridden method to run a preventive validation. """ @@ -87,7 +87,7 @@ def full_clean(self, *args, **kwargs): # pylint: disable=arguments-differ existing_role_queryset.first().role ) ) - super(Role, self).full_clean(*args, **kwargs) + super().full_clean(*args, **kwargs) def __str__(self): return "{user}: {role} in {program}".format( diff --git a/roles/models_test.py b/roles/models_test.py index d97f54eeab..48362e19ab 100644 --- a/roles/models_test.py +++ b/roles/models_test.py @@ -23,7 +23,7 @@ def setUpTestData(cls): cls.program2 = ProgramFactory.create() def tearDown(self): - super(MicroMastersRoleTest, self).tearDown() + super().tearDown() Role.objects.all().delete() def test_role_available(self): diff --git a/roles/signals_test.py b/roles/signals_test.py index 6f7872d0ac..3074b8d572 100644 --- a/roles/signals_test.py +++ b/roles/signals_test.py @@ -21,7 +21,7 @@ class SignalsTest(MockedESTestCase): """ def setUp(self): - super(SignalsTest, self).setUp() + super().setUp() self.user = UserFactory.create() self.program = ProgramFactory.create() diff --git a/runtime.txt b/runtime.txt index a01373a365..1a18179443 100644 --- a/runtime.txt +++ b/runtime.txt @@ -1 +1 @@ -python-3.7.2 +python-3.9.1 diff --git a/search/apps.py b/search/apps.py index 4378f399bf..39b9218b63 100644 --- a/search/apps.py +++ b/search/apps.py @@ -14,4 +14,4 @@ def ready(self): """ Ready handler. Import signals. """ - import search.signals # pylint: disable=unused-variable + import search.signals # pylint: disable=unused-import diff --git a/search/permissions_test.py b/search/permissions_test.py index ed2bee628d..348c8e3094 100644 --- a/search/permissions_test.py +++ b/search/permissions_test.py @@ -24,7 +24,7 @@ def setUpTestData(cls): cls.program = ProgramFactory.create(live=True) def setUp(self): - super(PermissionsTests, self).setUp() + super().setUp() self.request = RequestFactory().get('/') self.request.user = AnonymousUser() diff --git a/search/views.py b/search/views.py index e8a714d200..35a1fe3539 100644 --- a/search/views.py +++ b/search/views.py @@ -41,7 +41,7 @@ def _execute_search_from_request(self, user, request_data): return Response(results.to_dict()) - def post(self, request, *args, **kwargs): + def post(self, request, *args, **kwargs): # pylint: disable=unused-argument """ Handler for POST requests """ diff --git a/search/views_test.py b/search/views_test.py index 1cb484aa6b..aa8a5ff8af 100644 --- a/search/views_test.py +++ b/search/views_test.py @@ -64,7 +64,7 @@ def setUpTestData(cls): cls.search_url = reverse('search_api', kwargs={'elastic_url': ''}) def setUp(self): - super(SearchTests, self).setUp() + super().setUp() self.client.force_login(self.staff) @override_settings(ELASTICSEARCH_DEFAULT_PAGE_SIZE=1000) diff --git a/seed_data/lib.py b/seed_data/lib.py index 28b6255980..ba60ab5366 100644 --- a/seed_data/lib.py +++ b/seed_data/lib.py @@ -55,14 +55,14 @@ def result(cls, objects, params): cls.param_keys ) ) - elif len(objects) == 0: + if len(objects) == 0: raise Exception( "No {} found with the given params ({})".format( cls.model_cls.__name__, params ) ) - elif len(objects) > 1: + if len(objects) > 1: exc_text = ( "Multiple {} records found with the given params ({}). These parameters need to " "be specific enough to match a single record.\n{}" @@ -74,8 +74,7 @@ def result(cls, objects, params): '\n'.join(str(obj) for obj in objects) ) ) - else: - return objects[0] + return objects[0] @classmethod def calculate_params(cls, **kwargs): diff --git a/test_requirements.txt b/test_requirements.txt index 07eb79bf29..f114971d1d 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,4 +1,4 @@ -astroid==2.0.4 +astroid==2.4.2 bpython codecov ddt @@ -7,8 +7,8 @@ ipdb nplusone>=0.8.1 pdbpp pip-tools -pylint==2.1.0 -pylint-django==2.0.2 +pylint==2.6.0 +pylint-django==2.1.0 pylint-plugin-utils==0.6 pytest==5.3.5 pytest-cov diff --git a/tox.ini b/tox.ini index 06285f45a9..1ca92ba70e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py37 +envlist = py39 skipsdist = True [testenv] diff --git a/ui/views.py b/ui/views.py index c957b4727f..0e550e2cfc 100644 --- a/ui/views.py +++ b/ui/views.py @@ -29,7 +29,7 @@ class ReactView(View): # pylint: disable=unused-argument """ Abstract view for templates using React """ - def get(self, request, *args, **kwargs): + def get(self, request, *args, **kwargs): # pylint: disable=unused-argument """ Handle GET requests to templates using React """ @@ -81,7 +81,7 @@ def get(self, request, *args, **kwargs): } ) - def post(self, request, *args, **kwargs): + def post(self, request, *args, **kwargs): # pylint: disable=unused-argument """Redirect to GET. This assumes there's never any good reason to POST to these views.""" return redirect(request.build_absolute_uri()) @@ -112,7 +112,7 @@ def get(self, request, *args, **kwargs): # /learner/ redirects to logged in user's page, but user is not logged in here raise Http404 - return super(UsersView, self).get(request, *args, **kwargs) + return super().get(request, *args, **kwargs) def standard_error_page(request, status_code, template_filename): diff --git a/ui/views_test.py b/ui/views_test.py index 6281e29bb6..cca343c722 100644 --- a/ui/views_test.py +++ b/ui/views_test.py @@ -524,7 +524,7 @@ class TestProgramPage(ViewsTests): Test that the ProgramPage view work as expected. """ def setUp(self): - super(TestProgramPage, self).setUp() + super().setUp() homepage = HomePage.objects.first() program = ProgramFactory.create(title="Test Program Title", live=True) self.program_page = ProgramPage(program=program, title="Test Program")