diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 607b3e844..80de8c83f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,12 +61,11 @@ jobs: - name: Install dependencies run: poetry install --no-interaction -# TODO: use ruff or upgrade pylint, pylint-django, and asteroid to support String based model references. -# - name: Lint -# run: poetry run pylint ./**/*.py + - name: Lint (Ruff) + run: poetry run ruff check . - - name: Code Formatting (Black) - run: poetry run black --check . + - name: Code Formatting (Ruff) + run: poetry run ruff format --check . # Configurations required for elasticsearch. - name: Configure sysctl limits diff --git a/README.md b/README.md index b0dee3178..39d25b768 100644 --- a/README.md +++ b/README.md @@ -72,13 +72,13 @@ for running the app # Run Python test cases in a single file that match some function/class name docker-compose run --rm web pytest /path/to/test.py -k test_some_logic # Run Python linter - docker-compose run --rm web pylint + docker-compose run --rm web ruff check . ### PYTHON FORMATTING # Format all python files - docker-compose run --rm web black . + docker-compose run --rm web ruff format --check . # Format a specific file - docker-compose run --rm web black /path/to/file.py + docker-compose run --rm ruff format --check /path/to/file.py ### JS/CSS TESTS/LINTING # We also include a helper script to execute JS tests in most of our projects diff --git a/affiliate/admin.py b/affiliate/admin.py index ab34d427a..808676263 100644 --- a/affiliate/admin.py +++ b/affiliate/admin.py @@ -31,7 +31,7 @@ class AffiliateReferralActionAdmin(TimestampedModelAdmin): list_filter = ["affiliate__name"] ordering = ["-created_on"] - def get_queryset(self, request): + def get_queryset(self, request): # noqa: ARG002 """Overrides base method""" return self.model.objects.select_related("affiliate") diff --git a/affiliate/api.py b/affiliate/api.py index ee5d1de09..149aa7db9 100644 --- a/affiliate/api.py +++ b/affiliate/api.py @@ -14,10 +14,7 @@ def get_affiliate_code_from_qstring(request): Returns: Optional[str]: The affiliate code (or None) """ - if request.method != "GET": - return None - affiliate_code = request.GET.get(AFFILIATE_QS_PARAM) - return affiliate_code + return request.GET.get(AFFILIATE_QS_PARAM) if request.method == "GET" else None def get_affiliate_code_from_request(request): diff --git a/affiliate/api_test.py b/affiliate/api_test.py index 6478baa09..3fad2143f 100644 --- a/affiliate/api_test.py +++ b/affiliate/api_test.py @@ -36,7 +36,7 @@ def test_get_affiliate_code_from_request(): request = RequestFactory().get("/") code = get_affiliate_code_from_request(request) assert code is None - setattr(request, "affiliate_code", affiliate_code) + setattr(request, "affiliate_code", affiliate_code) # noqa: B010 code = get_affiliate_code_from_request(request) assert code == affiliate_code @@ -62,7 +62,7 @@ def test_get_affiliate_id_from_request(): """ affiliate_code = "abc" request = RequestFactory().get("/") - setattr(request, "affiliate_code", affiliate_code) + setattr(request, "affiliate_code", affiliate_code) # noqa: B010 affiliate_id = get_affiliate_id_from_request(request) assert affiliate_id is None affiliate = AffiliateFactory.create(code=affiliate_code) diff --git a/affiliate/middleware.py b/affiliate/middleware.py index 9c413a4d6..7668fd9cc 100644 --- a/affiliate/middleware.py +++ b/affiliate/middleware.py @@ -9,9 +9,9 @@ class AffiliateMiddleware: def __init__(self, get_response): self.get_response = get_response - def __call__(self, request): + def __call__(self, request): # noqa: D102 request.affiliate_code = None - session = getattr(request, "session") + session = getattr(request, "session") # noqa: B009 if session is None: return self.get_response(request) qs_affiliate_code = get_affiliate_code_from_qstring(request) diff --git a/affiliate/migrations/0001_affiliate_initial_models.py b/affiliate/migrations/0001_affiliate_initial_models.py index 1784a6c42..a60d76c90 100644 --- a/affiliate/migrations/0001_affiliate_initial_models.py +++ b/affiliate/migrations/0001_affiliate_initial_models.py @@ -1,12 +1,11 @@ # Generated by Django 2.2.10 on 2020-10-22 16:42 +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): - initial = True dependencies = [ diff --git a/affiliate/models.py b/affiliate/models.py index a2ca63903..2acc931c0 100644 --- a/affiliate/models.py +++ b/affiliate/models.py @@ -12,9 +12,7 @@ class Affiliate(TimestampedModel): name = models.CharField(max_length=50, unique=True) def __str__(self): - return "Affiliate: id={}, code={}, name={}".format( - self.id, self.code, self.name - ) + return f"Affiliate: id={self.id}, code={self.code}, name={self.name}" class AffiliateReferralAction(TimestampedModel): diff --git a/authentication/api.py b/authentication/api.py index 688de2b84..20127dcef 100644 --- a/authentication/api.py +++ b/authentication/api.py @@ -2,12 +2,11 @@ from importlib import import_module from django.conf import settings -from django.contrib.auth import SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY +from django.contrib.auth import BACKEND_SESSION_KEY, HASH_SESSION_KEY, SESSION_KEY from django.db import IntegrityError -from users.utils import is_duplicate_username_error from users.api import find_available_username - +from users.utils import is_duplicate_username_error USERNAME_COLLISION_ATTEMPTS = 10 @@ -26,7 +25,7 @@ def create_user_session(user): session = SessionStore() - session[SESSION_KEY] = user._meta.pk.value_to_string(user) + session[SESSION_KEY] = user._meta.pk.value_to_string(user) # noqa: SLF001 session[BACKEND_SESSION_KEY] = "django.contrib.auth.backends.ModelBackend" session[HASH_SESSION_KEY] = user.get_session_auth_hash() session.save() @@ -51,13 +50,13 @@ def create_user_with_generated_username(serializer, initial_username): username = initial_username attempts = 0 - if len(username) < 2: + if len(username) < 2: # noqa: PLR2004 username = username + "11" while created_user is None and attempts < USERNAME_COLLISION_ATTEMPTS: try: created_user = serializer.save(username=username) - except IntegrityError as exc: + except IntegrityError as exc: # noqa: PERF203 if not is_duplicate_username_error(exc): raise username = find_available_username(initial_username) diff --git a/authentication/api_test.py b/authentication/api_test.py index 737bb7196..e97c65f4c 100644 --- a/authentication/api_test.py +++ b/authentication/api_test.py @@ -110,6 +110,6 @@ def test_create_user_exception(mocker): patched_save = mocker.patch.object( UserSerializer, "save", side_effect=ValueError("idk") ) - with pytest.raises(ValueError): + with pytest.raises(ValueError): # noqa: PT011 api.create_user_with_generated_username(UserSerializer(data={}), "testuser") patched_save.assert_called_once() diff --git a/authentication/exceptions.py b/authentication/exceptions.py index 6db2932eb..e103fe9b1 100644 --- a/authentication/exceptions.py +++ b/authentication/exceptions.py @@ -92,5 +92,5 @@ class UserTryAgainLaterException(AuthException): """The user should try to register again later""" -class UserMissingSocialAuthException(Exception): +class UserMissingSocialAuthException(Exception): # noqa: N818 """Raised if the user doesn't have a social auth""" diff --git a/authentication/middleware.py b/authentication/middleware.py index 4821e945e..eaa545c50 100644 --- a/authentication/middleware.py +++ b/authentication/middleware.py @@ -1,7 +1,7 @@ """Authentication middleware""" -from django.shortcuts import redirect from urllib.parse import quote +from django.shortcuts import redirect from social_core.exceptions import SocialAuthBaseException from social_django.middleware import SocialAuthExceptionMiddleware @@ -19,17 +19,17 @@ def process_exception(self, request, exception): """ strategy = getattr(request, "social_strategy", None) if strategy is None or self.raise_exception(request, exception): - return + return # noqa: RET502 - if isinstance(exception, SocialAuthBaseException): + if isinstance(exception, SocialAuthBaseException): # noqa: RET503 backend = getattr(request, "backend", None) backend_name = getattr(backend, "name", "unknown-backend") message = self.get_message(request, exception) url = self.get_redirect_uri(request, exception) - if url: - url += ("?" in url and "&" or "?") + "message={0}&backend={1}".format( + if url: # noqa: RET503 + url += ("?" in url and "&" or "?") + "message={0}&backend={1}".format( # noqa: UP030 quote(message), backend_name ) return redirect(url) diff --git a/authentication/middleware_test.py b/authentication/middleware_test.py index 34b682872..6c44d5d90 100644 --- a/authentication/middleware_test.py +++ b/authentication/middleware_test.py @@ -1,7 +1,8 @@ """Tests for auth middleware""" +from urllib.parse import quote + from django.contrib.sessions.middleware import SessionMiddleware from django.shortcuts import reverse -from urllib.parse import quote from rest_framework import status from social_core.exceptions import AuthAlreadyAssociated from social_django.utils import load_backend, load_strategy diff --git a/authentication/pipeline/compliance.py b/authentication/pipeline/compliance.py index 179674bad..3e81f6d27 100644 --- a/authentication/pipeline/compliance.py +++ b/authentication/pipeline/compliance.py @@ -12,13 +12,15 @@ ) from compliance import api - log = logging.getLogger() def verify_exports_compliance( - strategy, backend, user=None, **kwargs -): # pylint: disable=unused-argument + strategy, # noqa: ARG001 + backend, + user=None, + **kwargs, # noqa: ARG001 +): """ Verify that the user is allowed by exports compliance @@ -37,7 +39,7 @@ def verify_exports_compliance( try: export_inquiry = api.verify_user_with_exports(user) - except Exception as exc: # pylint: disable=broad-except + except Exception as exc: # hard failure to request the exports API, log an error but don't let the user proceed log.exception("Unable to verify exports compliance") raise UserTryAgainLaterException(backend) from exc @@ -78,12 +80,12 @@ def verify_exports_compliance( [settings.ADMIN_EMAIL], connection=connection, ) - except Exception: # pylint: disable=broad-except + except Exception: log.exception( "Exception sending email to support regarding export compliance check failure" ) raise UserExportBlockedException(backend, export_inquiry.reason_code) if export_inquiry.is_unknown: - raise AuthException("Unable to authenticate, please contact support") + raise AuthException("Unable to authenticate, please contact support") # noqa: EM101 return {} diff --git a/authentication/pipeline/compliance_test.py b/authentication/pipeline/compliance_test.py index 9292c2dd1..d2b6d0aee 100644 --- a/authentication/pipeline/compliance_test.py +++ b/authentication/pipeline/compliance_test.py @@ -9,7 +9,6 @@ from authentication.pipeline import compliance from compliance.factories import ExportsInquiryLogFactory - pytestmark = pytest.mark.django_db @@ -22,17 +21,17 @@ def test_verify_exports_compliance_disabled(mocker): @pytest.mark.parametrize( - "is_active, inquiry_exists, should_verify", + "is_active, inquiry_exists, should_verify", # noqa: PT006 [ - [True, True, False], - [True, False, True], - [False, True, True], - [False, False, True], + [True, True, False], # noqa: PT007 + [True, False, True], # noqa: PT007 + [False, True, True], # noqa: PT007 + [False, False, True], # noqa: PT007 ], ) -def test_verify_exports_compliance_user_active( +def test_verify_exports_compliance_user_active( # noqa: PLR0913 mailoutbox, mocker, user, is_active, inquiry_exists, should_verify -): # pylint: disable=too-many-arguments +): """Assert that the user is verified only if they already haven't been""" user.is_active = is_active if inquiry_exists: diff --git a/authentication/pipeline/user.py b/authentication/pipeline/user.py index f27a29d04..0d31273b0 100644 --- a/authentication/pipeline/user.py +++ b/authentication/pipeline/user.py @@ -24,12 +24,12 @@ ) from authentication.utils import SocialAuthState, is_user_email_blocked from compliance import api as compliance_api -from courseware import api as courseware_api, tasks as courseware_tasks +from courseware import api as courseware_api +from courseware import tasks as courseware_tasks from hubspot_xpro.task_helpers import sync_hubspot_user from users.serializers import ProfileSerializer, UserSerializer from users.utils import usernameify - log = logging.getLogger() User = get_user_model() @@ -37,12 +37,14 @@ CREATE_COURSEWARE_USER_RETRY_DELAY = 60 NAME_MIN_LENGTH = 2 -# pylint: disable=keyword-arg-before-vararg - def validate_email_auth_request( - strategy, backend, user=None, *args, **kwargs -): # pylint: disable=unused-argument + strategy, # noqa: ARG001 + backend, + user=None, + *args, # noqa: ARG001 + **kwargs, # noqa: ARG001 +): """ Validates an auth request for email @@ -62,8 +64,12 @@ def validate_email_auth_request( def get_username( - strategy, backend, user=None, *args, **kwargs -): # pylint: disable=unused-argument + strategy, + backend, # noqa: ARG001 + user=None, + *args, # noqa: ARG001 + **kwargs, # noqa: ARG001 +): """ Gets the username for a user @@ -77,8 +83,14 @@ def get_username( @partial def create_user_via_email( - strategy, backend, user=None, flow=None, current_partial=None, *args, **kwargs -): # pylint: disable=too-many-arguments,unused-argument + strategy, + backend, + user=None, + flow=None, + current_partial=None, + *args, # noqa: ARG001 + **kwargs, +): """ Creates a new user if needed and sets the password and name. Args: @@ -131,10 +143,10 @@ def create_user_via_email( try: created_user = create_user_with_generated_username(serializer, username) if created_user is None: - raise IntegrityError( - "Failed to create User with generated username ({})".format(username) + raise IntegrityError( # noqa: TRY301 + "Failed to create User with generated username ({})".format(username) # noqa: EM103, UP032 ) - except Exception as exc: + except Exception as exc: # noqa: BLE001 raise UserCreationFailedException(backend, current_partial) from exc return {"is_new": True, "user": created_user, "username": created_user.username} @@ -142,8 +154,14 @@ def create_user_via_email( @partial def create_profile( - strategy, backend, user=None, flow=None, current_partial=None, *args, **kwargs -): # pylint: disable=too-many-arguments,unused-argument + strategy, + backend, + user=None, + flow=None, # noqa: ARG001 + current_partial=None, + *args, # noqa: ARG001 + **kwargs, # noqa: ARG001 +): """ Creates a new profile for the user Args: @@ -173,8 +191,14 @@ def create_profile( @partial def validate_email( - strategy, backend, user=None, flow=None, current_partial=None, *args, **kwargs -): # pylint: disable=unused-argument + strategy, + backend, + user=None, # noqa: ARG001 + flow=None, # noqa: ARG001 + current_partial=None, + *args, # noqa: ARG001 + **kwargs, # noqa: ARG001 +): """ Validates a user's email for register @@ -190,7 +214,7 @@ def validate_email( """ data = strategy.request_data() authentication_flow = data.get("flow") - if authentication_flow == SocialAuthState.FLOW_REGISTER and "email" in data: + if authentication_flow == SocialAuthState.FLOW_REGISTER and "email" in data: # noqa: SIM102 if is_user_email_blocked(data["email"]): raise EmailBlockedException(backend, current_partial) return {} @@ -198,8 +222,14 @@ def validate_email( @partial def validate_password( - strategy, backend, user=None, flow=None, current_partial=None, *args, **kwargs -): # pylint: disable=unused-argument + strategy, + backend, + user=None, + flow=None, + current_partial=None, + *args, # noqa: ARG001 + **kwargs, # noqa: ARG001 +): """ Validates a user's password for login @@ -231,7 +261,7 @@ def validate_password( return {} -def forbid_hijack(strategy, backend, **kwargs): # pylint: disable=unused-argument +def forbid_hijack(strategy, backend, **kwargs): # noqa: ARG001 """ Forbid an admin user from trying to login/register while hijacking another user @@ -241,13 +271,17 @@ def forbid_hijack(strategy, backend, **kwargs): # pylint: disable=unused-argume """ # As first step in pipeline, stop a hijacking admin from going any further if bool(strategy.session_get("hijack_history")): - raise AuthException("You are hijacking another user, don't try to login again") + raise AuthException("You are hijacking another user, don't try to login again") # noqa: EM101 return {} def activate_user( - strategy, backend, user=None, is_new=False, **kwargs -): # pylint: disable=unused-argument + strategy, # noqa: ARG001 + backend, # noqa: ARG001 + user=None, + is_new=False, # noqa: ARG001, FBT002 + **kwargs, # noqa: ARG001 +): """ Activate the user's account if they passed export controls @@ -272,8 +306,12 @@ def activate_user( def create_courseware_user( - strategy, backend, user=None, is_new=False, **kwargs -): # pylint: disable=unused-argument + strategy, # noqa: ARG001 + backend, # noqa: ARG001 + user=None, + is_new=False, # noqa: FBT002 + **kwargs, # noqa: ARG001 +): """ Create a user in the courseware, deferring a retry via celery if it fails @@ -286,7 +324,7 @@ def create_courseware_user( try: courseware_api.create_user(user) - except Exception: # pylint: disable=broad-except + except Exception: log.exception("Error creating courseware user records on User create") # try again later courseware_tasks.create_user_from_id.apply_async( @@ -319,14 +357,18 @@ def send_user_to_hubspot(request, **kwargs): url = f"https://forms.hubspot.com/uploads/form/v2/{portal_id}/{form_id}?&" - requests.post(url=url, data=data, headers=headers) + requests.post(url=url, data=data, headers=headers) # noqa: S113 return {} def sync_user_to_hubspot( - strategy, backend, user=None, is_new=False, **kwargs -): # pylint: disable=unused-argument + strategy, # noqa: ARG001 + backend, # noqa: ARG001 + user=None, + is_new=False, # noqa: ARG001, FBT002 + **kwargs, # noqa: ARG001 +): """ Sync the user's latest profile data with hubspot on login """ diff --git a/authentication/pipeline/user_test.py b/authentication/pipeline/user_test.py index 1da80d110..0771848af 100644 --- a/authentication/pipeline/user_test.py +++ b/authentication/pipeline/user_test.py @@ -1,5 +1,4 @@ """Tests of user pipeline actions""" -# pylint: disable=redefined-outer-name import pytest from django.contrib.sessions.middleware import SessionMiddleware @@ -36,7 +35,7 @@ def mock_email_backend(mocker, backend_settings): """Fixture that returns a fake EmailAuth backend object""" backend = mocker.Mock() backend.name = "email" - backend.setting.side_effect = lambda key, default, **kwargs: backend_settings.get( + backend.setting.side_effect = lambda key, default, **kwargs: backend_settings.get( # noqa: ARG005 key, default ) return backend @@ -85,7 +84,8 @@ def validate_email_auth_request_not_email_backend(mocker): @pytest.mark.parametrize( - "has_user,expected", [(True, {"flow": SocialAuthState.FLOW_LOGIN}), (False, {})] + "has_user,expected", # noqa: PT006 + [(True, {"flow": SocialAuthState.FLOW_LOGIN}), (False, {})], ) @pytest.mark.django_db def test_validate_email_auth_request(rf, has_user, expected, mocker): @@ -149,7 +149,7 @@ def test_user_password_not_email_backend(mocker): @pytest.mark.parametrize("user_password", ["abc123", "def456"]) def test_user_password_login(rf, user, user_password, mocker): """Tests that user_password works for login case""" - request_password = "abc123" + request_password = "abc123" # noqa: S105 user.set_password(user_password) user.save() request = rf.post( @@ -229,7 +229,7 @@ def test_user_password_not_exists(rf, mocker): @pytest.mark.parametrize( - "backend_name,flow", + "backend_name,flow", # noqa: PT006 [ ("notemail", None), ("notemail", SocialAuthState.FLOW_REGISTER), @@ -276,7 +276,7 @@ def test_create_user_via_email(mocker, mock_email_backend, mock_create_user_stra response = user_actions.create_user_via_email( mock_create_user_strategy, mock_email_backend, - details=dict(email=email), + details=dict(email=email), # noqa: C408 pipeline_index=0, flow=SocialAuthState.FLOW_REGISTER, ) @@ -333,7 +333,7 @@ def test_create_user_via_email_with_shorter_name(mocker, mock_email_backend): user_actions.create_user_via_email( mock_strategy, mock_email_backend, - details=dict(email="test@example.com"), + details=dict(email="test@example.com"), # noqa: C408 pipeline_index=0, flow=SocialAuthState.FLOW_REGISTER, ) @@ -372,14 +372,14 @@ def test_create_user_via_email_with_email_case_insensitive_existing_user( mock_email_backend, pipeline_index=0, flow=SocialAuthState.FLOW_REGISTER, - details=dict(email=email), + details=dict(email=email), # noqa: C408 ) @pytest.mark.django_db @pytest.mark.parametrize( - "create_user_return_val,create_user_exception", - [[None, None], [UserFactory.build(), ValueError("bad value")]], + "create_user_return_val,create_user_exception", # noqa: PT006 + [[None, None], [UserFactory.build(), ValueError("bad value")]], # noqa: PT007 ) def test_create_user_via_email_create_fail( mocker, @@ -398,7 +398,7 @@ def test_create_user_via_email_create_fail( user_actions.create_user_via_email( mock_create_user_strategy, mock_email_backend, - details=dict(email="someuser@example.com"), + details=dict(email="someuser@example.com"), # noqa: C408 pipeline_index=0, flow=SocialAuthState.FLOW_REGISTER, ) @@ -422,7 +422,7 @@ def test_create_user_via_email_affiliate( user_actions.create_user_via_email( mock_create_user_strategy, mock_email_backend, - details=dict(email="someuser@example.com"), + details=dict(email="someuser@example.com"), # noqa: C408 pipeline_index=0, flow=SocialAuthState.FLOW_REGISTER, ) @@ -438,7 +438,7 @@ def test_create_user_via_email_affiliate( @pytest.mark.parametrize("hubspot_key", [None, "fake-key"]) def test_create_profile( mock_email_backend, mock_create_profile_strategy, hubspot_key, settings, mocker -): # pylint:disable=too-many-arguments +): """ Tests that create_profile creates a profile """ @@ -495,7 +495,7 @@ def test_forbid_hijack(mocker, hijacked): kwargs = {"flow": SocialAuthState.FLOW_LOGIN} if hijacked: - with pytest.raises(ValueError): + with pytest.raises(ValueError): # noqa: PT011 user_actions.forbid_hijack(*args, **kwargs) else: assert user_actions.forbid_hijack(*args, **kwargs) == {} @@ -533,18 +533,33 @@ def test_send_user_to_hubspot(mocker, settings): @pytest.mark.parametrize("is_active", [True, False]) @pytest.mark.parametrize("is_new", [True, False]) @pytest.mark.parametrize( - "is_enabled, has_inquiry, computed_result, expected", + "is_enabled, has_inquiry, computed_result, expected", # noqa: PT006 [ - [True, True, RESULT_SUCCESS, True], # feature enabled, result is success - [True, True, RESULT_DENIED, False], # feature enabled, result is denied - [True, True, RESULT_UNKNOWN, False], # feature enabled, result is unknown - [False, False, None, True], # feature disabled - [True, False, None, False], # feature enabled, no result + [ # noqa: PT007 + True, + True, + RESULT_SUCCESS, + True, + ], # feature enabled, result is success + [ # noqa: PT007 + True, + True, + RESULT_DENIED, + False, + ], # feature enabled, result is denied + [ # noqa: PT007 + True, + True, + RESULT_UNKNOWN, + False, + ], # feature enabled, result is unknown + [False, False, None, True], # feature disabled # noqa: PT007 + [True, False, None, False], # feature enabled, no result # noqa: PT007 ], ) -def test_activate_user( +def test_activate_user( # noqa: PLR0913 mocker, user, is_active, is_new, is_enabled, has_inquiry, computed_result, expected -): # pylint: disable=too-many-arguments +): """Test that activate_user takes the correct action""" user.is_active = is_active if has_inquiry: @@ -564,17 +579,17 @@ def test_activate_user( @pytest.mark.parametrize("raises_error", [True, False]) @pytest.mark.parametrize( - "is_active, is_new, creates_records", + "is_active, is_new, creates_records", # noqa: PT006 [ - [True, True, True], - [True, False, False], - [False, True, False], - [False, False, False], + [True, True, True], # noqa: PT007 + [True, False, False], # noqa: PT007 + [False, True, False], # noqa: PT007 + [False, False, False], # noqa: PT007 ], ) -def test_create_courseware_user( +def test_create_courseware_user( # noqa: PLR0913 mocker, user, raises_error, is_active, is_new, creates_records -): # pylint: disable=too-many-arguments +): """Test that activate_user takes the correct action""" user.is_active = is_active @@ -606,10 +621,10 @@ def test_create_courseware_user( @pytest.mark.parametrize( - "backend_name,flow,data", + "backend_name,flow,data", # noqa: PT006 [ ("notemail", SocialAuthState.FLOW_REGISTER, {}), - ("notemail", SocialAuthState.FLOW_LOGIN, dict(email="test@example.com")), + ("notemail", SocialAuthState.FLOW_LOGIN, dict(email="test@example.com")), # noqa: C408 ], ) def test_validate_email_backend(mocker, backend_name, flow, data): diff --git a/authentication/serializers.py b/authentication/serializers.py index fdf1dfde8..a7cea5712 100644 --- a/authentication/serializers.py +++ b/authentication/serializers.py @@ -3,31 +3,31 @@ from django.contrib.auth import get_user_model from django.http import HttpResponseRedirect -from social_django.views import _do_login as login +from rest_framework import serializers from social_core.backends.email import EmailAuth -from social_core.exceptions import InvalidEmail, AuthException, AuthAlreadyAssociated +from social_core.exceptions import AuthAlreadyAssociated, AuthException, InvalidEmail from social_core.utils import ( - user_is_authenticated, - user_is_active, partial_pipeline_data, sanitize_redirect, + user_is_active, + user_is_authenticated, ) -from rest_framework import serializers +from social_django.views import _do_login as login from authentication.exceptions import ( + EmailBlockedException, InvalidPasswordException, - RequirePasswordException, RequirePasswordAndPersonalInfoException, + RequirePasswordException, + RequireProfileException, RequireProviderException, RequireRegistrationException, - RequireProfileException, UserExportBlockedException, UserTryAgainLaterException, - EmailBlockedException, ) from authentication.utils import SocialAuthState -PARTIAL_PIPELINE_TOKEN_KEY = "partial_pipeline_token" +PARTIAL_PIPELINE_TOKEN_KEY = "partial_pipeline_token" # noqa: S105 log = logging.getLogger() @@ -63,8 +63,8 @@ def _save_next(self, data): backend = self.context["backend"] # Check and sanitize a user-defined GET/POST next field value redirect_uri = data["next"] - if backend.setting("SANITIZE_REDIRECTS", True): - allowed_hosts = backend.setting("ALLOWED_REDIRECT_HOSTS", []) + [ + if backend.setting("SANITIZE_REDIRECTS", True): # noqa: FBT003 + allowed_hosts = backend.setting("ALLOWED_REDIRECT_HOSTS", []) + [ # noqa: RUF005 backend.strategy.request_host() ] redirect_uri = sanitize_redirect(allowed_hosts, redirect_uri) @@ -72,8 +72,7 @@ def _save_next(self, data): "next", redirect_uri or backend.setting("LOGIN_REDIRECT_URL") ) - # pylint: disable=too-many-return-statements - def _authenticate(self, flow): + def _authenticate(self, flow): # noqa: PLR0911 """Authenticate the current request""" request = self.context["request"] strategy = self.context["strategy"] @@ -146,7 +145,7 @@ def _authenticate(self, flow): SocialAuthState.STATE_ERROR, errors=["Unexpected authentication result"] ) - def save(self, **kwargs): + def save(self, **kwargs): # noqa: C901 """'Save' the auth request""" try: result = super().save(**kwargs) @@ -247,7 +246,7 @@ class LoginPasswordSerializer(SocialAuthSerializer): password = serializers.CharField(min_length=8, write_only=True) - def create(self, validated_data): + def create(self, validated_data): # noqa: ARG002 """Try to 'save' the request""" try: result = super()._authenticate(SocialAuthState.FLOW_LOGIN) @@ -266,14 +265,14 @@ class RegisterEmailSerializer(SocialAuthSerializer): email = serializers.EmailField(write_only=True, required=False) next = serializers.CharField(write_only=True, required=False) - def validate(self, attrs): + def validate(self, attrs): # noqa: D102 token = (attrs.get("partial", {}) or {}).get("token", None) email = attrs.get("email", None) if not email and not token: - raise serializers.ValidationError("One of 'partial' or 'email' is required") + raise serializers.ValidationError("One of 'partial' or 'email' is required") # noqa: EM101 if email and token: - raise serializers.ValidationError("Pass only one of 'partial' or 'email'") + raise serializers.ValidationError("Pass only one of 'partial' or 'email'") # noqa: EM101 return attrs @@ -307,7 +306,7 @@ class RegisterConfirmSerializer(SocialAuthSerializer): partial_token = serializers.CharField(source="get_partial_token") verification_code = serializers.CharField(write_only=True) - def create(self, validated_data): + def create(self, validated_data): # noqa: ARG002 """Try to 'save' the request""" return super()._authenticate(SocialAuthState.FLOW_REGISTER) @@ -318,7 +317,7 @@ class RegisterDetailsSerializer(SocialAuthSerializer): password = serializers.CharField(min_length=8, write_only=True) name = serializers.CharField(write_only=True) - def create(self, validated_data): + def create(self, validated_data): # noqa: ARG002 """Try to 'save' the request""" return super()._authenticate(SocialAuthState.FLOW_REGISTER) @@ -347,6 +346,6 @@ class RegisterExtraDetailsSerializer(SocialAuthSerializer): write_only=True, allow_blank=True, required=False ) - def create(self, validated_data): + def create(self, validated_data): # noqa: ARG002 """Try to 'save' the request""" return super()._authenticate(SocialAuthState.FLOW_REGISTER) diff --git a/authentication/serializers_test.py b/authentication/serializers_test.py index ee3f8a67e..05ccabaad 100644 --- a/authentication/serializers_test.py +++ b/authentication/serializers_test.py @@ -2,7 +2,7 @@ import pytest from rest_framework.serializers import ValidationError from social_core.backends.email import EmailAuth -from social_core.exceptions import InvalidEmail, AuthException +from social_core.exceptions import AuthException, InvalidEmail from authentication.serializers import RegisterEmailSerializer from authentication.utils import SocialAuthState @@ -14,8 +14,8 @@ @pytest.mark.parametrize( - "side_effect,result", - ( + "side_effect,result", # noqa: PT006 + ( # noqa: PT007 ( AuthException(None, "message"), SocialAuthState(SocialAuthState.STATE_ERROR, errors=["message"]), @@ -41,16 +41,14 @@ def test_social_auth_serializer_error(mocker, side_effect, result): "request": mocker.Mock(), }, ) - assert serializer.is_valid() is True, "Received errors: {}".format( - serializer.errors - ) + assert serializer.is_valid() is True, f"Received errors: {serializer.errors}" assert isinstance(serializer.save(), SocialAuthState) assert serializer.data == RegisterEmailSerializer(result).data @pytest.mark.parametrize( - "data,raises,message", - ( + "data,raises,message", # noqa: PT006 + ( # noqa: PT007 ( {"email": None, "partial": None}, ValidationError, diff --git a/authentication/strategy.py b/authentication/strategy.py index 7936058f6..0968220f2 100644 --- a/authentication/strategy.py +++ b/authentication/strategy.py @@ -8,10 +8,10 @@ class DjangoRestFrameworkStrategy(DjangoStrategy): def __init__(self, storage, drf_request=None, tpl=None): self.drf_request = drf_request # pass the original django request to DjangoStrategy - request = drf_request._request # pylint: disable=protected-access + request = drf_request._request # noqa: SLF001 super().__init__(storage, request=request, tpl=tpl) - def request_data(self, merge=True): + def request_data(self, merge=True): # noqa: ARG002, FBT002 """Returns the request data""" if not self.drf_request: return {} diff --git a/authentication/strategy_test.py b/authentication/strategy_test.py index 36caae06e..2f76d0408 100644 --- a/authentication/strategy_test.py +++ b/authentication/strategy_test.py @@ -10,7 +10,7 @@ def test_strategy_init(mocker): drf_request = mocker.Mock() strategy = load_drf_strategy(request=drf_request) assert strategy.drf_request == drf_request - assert strategy.request == drf_request._request # pylint: disable=protected-access + assert strategy.request == drf_request._request # noqa: SLF001 def test_strategy_request_data(mocker): diff --git a/authentication/urls.py b/authentication/urls.py index 467d04907..35d267615 100644 --- a/authentication/urls.py +++ b/authentication/urls.py @@ -1,19 +1,19 @@ """URL configurations for authentication""" from django.urls import path from django.urls.conf import include + from authentication.views import ( + CustomLogoutView, LoginEmailView, LoginPasswordView, - RegisterEmailView, RegisterConfirmView, RegisterDetailsView, + RegisterEmailView, RegisterExtraDetailsView, get_social_auth_types, - CustomLogoutView, well_known_openid_configuration, ) - urlpatterns = [ path("api/login/email/", LoginEmailView.as_view(), name="psa-login-email"), path("api/login/password/", LoginPasswordView.as_view(), name="psa-login-password"), diff --git a/authentication/utils.py b/authentication/utils.py index 5cb7580f4..bb702bed0 100644 --- a/authentication/utils.py +++ b/authentication/utils.py @@ -1,11 +1,13 @@ """Authentication utils""" import hashlib + from social_core.utils import get_strategy from social_django.utils import STORAGE + from users.models import BlockList -class SocialAuthState: # pylint: disable=too-many-instance-attributes +class SocialAuthState: """Social auth state""" FLOW_REGISTER = "register" @@ -13,7 +15,7 @@ class SocialAuthState: # pylint: disable=too-many-instance-attributes # login states STATE_LOGIN_EMAIL = "login/email" - STATE_LOGIN_PASSWORD = "login/password" + STATE_LOGIN_PASSWORD = "login/password" # noqa: S105 STATE_LOGIN_PROVIDER = "login/provider" # registration states @@ -34,7 +36,7 @@ class SocialAuthState: # pylint: disable=too-many-instance-attributes STATE_INVALID_LINK = "invalid-link" STATE_EXISTING_ACCOUNT = "existing-account" - def __init__( + def __init__( # noqa: PLR0913 self, state, *, @@ -45,7 +47,7 @@ def __init__( field_errors=None, redirect_url=None, user=None, - ): # pylint: disable=too-many-arguments + ): self.state = state self.partial = partial self.flow = flow @@ -69,7 +71,7 @@ def load_drf_strategy(request=None): def get_md5_hash(value): """Returns the md5 hash object for the given value""" - return hashlib.md5(value.lower().encode("utf-8")) + return hashlib.md5(value.lower().encode("utf-8")) # noqa: S324 def is_user_email_blocked(email): @@ -87,11 +89,7 @@ def block_user_email(email): hashed_email=hash_object.hexdigest() ) if created: - msg = "Email {email} is added to the blocklist of MIT xPRO.".format( - email=email - ) + msg = f"Email {email} is added to the blocklist of MIT xPRO." else: - msg = "Email {email} is already marked blocked for MIT xPRO.".format( - email=email - ) + msg = f"Email {email} is already marked blocked for MIT xPRO." return msg diff --git a/authentication/views.py b/authentication/views.py index 0162de57d..0aef07d77 100644 --- a/authentication/views.py +++ b/authentication/views.py @@ -1,27 +1,27 @@ """Authentication views""" -from urllib.parse import quote, urlparse, urlencode, urljoin +from urllib.parse import quote, urlencode, urljoin, urlparse import requests from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.views import LogoutView from django.shortcuts import render, reverse -from social_core.backends.email import EmailAuth -from social_django.models import UserSocialAuth -from social_django.utils import load_backend from rest_framework import status -from rest_framework.views import APIView -from rest_framework.response import Response from rest_framework.decorators import api_view, permission_classes, renderer_classes from rest_framework.permissions import IsAuthenticated from rest_framework.renderers import JSONRenderer +from rest_framework.response import Response +from rest_framework.views import APIView +from social_core.backends.email import EmailAuth +from social_django.models import UserSocialAuth +from social_django.utils import load_backend from authentication.serializers import ( LoginEmailSerializer, LoginPasswordSerializer, - RegisterEmailSerializer, RegisterConfirmSerializer, RegisterDetailsSerializer, + RegisterEmailSerializer, RegisterExtraDetailsSerializer, ) from authentication.utils import load_drf_strategy @@ -37,7 +37,7 @@ class SocialAuthAPIView(APIView): def get_serializer_cls(self): # pragma: no cover """Return the serializer cls""" - raise NotImplementedError("get_serializer_cls must be implemented") + raise NotImplementedError("get_serializer_cls must be implemented") # noqa: EM101 def post(self, request): """Processes a request""" @@ -86,7 +86,7 @@ def post(self, request): if bool(request.session.get("hijack_history")): return Response(status=status.HTTP_403_FORBIDDEN) if settings.RECAPTCHA_SITE_KEY: - r = requests.post( + r = requests.post( # noqa: S113 "https://www.google.com/recaptcha/api/siteverify?secret={key}&response={captcha}".format( key=quote(settings.RECAPTCHA_SECRET_KEY), captcha=quote(request.data["recaptcha"]), @@ -134,7 +134,7 @@ def get_social_auth_types(request): return Response(data=social_auths, status=status.HTTP_200_OK) -def confirmation_sent(request, **kwargs): # pylint: disable=unused-argument +def confirmation_sent(request, **kwargs): # noqa: ARG001 """The confirmation of an email being sent""" return render(request, "confirmation_sent.html") @@ -142,7 +142,7 @@ def confirmation_sent(request, **kwargs): # pylint: disable=unused-argument class CustomLogoutView(LogoutView): """Custom view to modify base functionality in django.contrib.auth.views.LogoutView""" - def get_next_page(self): + def get_next_page(self): # noqa: D102 next_page = super().get_next_page() if next_page in (self.next_page, self.request.path): @@ -156,7 +156,7 @@ def get_next_page(self): @api_view(["GET"]) @renderer_classes([JSONRenderer]) @permission_classes([]) -def well_known_openid_configuration(request): +def well_known_openid_configuration(request): # noqa: ARG001 """View for openid configuration""" # See: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig # NOTE: this is intentionally incomplete because we don't fully support OpenID diff --git a/authentication/views_test.py b/authentication/views_test.py index 53a1970b2..d8d0595fb 100644 --- a/authentication/views_test.py +++ b/authentication/views_test.py @@ -1,28 +1,29 @@ """Tests for authentication views""" -# pylint: disable=redefined-outer-name -from contextlib import contextmanager, ExitStack +from contextlib import ExitStack, contextmanager from unittest.mock import patch +import factory +import pytest +import responses from django.conf import settings from django.contrib.auth import get_user, get_user_model from django.core import mail from django.db import transaction -from django.urls import reverse from django.test import Client, override_settings -import factory +from django.urls import reverse from faker import Faker -from hypothesis import settings as hypothesis_settings, strategies as st, Verbosity +from hypothesis import Verbosity +from hypothesis import settings as hypothesis_settings +from hypothesis import strategies as st +from hypothesis.extra.django import TestCase as HTestCase from hypothesis.stateful import ( + Bundle, + HealthCheck, + RuleBasedStateMachine, consumes, precondition, rule, - Bundle, - RuleBasedStateMachine, - HealthCheck, ) -from hypothesis.extra.django import TestCase as HTestCase -import pytest -import responses from rest_framework import status from social_core.backends.email import EmailAuth @@ -35,8 +36,8 @@ mock_cybersource_wsdl, mock_cybersource_wsdl_operation, ) +from mitxpro.test_utils import MockResponse, any_instance_of from users.factories import UserFactory, UserSocialAuthFactory -from mitxpro.test_utils import any_instance_of, MockResponse pytestmark = [pytest.mark.django_db] @@ -47,8 +48,6 @@ fake = Faker() -# pylint: disable=too-many-public-methods - @pytest.fixture def email_user(user): @@ -57,15 +56,14 @@ def email_user(user): return user -# pylint: disable=too-many-arguments -def assert_api_call( +def assert_api_call( # noqa: PLR0913 client, url, payload, expected, - expect_authenticated=False, + expect_authenticated=False, # noqa: FBT002 expect_status=status.HTTP_200_OK, - use_defaults=True, + use_defaults=True, # noqa: FBT002 ): """Run the API call and perform basic assertions""" assert bool(get_user(client).is_authenticated) is False @@ -92,10 +90,10 @@ def assert_api_call( return actual -@pytest.fixture() +@pytest.fixture def mock_email_send(mocker): """Mock the email send API""" - yield mocker.patch("mail.verification_api.send_verification_email") + return mocker.patch("mail.verification_api.send_verification_email") @contextmanager @@ -128,8 +126,6 @@ class AuthStateMachine(RuleBasedStateMachine): methods to define transitions into and (optionally) out of that state. """ - # pylint: disable=too-many-instance-attributes - ConfirmationSentAuthStates = Bundle("confirmation-sent") ConfirmationRedeemedAuthStates = Bundle("confirmation-redeemed") RegisterExtraDetailsAuthStates = Bundle("register-details-extra") @@ -167,7 +163,7 @@ def __init__(self): # shared data self.email = fake.email() self.user = None - self.password = "password123" + self.password = "password123" # noqa: S105 # track whether we've hit an action that starts a flow or not self.flow_started = False @@ -183,7 +179,7 @@ def teardown(self): self.courseware_tasks_patcher.stop() # end the transaction with a rollback to cleanup any state - transaction.set_rollback(True) + transaction.set_rollback(True) # noqa: FBT003, RUF100 self.atomic.__exit__(None, None, None) def create_existing_user(self): @@ -337,12 +333,14 @@ def login_email_exists(self): auth_state=consumes(RegisterExtraDetailsAuthStates), ) @precondition(lambda self: self.flow_started) - def login_email_abandoned(self, auth_state): # pylint: disable=unused-argument + def login_email_abandoned(self, auth_state): """Login with a user that abandoned the register flow""" # NOTE: This works by "consuming" an extra details auth state, # but discarding the state and starting a new login. # It then re-targets the new state into the extra details again. - auth_state = None # assign None to ensure no accidental usage here + auth_state = ( # noqa: F841 + None # assign None to ensure no accidental usage here + ) return assert_api_call( self.client, @@ -735,7 +733,7 @@ def test_new_register_no_session_partial(client): "state": SocialAuthState.STATE_REGISTER_CONFIRM_SENT, }, ) - assert PARTIAL_PIPELINE_TOKEN_KEY not in client.session.keys() + assert PARTIAL_PIPELINE_TOKEN_KEY not in client.session.keys() # noqa: SIM118 def test_login_email_error(client, mocker): diff --git a/b2b_ecommerce/admin_test.py b/b2b_ecommerce/admin_test.py index 5e48ecc91..fcf52a94c 100644 --- a/b2b_ecommerce/admin_test.py +++ b/b2b_ecommerce/admin_test.py @@ -5,7 +5,6 @@ from b2b_ecommerce.factories import B2BCouponFactory, B2BOrderFactory from b2b_ecommerce.models import B2BCouponAudit, B2BOrderAudit - pytestmark = pytest.mark.django_db diff --git a/b2b_ecommerce/api.py b/b2b_ecommerce/api.py index 4a0a83e57..0bc16306d 100644 --- a/b2b_ecommerce/api.py +++ b/b2b_ecommerce/api.py @@ -167,7 +167,7 @@ def determine_price_and_discount(*, product_version, discount_code, num_seats): coupon_code=discount_code, product_id=product_version.product.id ) except B2BCoupon.DoesNotExist as exc: - raise ValidationError("Invalid coupon code") from exc + raise ValidationError("Invalid coupon code") from exc # noqa: EM101 else: coupon = None diff --git a/b2b_ecommerce/api_test.py b/b2b_ecommerce/api_test.py index 0cddc3524..77819d310 100644 --- a/b2b_ecommerce/api_test.py +++ b/b2b_ecommerce/api_test.py @@ -18,7 +18,6 @@ from ecommerce.models import CouponPaymentVersion from mitxpro.utils import dict_without_keys, now_in_utc - FAKE = faker.Factory.create() @@ -31,7 +30,7 @@ @pytest.fixture(autouse=True) -def cybersource_settings(settings): +def cybersource_settings(settings): # noqa: PT004 """ Set cybersource settings """ @@ -88,7 +87,7 @@ def test_signed_payload(mocker, contract_number): "item_0_code": "enrollment_code", "item_0_name": f"Enrollment codes for {product_version.description}"[:254], "item_0_quantity": order.num_seats, - "item_0_sku": f"enrollment_code-{str(product.content_type)}-{product.content_object.id}", + "item_0_sku": f"enrollment_code-{str(product.content_type)}-{product.content_object.id}", # noqa: RUF010 "item_0_tax_amount": "0", "item_0_unit_price": str(total_price), "line_item_count": 1, @@ -108,7 +107,7 @@ def test_signed_payload(mocker, contract_number): @pytest.mark.parametrize( - "contract_number, b2b_coupon_code", + "contract_number, b2b_coupon_code", # noqa: PT006 [ ("contract_number", "code"), ("contract_number", None), @@ -158,7 +157,7 @@ def test_complete_b2b_order(mocker, contract_number, b2b_coupon_code): @pytest.mark.parametrize( - "order_status, decision", + "order_status, decision", # noqa: PT006 [ (B2BOrder.FAILED, "ERROR"), (B2BOrder.FULFILLED, "ERROR"), @@ -209,7 +208,7 @@ def test_ignore_duplicate_cancel(): assert B2BOrder.objects.get(id=order.id).status == B2BOrder.FAILED -def test_order_fulfilled(mocker): # pylint:disable=too-many-arguments +def test_order_fulfilled(mocker): """ Test the happy case """ diff --git a/b2b_ecommerce/apps.py b/b2b_ecommerce/apps.py index d5b2e5464..9bb3ae721 100644 --- a/b2b_ecommerce/apps.py +++ b/b2b_ecommerce/apps.py @@ -2,11 +2,11 @@ from django.apps import AppConfig -class B2B_EcommerceConfig(AppConfig): +class B2BEcommerceConfig(AppConfig): """AppConfig for B2B_Ecommerce""" name = "b2b_ecommerce" def ready(self): """Application is ready""" - import b2b_ecommerce.signals # pylint:disable=unused-import + import b2b_ecommerce.signals # noqa: F401 diff --git a/b2b_ecommerce/migrations/0001_initial.py b/b2b_ecommerce/migrations/0001_initial.py index 9e992e47a..494b4c430 100644 --- a/b2b_ecommerce/migrations/0001_initial.py +++ b/b2b_ecommerce/migrations/0001_initial.py @@ -1,13 +1,12 @@ # Generated by Django 2.2.3 on 2019-07-29 19:32 -from django.conf import settings import django.contrib.postgres.fields.jsonb -from django.db import migrations, models import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): - initial = True dependencies = [ diff --git a/b2b_ecommerce/migrations/0002_order_coupon_payment.py b/b2b_ecommerce/migrations/0002_order_coupon_payment.py index 909d26165..0617f364c 100644 --- a/b2b_ecommerce/migrations/0002_order_coupon_payment.py +++ b/b2b_ecommerce/migrations/0002_order_coupon_payment.py @@ -1,12 +1,12 @@ # Generated by Django 2.2.3 on 2019-08-05 13:54 -from django.db import migrations, models -import django.db.models.deletion import uuid +import django.db.models.deletion +from django.db import migrations, models + class Migration(migrations.Migration): - dependencies = [ ("ecommerce", "0016_payment_type_choices"), ("b2b_ecommerce", "0001_initial"), diff --git a/b2b_ecommerce/migrations/0003_coupons.py b/b2b_ecommerce/migrations/0003_coupons.py index 743cd8032..c6121bfd3 100644 --- a/b2b_ecommerce/migrations/0003_coupons.py +++ b/b2b_ecommerce/migrations/0003_coupons.py @@ -1,14 +1,13 @@ # Generated by Django 2.2.4 on 2019-09-04 20:11 -from django.conf import settings import django.contrib.postgres.fields.jsonb import django.core.validators -from django.db import migrations, models import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ("ecommerce", "0016_payment_type_choices"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), diff --git a/b2b_ecommerce/migrations/0004_coupon_company_blank.py b/b2b_ecommerce/migrations/0004_coupon_company_blank.py index edc4e69f9..172b9afe1 100644 --- a/b2b_ecommerce/migrations/0004_coupon_company_blank.py +++ b/b2b_ecommerce/migrations/0004_coupon_company_blank.py @@ -1,11 +1,10 @@ # Generated by Django 2.2.4 on 2019-09-11 13:49 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [("b2b_ecommerce", "0003_coupons")] operations = [ diff --git a/b2b_ecommerce/migrations/0005_b2border_contract_number.py b/b2b_ecommerce/migrations/0005_b2border_contract_number.py index e58fabf96..5228bad48 100644 --- a/b2b_ecommerce/migrations/0005_b2border_contract_number.py +++ b/b2b_ecommerce/migrations/0005_b2border_contract_number.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [("b2b_ecommerce", "0004_coupon_company_blank")] operations = [ diff --git a/b2b_ecommerce/migrations/0006_b2border_admin_fix.py b/b2b_ecommerce/migrations/0006_b2border_admin_fix.py index 62746f1b5..eab24700f 100644 --- a/b2b_ecommerce/migrations/0006_b2border_admin_fix.py +++ b/b2b_ecommerce/migrations/0006_b2border_admin_fix.py @@ -1,11 +1,10 @@ # Generated by Django 2.2.10 on 2020-04-06 22:19 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [("b2b_ecommerce", "0005_b2border_contract_number")] operations = [ diff --git a/b2b_ecommerce/migrations/0007_b2bcoupon_reusable.py b/b2b_ecommerce/migrations/0007_b2bcoupon_reusable.py index 63620da3d..797308167 100644 --- a/b2b_ecommerce/migrations/0007_b2bcoupon_reusable.py +++ b/b2b_ecommerce/migrations/0007_b2bcoupon_reusable.py @@ -1,11 +1,10 @@ # Generated by Django 2.2.10 on 2020-07-20 08:24 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [("b2b_ecommerce", "0006_b2border_admin_fix")] operations = [ diff --git a/b2b_ecommerce/migrations/0008_b2b_order_program_run.py b/b2b_ecommerce/migrations/0008_b2b_order_program_run.py index a80fb4ba9..51ba00e2f 100644 --- a/b2b_ecommerce/migrations/0008_b2b_order_program_run.py +++ b/b2b_ecommerce/migrations/0008_b2b_order_program_run.py @@ -1,11 +1,10 @@ # Generated by Django 2.2.10 on 2020-07-22 09:36 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ("courses", "0026_nullify_expiration_date"), ("b2b_ecommerce", "0007_b2bcoupon_reusable"), diff --git a/b2b_ecommerce/migrations/0009_jsonField_from_django_models.py b/b2b_ecommerce/migrations/0009_jsonField_from_django_models.py index 84c8f3a00..98c4df636 100644 --- a/b2b_ecommerce/migrations/0009_jsonField_from_django_models.py +++ b/b2b_ecommerce/migrations/0009_jsonField_from_django_models.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - dependencies = [("b2b_ecommerce", "0008_b2b_order_program_run")] operations = [ diff --git a/b2b_ecommerce/migrations/0010_b2bline.py b/b2b_ecommerce/migrations/0010_b2bline.py index 613ae80de..7bc90fcf9 100644 --- a/b2b_ecommerce/migrations/0010_b2bline.py +++ b/b2b_ecommerce/migrations/0010_b2bline.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.15 on 2022-10-17 15:03 -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models def backpopulate_b2b_lines(apps, schema_editor): @@ -14,7 +14,6 @@ def backpopulate_b2b_lines(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ ("b2b_ecommerce", "0009_jsonField_from_django_models"), ] diff --git a/b2b_ecommerce/migrations/0011_alter_b2bcoupon_coupon_code.py b/b2b_ecommerce/migrations/0011_alter_b2bcoupon_coupon_code.py index 119b13d59..151046057 100644 --- a/b2b_ecommerce/migrations/0011_alter_b2bcoupon_coupon_code.py +++ b/b2b_ecommerce/migrations/0011_alter_b2bcoupon_coupon_code.py @@ -1,11 +1,11 @@ # Generated by Django 3.2.23 on 2024-03-04 12:48 from django.db import migrations, models + import ecommerce.utils class Migration(migrations.Migration): - dependencies = [ ("b2b_ecommerce", "0010_b2bline"), ] diff --git a/b2b_ecommerce/models.py b/b2b_ecommerce/models.py index 0fdccdd2a..7c783c3f8 100644 --- a/b2b_ecommerce/models.py +++ b/b2b_ecommerce/models.py @@ -20,7 +20,6 @@ from mitxpro.models import AuditableModel, AuditModel, TimestampedModel from mitxpro.utils import serialize_model_object - B2B_INTEGRATION_PREFIX = "B2B-" @@ -106,7 +105,7 @@ class B2BCoupon(TimestampedModel, AuditableModel): objects = B2BCouponManager() @classmethod - def get_audit_class(cls): + def get_audit_class(cls): # noqa: D102 return B2BCouponAudit def to_dict(self): @@ -123,7 +122,7 @@ class B2BCouponAudit(AuditModel): coupon = models.ForeignKey(B2BCoupon, null=True, on_delete=models.PROTECT) @classmethod - def get_related_field_name(cls): + def get_related_field_name(cls): # noqa: D102 return "coupon" @@ -149,7 +148,7 @@ class B2BOrder(OrderAbstract, AuditableModel): discount = models.DecimalField( decimal_places=2, max_digits=20, null=True, blank=True ) - contract_number = models.CharField(max_length=50, null=True, blank=True) + contract_number = models.CharField(max_length=50, null=True, blank=True) # noqa: DJ001 program_run = models.ForeignKey( "courses.ProgramRun", blank=True, @@ -170,7 +169,7 @@ def __str__(self): return f"B2BOrder #{self.id}, status={self.status}" @classmethod - def get_audit_class(cls): + def get_audit_class(cls): # noqa: D102 return B2BOrderAudit def to_dict(self): @@ -225,7 +224,7 @@ class B2BOrderAudit(AuditModel): order = models.ForeignKey(B2BOrder, null=True, on_delete=models.PROTECT) @classmethod - def get_related_field_name(cls): + def get_related_field_name(cls): # noqa: D102 return "order" diff --git a/b2b_ecommerce/models_test.py b/b2b_ecommerce/models_test.py index 45f238a87..836dd1f8e 100644 --- a/b2b_ecommerce/models_test.py +++ b/b2b_ecommerce/models_test.py @@ -9,7 +9,6 @@ from b2b_ecommerce.models import B2BCoupon, B2BOrder, B2BOrderAudit from mitxpro.utils import serialize_model_object - pytestmark = pytest.mark.django_db @@ -57,12 +56,12 @@ def test_reference_number(settings): @pytest.mark.parametrize( - "activation_date, expiration_date", + "activation_date, expiration_date", # noqa: PT006 [ - [None, None], - [timezone.now() - timedelta(days=1), timezone.now() + timedelta(days=1)], - [None, timezone.now() + timedelta(days=1)], - [timezone.now() - timedelta(days=1), None], + [None, None], # noqa: PT007 + [timezone.now() - timedelta(days=1), timezone.now() + timedelta(days=1)], # noqa: PT007 + [None, timezone.now() + timedelta(days=1)], # noqa: PT007 + [timezone.now() - timedelta(days=1), None], # noqa: PT007 ], ) def test_get_unexpired_coupon(order_with_coupon, activation_date, expiration_date): @@ -80,11 +79,11 @@ def test_get_unexpired_coupon(order_with_coupon, activation_date, expiration_dat @pytest.mark.parametrize( - "attr_name, attr_value", + "attr_name, attr_value", # noqa: PT006 [ - ["enabled", False], - ["activation_date", timezone.now() + timedelta(days=1)], - ["expiration_date", timezone.now() - timedelta(days=1)], + ["enabled", False], # noqa: PT007 + ["activation_date", timezone.now() + timedelta(days=1)], # noqa: PT007 + ["expiration_date", timezone.now() - timedelta(days=1)], # noqa: PT007 ], ) def test_get_unexpired_coupon_not_found(order_with_coupon, attr_name, attr_value): diff --git a/b2b_ecommerce/signals.py b/b2b_ecommerce/signals.py index be6f875bc..6c68c17c5 100644 --- a/b2b_ecommerce/signals.py +++ b/b2b_ecommerce/signals.py @@ -7,8 +7,11 @@ @receiver(post_save, sender=B2BOrder, dispatch_uid="b2b_order_post_save") def create_b2b_line( - sender, instance, created, **kwargs -): # pylint:disable=unused-argument + sender, # noqa: ARG001 + instance, + created, + **kwargs, # noqa: ARG001 +): """ Create a B2BLine object for each B2BOrder """ diff --git a/b2b_ecommerce/urls.py b/b2b_ecommerce/urls.py index 9871178f7..f25a7dabb 100644 --- a/b2b_ecommerce/urls.py +++ b/b2b_ecommerce/urls.py @@ -9,7 +9,6 @@ ) from mitxpro.views import index - urlpatterns = [ path("api/b2b/checkout/", B2BCheckoutView.as_view(), name="b2b-checkout"), path( diff --git a/b2b_ecommerce/views.py b/b2b_ecommerce/views.py index f19baa5aa..239afbf35 100644 --- a/b2b_ecommerce/views.py +++ b/b2b_ecommerce/views.py @@ -30,7 +30,6 @@ from mitxpro.utils import make_csv_http_response from users.models import User - log = logging.getLogger(__name__) @@ -44,8 +43,11 @@ class B2BCheckoutView(APIView): permission_classes = () def post( - self, request, *args, **kwargs - ): # pylint: disable=too-many-locals,unused-argument + self, + request, + *args, # noqa: ARG002 + **kwargs, # noqa: ARG002 + ): """ Create a new unfulfilled Order from the user's basket and return information used to submit to CyberSource. @@ -58,17 +60,17 @@ def post( contract_number = request.data.get("contract_number") run_id = request.data.get("run_id") except KeyError as ex: - raise ValidationError(f"Missing parameter {ex.args[0]}") + raise ValidationError(f"Missing parameter {ex.args[0]}") # noqa: B904, EM102, TRY200 try: validate_email(email) except DjangoValidationError: - raise ValidationError({"email": "Invalid email"}) + raise ValidationError({"email": "Invalid email"}) # noqa: B904, TRY200 try: num_seats = int(num_seats) except ValueError: - raise ValidationError({"num_seats": "num_seats must be a number"}) + raise ValidationError({"num_seats": "num_seats must be a number"}) # noqa: B904, TRY200 if ( contract_number @@ -144,7 +146,7 @@ class B2BOrderStatusView(APIView): authentication_classes = () permission_classes = () - def get(self, request, *args, **kwargs): # pylint: disable=unused-argument + def get(self, request, *args, **kwargs): # noqa: ARG002 """Return B2B order status and other information about the order needed to display the receipt""" order_hash = kwargs["hash"] order = get_object_or_404(B2BOrder, unique_id=order_hash) @@ -196,7 +198,7 @@ class B2BEnrollmentCodesView(APIView): authentication_classes = () permission_classes = () - def get(self, request, *args, **kwargs): # pylint: disable=unused-argument + def get(self, request, *args, **kwargs): # noqa: ARG002 """Create a CSV with enrollment codes""" order_hash = kwargs["hash"] order = get_object_or_404( @@ -236,14 +238,14 @@ class B2BCouponView(APIView): authentication_classes = () permission_classes = () - def get(self, request, *args, **kwargs): # pylint: disable=unused-argument + def get(self, request, *args, **kwargs): # noqa: ARG002 """Get information about a coupon""" product = None try: coupon_code = request.GET["code"] product_id = request.GET["product_id"] except KeyError as ex: - raise ValidationError(f"Missing parameter {ex.args[0]}") + raise ValidationError(f"Missing parameter {ex.args[0]}") # noqa: B904, EM102, TRY200 try: # product_id can be an integer e.g. 1234 or @@ -259,7 +261,7 @@ def get(self, request, *args, **kwargs): # pylint: disable=unused-argument coupon_code=coupon_code, product_id=product_id ) except B2BCoupon.DoesNotExist: - raise Http404 + raise Http404 # noqa: B904 return Response( data={ diff --git a/b2b_ecommerce/views_test.py b/b2b_ecommerce/views_test.py index 1cdc373a1..9d38478b4 100644 --- a/b2b_ecommerce/views_test.py +++ b/b2b_ecommerce/views_test.py @@ -21,7 +21,6 @@ from mitxpro.utils import dict_without_keys from users.factories import UserFactory - CYBERSOURCE_SECURE_ACCEPTANCE_URL = "http://fake" CYBERSOURCE_ACCESS_KEY = "access" CYBERSOURCE_PROFILE_ID = "profile" @@ -30,11 +29,10 @@ pytestmark = pytest.mark.django_db -# pylint: disable=redefined-outer-name,unused-argument,too-many-lines @pytest.fixture(autouse=True) -def ecommerce_settings(settings): +def ecommerce_settings(settings): # noqa: PT004 """ Set cybersource settings """ @@ -85,7 +83,7 @@ def test_create_order(client, mocker): assert order.num_seats == num_seats assert order.b2breceipt_set.count() == 0 base_url = "http://testserver/" - receipt_url = f'{urljoin(base_url, reverse("bulk-enrollment-code-receipt"))}?hash={str(order.unique_id)}' + receipt_url = f'{urljoin(base_url, reverse("bulk-enrollment-code-receipt"))}?hash={str(order.unique_id)}' # noqa: RUF010 assert generate_mock.call_count == 1 assert generate_mock.call_args[0] == () assert generate_mock.call_args[1] == { @@ -138,7 +136,7 @@ def test_create_order_with_coupon(client, mocker): assert order.num_seats == num_seats assert order.b2breceipt_set.count() == 0 base_url = "http://testserver/" - receipt_url = f'{urljoin(base_url, reverse("bulk-enrollment-code-receipt"))}?hash={str(order.unique_id)}' + receipt_url = f'{urljoin(base_url, reverse("bulk-enrollment-code-receipt"))}?hash={str(order.unique_id)}' # noqa: RUF010 assert generate_payload_mock.call_count == 1 assert generate_payload_mock.call_args[0] == () assert generate_payload_mock.call_args[1] == { @@ -260,7 +258,7 @@ def test_create_order_product_version(client): assert resp.status_code == status.HTTP_404_NOT_FOUND -def test_zero_price_checkout(client, mocker): # pylint:disable=too-many-arguments +def test_zero_price_checkout(client, mocker): """ If the order total is $0, we should just fulfill the order and direct the user to our order receipt page """ @@ -279,7 +277,7 @@ def test_zero_price_checkout(client, mocker): # pylint:disable=too-many-argumen assert B2BOrder.objects.count() == 1 order = B2BOrder.objects.first() base_url = "http://testserver" - receipt_url = f'{urljoin(base_url, reverse("bulk-enrollment-code-receipt"))}?hash={str(order.unique_id)}' + receipt_url = f'{urljoin(base_url, reverse("bulk-enrollment-code-receipt"))}?hash={str(order.unique_id)}' # noqa: RUF010 assert resp.status_code == status.HTTP_200_OK assert resp.json() == {"payload": {}, "url": receipt_url, "method": "GET"} diff --git a/blog/api.py b/blog/api.py index ac7e6c956..f17249c7c 100644 --- a/blog/api.py +++ b/blog/api.py @@ -7,7 +7,6 @@ from django.utils.dateformat import DateFormat from django.utils.dateparse import parse_datetime - log = logging.getLogger() RSS_FEED_URL = "https://curve.mit.edu/rss.xml" diff --git a/blog/api_test.py b/blog/api_test.py index 7f0661184..850cce099 100644 --- a/blog/api_test.py +++ b/blog/api_test.py @@ -16,8 +16,8 @@ def valid_blog_post(): 'src="https://curve.mit.edu/hubfs/Screenshot%202023-10-05%20at%203.55.25%20PM.png" alt="Ask ' 'an MIT Professor: The Science Behind Oppenheimer" class="hs-featured-image" ' 'style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"> ' - "\n \n
It’s not every day you see a topic like quantum physics represented in a hit " - 'summer movie. Yet Christopher Nolan’s Oppenheimer ' + "\n \n
It’s not every day you see a topic like quantum physics represented in a hit " # noqa: RUF001 + 'summer movie. Yet Christopher Nolan’s Oppenheimer ' # noqa: RUF001 "has dazzled audiences everywhere and is on track to earn nearly $1 billion at the global box ' @@ -28,8 +28,8 @@ def valid_blog_post(): 'src="https://curve.mit.edu/hubfs/Screenshot%202023-10-05%20at%203.55.25%20PM.png" alt="Ask ' 'an MIT Professor: The Science Behind Oppenheimer" class="hs-featured-image" ' 'style="width:auto !important; max-width:50%; float:left; margin:0 15px 15px 0;"> ' - "\n \n
It’s not every day you see a topic like quantum physics represented in a hit " - 'summer movie. Yet Christopher Nolan’s Oppenheimer ' + "\n \n
It’s not every day you see a topic like quantum physics represented in a hit " # noqa: RUF001
+ 'summer movie. Yet Christopher Nolan’s Oppenheimer ' # noqa: RUF001
"has dazzled audiences everywhere and is on track to earn nearly $1 billion at the global box '
@@ -43,18 +43,16 @@ def valid_blog_post():
@pytest.mark.parametrize(
- "category, expected_category",
+ "category, expected_category", # noqa: PT006
[
- ["Quantum Computing", ["Quantum Computing"]],
- [
+ ["Quantum Computing", ["Quantum Computing"]], # noqa: PT007
+ [ # noqa: PT007
["Quantum Computing", "Online Education"],
["Quantum Computing", "Online Education"],
],
],
)
-def test_parse_blog(
- category, expected_category, valid_blog_post
-): # pylint: disable=redefined-outer-name
+def test_parse_blog(category, expected_category, valid_blog_post):
"""
Tests that `parse_blog` parses a blog post as required.
"""
@@ -93,8 +91,8 @@ def test_parse_blog(
)
assert (
valid_blog_post["description"]
- == "It’s not every day you see a topic like quantum physics represented in a hit "
- "summer movie. Yet Christopher Nolan’s Oppenheimer has dazzled audiences everywhere"
+ == "It’s not every day you see a topic like quantum physics represented in a hit " # noqa: RUF001
+ "summer movie. Yet Christopher Nolan’s Oppenheimer has dazzled audiences everywhere" # noqa: RUF001
" and is on track to earn nearly $1 billion at the global box office."
)
assert valid_blog_post["categories"] == expected_category
@@ -124,9 +122,7 @@ def test_fetch_blog():
)
-def test_parse_blog_invalid_type_and_data(
- mocker, valid_blog_post
-): # pylint: disable=redefined-outer-name
+def test_parse_blog_invalid_type_and_data(mocker, valid_blog_post):
"""
Test that `parse_blog` logs error when post item type or data is not valid.
"""
diff --git a/cms/api.py b/cms/api.py
index f745a7b98..7f160b0ab 100644
--- a/cms/api.py
+++ b/cms/api.py
@@ -3,16 +3,15 @@
import logging
from datetime import MAXYEAR, datetime, timezone
-
from django.contrib.contenttypes.models import ContentType
from wagtail.models import Page, Site
+
from cms import models as cms_models
from cms.constants import CERTIFICATE_INDEX_SLUG, ENTERPRISE_PAGE_SLUG
-
log = logging.getLogger(__name__)
-DEFAULT_HOMEPAGE_PROPS = dict(title="Home Page", subhead="This is the home page")
-DEFAULT_SITE_PROPS = dict(hostname="localhost", port=80)
+DEFAULT_HOMEPAGE_PROPS = dict(title="Home Page", subhead="This is the home page") # noqa: C408
+DEFAULT_SITE_PROPS = dict(hostname="localhost", port=80) # noqa: C408
def filter_and_sort_catalog_pages(
@@ -136,7 +135,7 @@ def ensure_catalog_page():
catalog_page.refresh_from_db()
-def ensure_index_pages(): # pylint: disable=too-many-branches
+def ensure_index_pages(): # noqa: C901
"""
Ensures that the proper index pages exist as children of the home page, and that
any pages that should belong to those index pages are set as children.
@@ -203,7 +202,7 @@ def ensure_index_pages(): # pylint: disable=too-many-branches
def ensure_enterprise_page():
"""
- Ensures that an enterprise page with the correct slug exists.
+ Ensure that an enterprise page with the correct slug exists.
"""
enterprise_page = cms_models.EnterprisePage.objects.first()
@@ -213,7 +212,7 @@ def ensure_enterprise_page():
enterprise_page_data = {
"title": "Enterprise Page",
"slug": ENTERPRISE_PAGE_SLUG,
- "description": "Deepen your team’s career knowledge and expand their abilities with MIT xPRO’s online "
+ "description": "Deepen your team's career knowledge and expand their abilities with MIT xPRO's online "
"courses for professionals.",
"action_title": "Find out what MIT xPRO can do for your team.",
"headings": [
diff --git a/cms/api_test.py b/cms/api_test.py
index 5fae1cf98..a960cefa7 100644
--- a/cms/api_test.py
+++ b/cms/api_test.py
@@ -2,16 +2,17 @@
from datetime import timedelta
import pytest
+
+from cms.api import filter_and_sort_catalog_pages
from cms.factories import ExternalCoursePageFactory, ExternalProgramPageFactory
from cms.models import ExternalCoursePage
-from cms.api import filter_and_sort_catalog_pages
from courses.factories import CourseRunFactory, ProgramRunFactory
from mitxpro.utils import now_in_utc
pytestmark = pytest.mark.django_db
-def test_filter_and_sort_catalog_pages(): # pylint:disable=too-many-locals
+def test_filter_and_sort_catalog_pages():
"""
Test that filter_and_sort_catalog_pages removes program/course/external course pages that do not have a future start date
or enrollment end date, and returns appropriately sorted lists of pages
diff --git a/cms/blocks.py b/cms/blocks.py
index bd26451f1..cf498d635 100644
--- a/cms/blocks.py
+++ b/cms/blocks.py
@@ -164,7 +164,7 @@ def validate_unique_readable_ids(value):
unique readable IDs
"""
# We want to validate the overall stream not underlying blocks individually
- if len(value) < 2:
+ if len(value) < 2: # noqa: PLR2004
return
items = [
stream_block.value.get("readable_id")
diff --git a/cms/embeds.py b/cms/embeds.py
index 771f5034f..10354f08f 100644
--- a/cms/embeds.py
+++ b/cms/embeds.py
@@ -4,9 +4,8 @@
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
-from django.core.exceptions import ImproperlyConfigured
-
from bs4 import BeautifulSoup
+from django.core.exceptions import ImproperlyConfigured
from wagtail.embeds.finders.oembed import OEmbedFinder
from wagtail.embeds.oembed_providers import youtube
@@ -25,12 +24,12 @@ def __init__(self, providers=None, options=None):
if providers != [youtube]:
raise ImproperlyConfigured(
- "The YouTubeEmbedFinder only operates on the youtube provider"
+ "The YouTubeEmbedFinder only operates on the youtube provider" # noqa: EM101
)
super().__init__(providers=providers, options=options)
- def find_embed(self, url, max_width=None): # pylint: disable=arguments-differ
+ def find_embed(self, url, max_width=None): # noqa: D102
embed = super().find_embed(url, max_width)
embed_tag = BeautifulSoup(embed["html"], "html.parser")
player_iframe = embed_tag.find("iframe")
diff --git a/cms/factories.py b/cms/factories.py
index 83298ea60..dd309b94e 100644
--- a/cms/factories.py
+++ b/cms/factories.py
@@ -53,7 +53,6 @@
)
from courses.factories import CourseFactory, ProgramFactory
-
factory.Faker.add_provider(internet)
FAKE = faker.Factory.create()
@@ -84,7 +83,7 @@ class Meta:
model = ProgramPage
@factory.post_generation
- def post_gen(obj, create, extracted, **kwargs): # pylint:disable=unused-argument
+ def post_gen(obj, create, extracted, **kwargs): # noqa: ARG002, N805
"""Post-generation hook"""
if create:
# Move the created page to be a child of the program index page
@@ -114,7 +113,7 @@ class Meta:
model = CoursePage
@factory.post_generation
- def post_gen(obj, create, extracted, **kwargs): # pylint:disable=unused-argument
+ def post_gen(obj, create, extracted, **kwargs): # noqa: ARG002, N805
"""Post-generation hook"""
if create:
# Move the created page to be a child of the course index page
@@ -143,7 +142,7 @@ class Meta:
model = ExternalCoursePage
@factory.post_generation
- def post_gen(obj, create, extracted, **kwargs): # pylint:disable=unused-argument
+ def post_gen(obj, create, extracted, **kwargs): # noqa: ARG002, N805
"""Post-generation hook"""
if create:
# Move the created page to be a child of the course index page
@@ -172,7 +171,7 @@ class Meta:
model = ExternalProgramPage
@factory.post_generation
- def post_gen(obj, create, extracted, **kwargs): # pylint:disable=unused-argument
+ def post_gen(obj, create, extracted, **kwargs): # noqa: ARG002, N805
"""Post-generation hook"""
if create:
# Move the created page to be a child of the program index page
@@ -375,9 +374,7 @@ class FacultyBlockFactory(wagtail_factories.StructBlockFactory):
name = factory.Faker("name")
image = factory.SubFactory(wagtail_factories.ImageChooserBlockFactory)
- description = factory.LazyFunction(
- lambda: RichText(" {} {FAKE.paragraph()} subheading subheading item item