diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3face93f9..a6797d7b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ jobs: services: db: - image: postgres:15.7 + image: postgres:15.8 # Health checks to wait until postgres has started options: >- --health-cmd pg_isready @@ -45,20 +45,32 @@ jobs: - name: Apt install run: cat Aptfile | sudo xargs apt-get install + - name: Set up Python + id: setup-python + uses: actions/setup-python@v4 + with: + python-version: "3.12.5" + - name: Install poetry uses: snok/install-poetry@v1 with: version: 1.8.2 virtualenvs-create: true virtualenvs-in-project: true + virtualenvs-path: .venv - - name: Set up Python - uses: actions/setup-python@v4 + - name: Load cached venv + id: cached-poetry-dependencies + uses: actions/cache@v4 with: - python-version: "3.9.15" - cache: "poetry" + path: .venv + key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - name: Install dependencies + if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' + run: poetry install --no-interaction --no-root + + - name: Install project run: poetry install --no-interaction # Configurations required for elasticsearch. @@ -93,6 +105,7 @@ jobs: - name: Tests run: | + source .venv/bin/activate export MEDIA_ROOT="$(mktemp -d)" poetry run ./scripts/test/python_tests.sh env: diff --git a/.secrets.baseline b/.secrets.baseline index 70560a3d2..1af411940 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -266,16 +266,16 @@ "filename": "users/api.py", "hashed_secret": "f205fad2d580e981bb53020ed8f77c0cb7c35014", "is_verified": false, - "line_number": 165 + "line_number": 163 }, { "type": "Base64 High Entropy String", "filename": "users/api.py", "hashed_secret": "915109282e07e7e73fb6939dd221f675e60d118f", "is_verified": false, - "line_number": 168 + "line_number": 166 } ] }, - "generated_at": "2024-05-22T09:32:37Z" + "generated_at": "2024-08-06T16:53:29Z" } diff --git a/Dockerfile b/Dockerfile index eda5e1701..061f4f086 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9.15 +FROM python:3.12.5 LABEL maintainer "ODL DevOps " # Add package files, install updated node and pip diff --git a/RELEASE.rst b/RELEASE.rst index 064c80e7a..47d95bd9b 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -1,6 +1,23 @@ Release Notes ============= +Version 0.159.0 +--------------- + +- fix(deps): update dependency django-robots to v6.1 +- fix(deps): update dependency django-hijack to v3.6.0 +- fix(deps): update dependency boto3 to v1.35.8 +- chore(deps): update postgres docker tag to v15.8 (#3117) +- chore(deps): update node.js to v20.17.0 (#3116) +- chore(deps): update nginx docker tag to v1.27.1 (#3114) +- fix(deps): update python to v3.12.5 (#3003) +- chore(deps): lock file maintenance (#3113) +- feat: display tax details for countries where taxes are enabled (#3109) +- fix(deps): update dependency zeep to v4 (#3062) +- fix(deps): update dependency pygsheets to v2.0.6 (#2736) +- fix(deps): update dependency webpack to v5.94.0 [security] +- Python upgrade from 3.9.x to 3.12.x (#3089) + Version 0.158.0 (Released August 29, 2024) --------------- diff --git a/affiliate/factories.py b/affiliate/factories.py index 3edd60728..e802f932d 100644 --- a/affiliate/factories.py +++ b/affiliate/factories.py @@ -12,7 +12,7 @@ class AffiliateFactory(DjangoModelFactory): """Factory for Affiliate""" - code = factory.Sequence("affiliate-code-{0}".format) + code = factory.Sequence("affiliate-code-{}".format) name = fuzzy.FuzzyText(prefix="Affiliate ", length=30) class Meta: diff --git a/authentication/api.py b/authentication/api.py index d7430896a..dd5771dae 100644 --- a/authentication/api.py +++ b/authentication/api.py @@ -57,7 +57,7 @@ def create_user_with_generated_username(serializer, initial_username): while created_user is None and attempts < USERNAME_COLLISION_ATTEMPTS: try: created_user = serializer.save(username=username) - except IntegrityError as exc: # noqa: PERF203 + except IntegrityError as exc: if not is_duplicate_username_error(exc): raise username = find_available_username(initial_username) diff --git a/authentication/middleware.py b/authentication/middleware.py index 258254dbd..f0d517f32 100644 --- a/authentication/middleware.py +++ b/authentication/middleware.py @@ -30,7 +30,7 @@ def process_exception(self, request, exception): url = self.get_redirect_uri(request, exception) if url: # noqa: RET503 - url += ("?" in url and "&" or "?") + "message={0}&backend={1}".format( # noqa: UP030, UP032 + url += ("?" in url and "&" or "?") + "message={}&backend={}".format( # noqa: UP032 quote(message), backend_name ) return redirect(url) diff --git a/authentication/pipeline/user.py b/authentication/pipeline/user.py index 98ec916d5..89c3e2b7e 100644 --- a/authentication/pipeline/user.py +++ b/authentication/pipeline/user.py @@ -149,7 +149,7 @@ def create_user_via_email( created_user = create_user_with_generated_username(serializer, username) if created_user is None: raise IntegrityError( # noqa: TRY301 - "Failed to create User with generated username ({})".format(username) # noqa: EM103, UP032 + f"Failed to create User with generated username ({username})" # noqa: EM102 ) except Exception as exc: raise UserCreationFailedException(backend, current_partial) from exc diff --git a/b2b_ecommerce/factories.py b/b2b_ecommerce/factories.py index 8c9d2a0f7..85b82c224 100644 --- a/b2b_ecommerce/factories.py +++ b/b2b_ecommerce/factories.py @@ -1,6 +1,6 @@ """Factories for b2b_ecommerce""" -from datetime import timezone +from datetime import UTC import factory from factory import fuzzy @@ -46,10 +46,10 @@ class B2BCouponFactory(DjangoModelFactory): name = fuzzy.FuzzyText() coupon_code = fuzzy.FuzzyText() activation_date = factory.Faker( - "date_time_this_year", before_now=True, after_now=False, tzinfo=timezone.utc + "date_time_this_year", before_now=True, after_now=False, tzinfo=UTC ) expiration_date = factory.Faker( - "date_time_this_year", before_now=False, after_now=True, tzinfo=timezone.utc + "date_time_this_year", before_now=False, after_now=True, tzinfo=UTC ) enabled = True reusable = False diff --git a/cms/api.py b/cms/api.py index 083d132a8..4704dff3d 100644 --- a/cms/api.py +++ b/cms/api.py @@ -2,7 +2,7 @@ import itertools import logging -from datetime import MAXYEAR, datetime, timezone +from datetime import MAXYEAR, UTC, datetime from django.contrib.contenttypes.models import ContentType from wagtail.models import Page, Site @@ -44,7 +44,7 @@ def filter_and_sort_catalog_pages( page_run_dates = { page: page.product.next_run_date - or datetime(year=MAXYEAR, month=1, day=1, tzinfo=timezone.utc) + or datetime(year=MAXYEAR, month=1, day=1, tzinfo=UTC) for page in itertools.chain( valid_program_pages, valid_course_pages, diff --git a/cms/factories.py b/cms/factories.py index 5398fb33b..8368ff4ab 100644 --- a/cms/factories.py +++ b/cms/factories.py @@ -69,7 +69,7 @@ class Meta: class ProgramPageFactory(wagtail_factories.PageFactory): """ProgramPage factory class""" - title = factory.Sequence("Test page - Program {0}".format) + title = factory.Sequence("Test page - Program {}".format) program = factory.SubFactory(ProgramFactory, page=None) subhead = factory.fuzzy.FuzzyText(prefix="Subhead ") thumbnail_image = factory.SubFactory(wagtail_factories.ImageFactory) @@ -99,7 +99,7 @@ def post_gen(obj, create, extracted, **kwargs): # noqa: ARG002, N805 class CoursePageFactory(wagtail_factories.PageFactory): """CoursePage factory class""" - title = factory.Sequence("Test page - Course {0}".format) + title = factory.Sequence("Test page - Course {}".format) course = factory.SubFactory(CourseFactory, page=None) subhead = factory.fuzzy.FuzzyText(prefix="Subhead ") thumbnail_image = factory.SubFactory(wagtail_factories.ImageFactory) @@ -131,7 +131,7 @@ class ExternalCoursePageFactory(wagtail_factories.PageFactory): course = factory.SubFactory(CourseFactory, page=None) - title = factory.Sequence("Test page - External Course {0}".format) + title = factory.Sequence("Test page - External Course {}".format) external_marketing_url = factory.Faker("uri") marketing_hubspot_form_id = factory.Faker("bothify", text="??????????") subhead = factory.fuzzy.FuzzyText(prefix="Subhead ") @@ -160,7 +160,7 @@ class ExternalProgramPageFactory(wagtail_factories.PageFactory): program = factory.SubFactory(ProgramFactory, page=None) - title = factory.Sequence("Test page - External Program {0}".format) + title = factory.Sequence("Test page - External Program {}".format) external_marketing_url = factory.Faker("uri") marketing_hubspot_form_id = factory.Faker("bothify", text="??????????") subhead = factory.fuzzy.FuzzyText(prefix="Subhead ") diff --git a/cms/forms.py b/cms/forms.py index 31f18f5ff..1259fdf1d 100644 --- a/cms/forms.py +++ b/cms/forms.py @@ -26,7 +26,7 @@ def clean(self): cleaned_data = super().clean() parent_page = self.parent_page.specific - if (isinstance(parent_page, (CoursePage, ProgramPage))) and not cleaned_data[ + if (isinstance(parent_page, (CoursePage, ProgramPage))) and not cleaned_data[ # noqa: UP038 "signatories" ]: self.add_error("signatories", "Signatories is a required field.") diff --git a/cms/migrations/0054_create_external_courseware_asociations.py b/cms/migrations/0054_create_external_courseware_asociations.py index c66edbce5..58027ae25 100644 --- a/cms/migrations/0054_create_external_courseware_asociations.py +++ b/cms/migrations/0054_create_external_courseware_asociations.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.18 on 2023-03-16 12:26 -from datetime import datetime, timezone +from datetime import UTC, datetime import django.db.models.deletion from django.db import migrations, models @@ -13,7 +13,7 @@ def get_zone_aware_datetime(date): """Takes a date object and returns a zone aware datetime""" - return datetime.combine(date, datetime.max.time(), timezone.utc) if date else None + return datetime.combine(date, datetime.max.time(), UTC) if date else None def check_and_generate_associated_product( diff --git a/cms/models.py b/cms/models.py index 4ebd92c32..71aa94dad 100644 --- a/cms/models.py +++ b/cms/models.py @@ -214,7 +214,7 @@ def get_context(self, request, *args, **kwargs): .exclude(Q(category=UPCOMING_WEBINAR) & Q(date__lt=now_in_utc().date())) .order_by("-category", "date") ) - webinars_dict = defaultdict(lambda: []) # noqa: PIE807 + webinars_dict = defaultdict(list) for webinar in webinars: webinar.detail_page_url = webinar.detail_page_url(request) webinars_dict[webinar.category].append(webinar) @@ -1028,7 +1028,7 @@ def get_url_parts(self, request=None): # of the Course/Program instead (e.g.: "/courses/course-v1:edX+DemoX+Demo_Course") re.sub( self.slugged_page_path_pattern, - r"\1{}\3".format(self.product.readable_id), # noqa: UP032 + rf"\1{self.product.readable_id}\3", url_parts[2], ), ) @@ -1123,7 +1123,7 @@ def is_course_page(self): @property def is_internal_or_external_course_page(self): """Gets the product page type, this is used for sorting product pages.""" - return isinstance(self, (CoursePage, ExternalCoursePage)) + return isinstance(self, (CoursePage, ExternalCoursePage)) # noqa: UP038 @property def external_courseware_url(self): @@ -1148,7 +1148,7 @@ def is_program_page(self): @property def is_internal_or_external_program_page(self): """Check whether the page is an internal or external program page.""" - return isinstance(self, (ProgramPage, ExternalProgramPage)) + return isinstance(self, (ProgramPage, ExternalProgramPage)) # noqa: UP038 @property def is_external_page(self): @@ -1508,7 +1508,7 @@ class Meta: def can_create_at(cls, parent): # You can only create one of these page under course / program. return ( - super(CourseProgramChildPage, cls).can_create_at(parent) # noqa: UP008 + super().can_create_at(parent) and parent.get_children().type(cls).count() == 0 ) diff --git a/compliance/test_utils.py b/compliance/test_utils.py index 0d950bad0..40bf0e620 100644 --- a/compliance/test_utils.py +++ b/compliance/test_utils.py @@ -33,14 +33,15 @@ def mock_cybersource_wsdl(mocked_responses, settings, service_version=SERVICE_VE Mocks the responses to achieve a functional WSDL """ # in order for zeep to load the wsdl, it will load the wsdl and the accompanying xsd definitions - with open(f"{DATA_DIR}/CyberSourceTransaction_{service_version}.wsdl", "r") as wsdl: # noqa: PTH123, UP015 + # Note: open() defaults to read mode ("r") + with open(f"{DATA_DIR}/CyberSourceTransaction_{service_version}.wsdl") as wsdl: # noqa: PTH123 mocked_responses.add( mocked_responses.GET, settings.CYBERSOURCE_WSDL_URL, body=wsdl.read(), status=status.HTTP_200_OK, ) - with open(f"{DATA_DIR}/CyberSourceTransaction_{SERVICE_VERSION}.xsd", "r") as xsd: # noqa: PTH123, UP015 + with open(f"{DATA_DIR}/CyberSourceTransaction_{SERVICE_VERSION}.xsd") as xsd: # noqa: PTH123 mocked_responses.add( mocked_responses.GET, f"http://localhost/service/CyberSourceTransaction_{service_version}.xsd", diff --git a/courses/api.py b/courses/api.py index 350bc4af1..781b91cb2 100644 --- a/courses/api.py +++ b/courses/api.py @@ -57,7 +57,7 @@ def get_user_enrollments(user): for program_enrollment in program_enrollments ) ) - program_course_ids = set(course.id for course in program_courses) # noqa: C401 + program_course_ids = {course.id for course in program_courses} course_run_enrollments = ( CourseRunEnrollment.objects.select_related("run__course__coursepage", "company") .filter(user=user) @@ -143,7 +143,7 @@ def create_run_enrollments( if not created and not enrollment.active: enrollment.edx_enrolled = edx_request_success enrollment.reactivate_and_save() - except: # noqa: E722, PERF203 + except: # noqa: E722 mail_api.send_enrollment_failure_message(order, run, details=format_exc()) log.exception( "Failed to create/update enrollment record (user: %s, run: %s, order: %s)", @@ -184,7 +184,7 @@ def create_program_enrollments(user, programs, order=None, company=None): ) if not created and not enrollment.active: enrollment.reactivate_and_save() - except: # noqa: E722, PERF203 + except: # noqa: E722 mail_api.send_enrollment_failure_message( order, program, details=format_exc() ) @@ -316,7 +316,7 @@ def defer_enrollment( to_run = CourseRun.objects.get(courseware_id=to_courseware_id) if from_enrollment.run == to_run: raise ValidationError( - "Cannot defer to the same course run (run: {})".format(to_run.courseware_id) # noqa: EM103, UP032 + f"Cannot defer to the same course run (run: {to_run.courseware_id})" # noqa: EM102 ) if not force and not to_run.is_not_beyond_enrollment: raise ValidationError( diff --git a/courses/credentials.py b/courses/credentials.py index cafcf2ed7..a2a31a621 100644 --- a/courses/credentials.py +++ b/courses/credentials.py @@ -1,7 +1,6 @@ """Digital courseware credentials""" import logging -from typing import Union from urllib.parse import urljoin from django.conf import settings @@ -81,7 +80,7 @@ def build_course_run_credential(certificate: CourseRunCertificate) -> dict: def build_digital_credential( - certificate: Union[ProgramCertificate, CourseRunCertificate], # noqa: FA100 + certificate: ProgramCertificate | CourseRunCertificate, learner_did: LearnerDID, ) -> dict: """Function for building certificate digital credentials""" diff --git a/courses/factories.py b/courses/factories.py index 23fd4c807..4ef1c89cb 100644 --- a/courses/factories.py +++ b/courses/factories.py @@ -1,6 +1,6 @@ """Factories for creating course data in tests""" -from datetime import timezone +from datetime import UTC import factory import faker @@ -65,7 +65,7 @@ class ProgramRunFactory(DjangoModelFactory): """Factory for ProgramRuns""" program = factory.SubFactory(ProgramFactory) - run_tag = factory.Sequence("R{0}".format) + run_tag = factory.Sequence("R{}".format) class Meta: model = ProgramRun @@ -77,7 +77,7 @@ class CourseFactory(DjangoModelFactory): program = factory.SubFactory(ProgramFactory) position_in_program = None # will get populated in save() title = fuzzy.FuzzyText(prefix="Course ") - readable_id = factory.Sequence("course-{0}".format) + readable_id = factory.Sequence("course-{}".format) platform = factory.SubFactory(PlatformFactory) live = True @@ -96,22 +96,22 @@ class CourseRunFactory(DjangoModelFactory): course = factory.SubFactory(CourseFactory) title = factory.LazyAttribute(lambda x: "CourseRun " + FAKE.sentence()) # noqa: ARG005 courseware_id = factory.Sequence(lambda number: f"course:v{number}+{FAKE.slug()}") - run_tag = factory.Sequence("R{0}".format) + run_tag = factory.Sequence("R{}".format) courseware_url_path = factory.Faker("uri") start_date = factory.Faker( - "date_time_this_month", before_now=True, after_now=False, tzinfo=timezone.utc + "date_time_this_month", before_now=True, after_now=False, tzinfo=UTC ) end_date = factory.Faker( - "date_time_this_year", before_now=False, after_now=True, tzinfo=timezone.utc + "date_time_this_year", before_now=False, after_now=True, tzinfo=UTC ) enrollment_start = factory.Faker( - "date_time_this_month", before_now=True, after_now=False, tzinfo=timezone.utc + "date_time_this_month", before_now=True, after_now=False, tzinfo=UTC ) enrollment_end = factory.Faker( - "date_time_this_month", before_now=False, after_now=True, tzinfo=timezone.utc + "date_time_this_month", before_now=False, after_now=True, tzinfo=UTC ) expiration_date = factory.Faker( - "date_time_between", start_date="+1y", end_date="+2y", tzinfo=timezone.utc + "date_time_between", start_date="+1y", end_date="+2y", tzinfo=UTC ) live = True @@ -120,10 +120,10 @@ class Meta: class Params: past_start = factory.Trait( - start_date=factory.Faker("past_datetime", tzinfo=timezone.utc) + start_date=factory.Faker("past_datetime", tzinfo=UTC) ) past_enrollment_end = factory.Trait( - enrollment_end=factory.Faker("past_datetime", tzinfo=timezone.utc) + enrollment_end=factory.Faker("past_datetime", tzinfo=UTC) ) diff --git a/courses/management/commands/defer_enrollment.py b/courses/management/commands/defer_enrollment.py index 3764bc747..a05602570 100644 --- a/courses/management/commands/defer_enrollment.py +++ b/courses/management/commands/defer_enrollment.py @@ -63,12 +63,12 @@ def handle(self, *args, **options): # noqa: ARG002 if isinstance(exc, CourseRunEnrollment.DoesNotExist): message = f"'from' course run enrollment does not exist ({from_courseware_id})" elif isinstance(exc, CourseRun.DoesNotExist): - message = "'to' course does not exist ({})".format(to_courseware_id) # noqa: UP032 + message = f"'to' course does not exist ({to_courseware_id})" else: message = str(exc) raise CommandError(message) # noqa: B904 except ValidationError as exc: - raise CommandError("Invalid enrollment deferral - {}".format(exc)) # noqa: B904, EM103, UP032 + raise CommandError(f"Invalid enrollment deferral - {exc}") # noqa: B904, EM102 else: if not to_enrollment: raise CommandError( diff --git a/courses/management/commands/revoke_certificate.py b/courses/management/commands/revoke_certificate.py index c9a53e074..37898bbe5 100644 --- a/courses/management/commands/revoke_certificate.py +++ b/courses/management/commands/revoke_certificate.py @@ -81,7 +81,7 @@ def handle(self, *args, **options): # noqa: ARG002 if updated: msg = "Certificate for {} has been {}".format( - "run: {}".format(run) if run else "program: {}".format(program), # noqa: UP032 + f"run: {run}" if run else f"program: {program}", "revoked" if revoke else "un-revoked", ) self.stdout.write(self.style.SUCCESS(msg)) diff --git a/courses/management/commands/sync_grades_and_certificates.py b/courses/management/commands/sync_grades_and_certificates.py index 94446781f..487d26f78 100644 --- a/courses/management/commands/sync_grades_and_certificates.py +++ b/courses/management/commands/sync_grades_and_certificates.py @@ -130,11 +130,9 @@ def handle( # noqa: C901, PLR0915 else: grade_status = "already exists" - grade_summary = ["passed: {}".format(course_run_grade.passed)] # noqa: UP032 + grade_summary = [f"passed: {course_run_grade.passed}"] if override_grade is not None: - grade_summary.append( - "value override: {}".format(course_run_grade.grade) # noqa: UP032 - ) + grade_summary.append(f"value override: {course_run_grade.grade}") if created_cert: cert_status = "created" diff --git a/courses/management/utils.py b/courses/management/utils.py index f1c830a5a..b6de101bf 100644 --- a/courses/management/utils.py +++ b/courses/management/utils.py @@ -112,7 +112,7 @@ def fetch_enrollment(user, command_options): enrollment = CourseRunEnrollment.all_objects.filter(**query_params).first() if not enrollment: - raise CommandError("Enrollment not found for: {}".format(enrolled_obj)) # noqa: EM103, UP032 + raise CommandError(f"Enrollment not found for: {enrolled_obj}") # noqa: EM102 if not enrollment.active and not force: raise CommandError( "The given enrollment is not active ({}).\n" # noqa: EM103, UP032, RUF100 diff --git a/courses/migrations/0029_revert_certificates_prior_aug_8.py b/courses/migrations/0029_revert_certificates_prior_aug_8.py index 578587de9..1c7c3ce58 100644 --- a/courses/migrations/0029_revert_certificates_prior_aug_8.py +++ b/courses/migrations/0029_revert_certificates_prior_aug_8.py @@ -1,14 +1,14 @@ # Generated by Django 3.2.15 on 2022-10-26 15:40 -from datetime import datetime, timezone +from datetime import UTC, datetime from django.db import migrations from cms.models import CertificatePage from courses.models import CourseRunCertificate, ProgramCertificate -AUGUST_8_2022 = datetime(2022, 8, 8, tzinfo=timezone.utc) -SEPTEMBER_20_2022 = datetime(2022, 9, 20, tzinfo=timezone.utc) +AUGUST_8_2022 = datetime(2022, 8, 8, tzinfo=UTC) +SEPTEMBER_20_2022 = datetime(2022, 9, 20, tzinfo=UTC) def get_course_certificate_cms_page(course_run_cert): diff --git a/courses/models.py b/courses/models.py index 915cbf923..89bb3a36b 100644 --- a/courses/models.py +++ b/courses/models.py @@ -200,7 +200,7 @@ def catalog_image_url(self): validate_url_path_field = RegexValidator( - r"^[{}]+$".format(detail_path_char_pattern), # noqa: UP032 + rf"^[{detail_path_char_pattern}]+$", f"This field is used to produce URL paths. It must contain only characters that match this pattern: [{detail_path_char_pattern}]", ) diff --git a/courses/serializers.py b/courses/serializers.py index dae0479f6..f2422ccd1 100644 --- a/courses/serializers.py +++ b/courses/serializers.py @@ -371,12 +371,12 @@ def get_instructors(self, instance): def get_topics(self, instance): """List all topics in all courses in the program""" - topics = set( # noqa: C401 + topics = { topic.name for course in instance.courses.all() if course.page for topic in course.page.topics.all() - ) + } return [{"name": topic} for topic in sorted(topics)] def get_time_commitment(self, instance): diff --git a/courses/serializers_test.py b/courses/serializers_test.py index 7543c6ca5..5491839d4 100644 --- a/courses/serializers_test.py +++ b/courses/serializers_test.py @@ -2,7 +2,7 @@ Tests for course serializers """ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import factory import pytest @@ -106,7 +106,7 @@ def test_serialize_program( # noqa: PLR0913 non_live_run = CourseRunFactory.create( course=course1, - end_date=datetime.max.astimezone(timezone.utc), + end_date=datetime.max.replace(tzinfo=UTC), expiration_date=None, live=False, ) @@ -221,7 +221,7 @@ def test_serialize_course( # noqa: PLR0913 marketing_hubspot_form_id, ): """Test Course serialization""" - now = datetime.now(tz=timezone.utc) + now = datetime.now(tz=UTC) if is_anonymous: mock_context["request"].user = AnonymousUser() if all_runs: diff --git a/courses/sync_external_courses/emeritus_api.py b/courses/sync_external_courses/emeritus_api.py index a88e709d4..5b9cb62d4 100644 --- a/courses/sync_external_courses/emeritus_api.py +++ b/courses/sync_external_courses/emeritus_api.py @@ -1,3 +1,5 @@ +"""API for Emeritus course sync""" + import json import logging import re diff --git a/courses/tasks.py b/courses/tasks.py index 862b0b3c8..a546c1acd 100644 --- a/courses/tasks.py +++ b/courses/tasks.py @@ -88,7 +88,7 @@ def exception_logging_generator(generator): while True: try: yield next(generator) - except StopIteration: # noqa: PERF203 + except StopIteration: return except HTTPError as exc: log.exception("EdX API error for fetching user grades %s:", exc) # noqa: TRY401 diff --git a/courses/utils.py b/courses/utils.py index 9b7610ffb..7f6b7aa1e 100644 --- a/courses/utils.py +++ b/courses/utils.py @@ -271,7 +271,7 @@ def sync_course_runs(runs): course_id=run.courseware_id, username=settings.OPENEDX_SERVICE_WORKER_USERNAME, ) - except HTTPError as e: # noqa: PERF203 + except HTTPError as e: failure_count += 1 if e.response.status_code == HTTP_404_NOT_FOUND: log.error( # noqa: TRY400 diff --git a/courses/views_test.py b/courses/views_test.py index b65581c23..7cb16e424 100644 --- a/courses/views_test.py +++ b/courses/views_test.py @@ -282,14 +282,13 @@ def test_course_view( # noqa: PLR0913 url = reverse("user-dashboard") class_name = "enrolled" + # Note: UTF-8 is the default encoding in Python 3. assert ( - f''.encode("utf-8") # noqa: UP012 - in resp.content + f''.encode() in resp.content ) is has_button - assert ( - "Please Sign In to MITx PRO to enroll in a course".encode("utf-8") # noqa: UP012 - in resp.content - ) is (is_anonymous and has_product and has_unexpired_run) + assert (b"Please Sign In to MITx PRO to enroll in a course" in resp.content) is ( + is_anonymous and has_product and has_unexpired_run + ) @pytest.mark.parametrize("is_enrolled", [True, False]) @@ -348,14 +347,13 @@ def test_program_view( # noqa: PLR0913 url = reverse("user-dashboard") class_name = "enrolled" + # Note: UTF-8 is the default encoding in Python 3. assert ( - f''.encode("utf-8") # noqa: UP012 - in resp.content + f''.encode() in resp.content ) is has_button - assert ( - "Please Sign In to MITx PRO to enroll in a course".encode("utf-8") # noqa: UP012 - in resp.content - ) is (is_anonymous and has_product and has_unexpired_run) + assert (b"Please Sign In to MITx PRO to enroll in a course" in resp.content) is ( + is_anonymous and has_product and has_unexpired_run + ) def test_user_enrollments_view(mocker, client, user): diff --git a/courseware/api.py b/courseware/api.py index 9f223f425..20e04e2fb 100644 --- a/courseware/api.py +++ b/courseware/api.py @@ -413,7 +413,7 @@ def repair_faulty_courseware_users(): # edX is our only courseware for the time being. If a different courseware is added, this # function will need to be updated. created_user, created_auth_token = repair_faulty_edx_user(user) - except HTTPError as exc: # noqa: PERF203 + except HTTPError as exc: log.exception( "Failed to repair faulty user %s (%s). %s", user.username, @@ -510,7 +510,7 @@ def get_edx_api_client(user, ttl_in_seconds=OPENEDX_AUTH_DEFAULT_TTL_IN_SECONDS) auth = get_valid_edx_api_auth(user, ttl_in_seconds=ttl_in_seconds) except OpenEdxApiAuth.DoesNotExist: raise NoEdxApiAuthError( # noqa: B904 - "{} does not have an associated OpenEdxApiAuth".format(str(user)) # noqa: EM103, UP032 + f"{user!s} does not have an associated OpenEdxApiAuth" # noqa: EM102 ) return EdxApi( {"access_token": auth.access_token}, @@ -597,7 +597,7 @@ def get_edx_grades_with_users(course_run, user=None): for edx_grade in all_grades: try: user = User.objects.get(username=edx_grade.username) - except User.DoesNotExist: # noqa: PERF203 + except User.DoesNotExist: log.warning("User with username %s not found", edx_grade.username) else: yield edx_grade, user @@ -651,7 +651,7 @@ def enroll_in_edx_course_runs(user, course_runs, force_enrollment=True): # noqa force_enrollment=force_enrollment, ) results.append(result) - except HTTPError as exc: # noqa: PERF203 + except HTTPError as exc: # If there is an error message and it indicates that the preferred enrollment mode was the cause of the # error, log an error and try to enroll the user in 'audit' mode as a failover. if not is_json_response(exc.response): @@ -815,7 +815,7 @@ def create_oauth_application(): defaults=dict( # noqa: C408 redirect_uris=urljoin( settings.OPENEDX_BASE_REDIRECT_URL, - "/auth/complete/{}/".format(settings.MITXPRO_OAUTH_PROVIDER), # noqa: UP032 + f"/auth/complete/{settings.MITXPRO_OAUTH_PROVIDER}/", ), client_type="confidential", authorization_grant_type="authorization-code", diff --git a/courseware/factories.py b/courseware/factories.py index 7dff6a385..f8f15c423 100644 --- a/courseware/factories.py +++ b/courseware/factories.py @@ -1,6 +1,6 @@ """Courseware factories""" -from datetime import timedelta, timezone +from datetime import UTC, timedelta from factory import Faker, LazyAttribute, SubFactory, Trait from factory.django import DjangoModelFactory @@ -28,7 +28,7 @@ class OpenEdxApiAuthFactory(DjangoModelFactory): refresh_token = Faker("pystr", max_chars=30) access_token = Faker("pystr", max_chars=30) access_token_expires_on = Faker( - "date_time_between", start_date="+1d", end_date="+2d", tzinfo=timezone.utc + "date_time_between", start_date="+1d", end_date="+2d", tzinfo=UTC ) class Meta: diff --git a/docker-compose.yml b/docker-compose.yml index a3a556c7c..a2081a2aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,7 +23,7 @@ x-extra-hosts: &default-extra-hosts services: db: - image: postgres:15.7 + image: postgres:15.8 environment: POSTGRES_PASSWORD: postgres # pragma: allowlist secret ports: @@ -35,7 +35,7 @@ services: - "6379" nginx: - image: nginx:1.27.0 + image: nginx:1.27.1 ports: - "8053:8053" links: @@ -70,7 +70,7 @@ services: extra_hosts: *default-extra-hosts watch: - image: node:20.16.0 + image: node:20.17.0 working_dir: /src command: > /bin/bash -c './webpack_dev_server.sh --install' diff --git a/ecommerce/api.py b/ecommerce/api.py index ce20da78e..3b6f28e41 100644 --- a/ecommerce/api.py +++ b/ecommerce/api.py @@ -10,8 +10,9 @@ import uuid from base64 import b64encode from collections import defaultdict +from collections.abc import Iterable from datetime import timedelta -from typing import Iterable, NamedTuple, Optional # noqa: UP035 +from typing import NamedTuple from urllib.parse import quote_plus, urljoin from django.conf import settings @@ -156,6 +157,23 @@ def calculate_tax( return (0, "", item_price) +def display_taxes(request): + """ + Returns a boolean to manage the taxes display. + + Args: + request(HttpRequest): Request object + + Returns: + Boolean: True if flag and taxes are enabled for the specific country. + """ + visitor_country = determine_visitor_country(request) + return ( + settings.FEATURES.get("ENABLE_TAXES_DISPLAY", False) + and TaxRate.objects.filter(active=True, country_code=visitor_country).exists() + ) + + def generate_cybersource_sa_signature(payload): """ Generate an HMAC SHA256 signature for the CyberSource Secure Acceptance payload @@ -836,11 +854,9 @@ def enroll_user_in_order_items(order): ): voucher_target = voucher.product.content_object voucher_enrollment = first_or_none( - ( # noqa: UP034 - enrollment - for enrollment in successful_run_enrollments - if enrollment.run == voucher_target - ) + enrollment + for enrollment in successful_run_enrollments + if enrollment.run == voucher_target ) if voucher_enrollment is not None: voucher.enrollment = voucher_enrollment @@ -999,9 +1015,9 @@ class ValidatedBasket(NamedTuple): basket: Basket basket_item: BasketItem product_version: ProductVersion - coupon_version: Optional[CouponVersion] # noqa: FA100 - run_selection_ids: Optional[Iterable[int]] # noqa: FA100 - data_consent_users: Optional[Iterable[DataConsentUser]] # noqa: FA100 + coupon_version: CouponVersion | None + run_selection_ids: Iterable[int] | None + data_consent_users: Iterable[DataConsentUser] | None def _validate_basket_contents(basket): diff --git a/ecommerce/api_test.py b/ecommerce/api_test.py index 38d268cfe..f0dcfe992 100644 --- a/ecommerce/api_test.py +++ b/ecommerce/api_test.py @@ -39,6 +39,7 @@ complete_order, create_coupons, create_unfulfilled_order, + display_taxes, enroll_user_in_order_items, fetch_and_serialize_unused_coupons, generate_cybersource_sa_payload, @@ -103,6 +104,7 @@ from maxmind.factories import GeonameFactory, NetBlockIPv4Factory from mitxpro.test_utils import update_namespace from mitxpro.utils import now_in_utc +from users.factories import UserFactory from voucher.factories import VoucherFactory from voucher.models import Voucher @@ -708,9 +710,9 @@ def test_get_by_reference_number( same_order = Order.objects.get_by_reference_number(order.reference_number) assert same_order.id == order.id if hubspot_api_key: - assert mock_hubspot_syncs.order.called_with(order.id) # noqa: PGH005 + mock_hubspot_syncs.order.assert_called_with(order.id) else: - assert mock_hubspot_syncs.order.not_called() # noqa: PGH005 + mock_hubspot_syncs.order.assert_not_called() def test_get_by_reference_number_missing(validated_basket): @@ -774,9 +776,9 @@ def test_create_unfulfilled_order( # noqa: PLR0913 assert CouponRedemption.objects.count() == 0 if hubspot_api_key: - assert mock_hubspot_syncs.order.called_with(order.id) # noqa: PGH005 + mock_hubspot_syncs.order.assert_called_with(order.id) else: - assert mock_hubspot_syncs.order.not_called() # noqa: PGH005 + mock_hubspot_syncs.order.assert_not_called() @pytest.mark.parametrize("has_program_run", [True, False]) @@ -1110,11 +1112,11 @@ def test_delete_expired_basket(patch_now): Test to verify that the expired baskets are deleted on calling clear_and_delete_baskets fn without user argument """ patch_now.return_value = datetime.datetime.now( - tz=datetime.timezone.utc + tz=datetime.UTC ) - datetime.timedelta(days=settings.BASKET_EXPIRY_DAYS) BasketFactory.create_batch(3) patch_now.return_value = datetime.datetime.now( - tz=datetime.timezone.utc + tz=datetime.UTC ) + datetime.timedelta(days=settings.BASKET_EXPIRY_DAYS + 1) unexpired_baskets = BasketFactory.create_batch(3) patch_now.stop() @@ -1864,3 +1866,54 @@ def test_tax_country_and_no_ip_tax(user): location_tax_rate.country_code, 1000 + (1000 * Decimal(location_tax_rate.tax_rate / 100)), ) + + +@pytest.mark.parametrize( + ( + "is_taxes_display_flag_enabled", + "is_force_profile_country_flag_enabled", + "user_profile_country", + "user_determined_country", + "tax_rate_country", + "tax_rate_created", + "tax_rate_enabled", + "expected_taxes_display", + ), + [ + (True, True, "US", "US", "US", True, True, True), + (True, True, "US", "US", "PK", True, True, False), + (True, False, "US", "US", "US", True, True, True), + (True, False, "PK", "US", "US", True, True, True), + (True, False, "PK", "US", "PK", True, True, False), + (True, False, "US", "US", "US", True, False, False), + (True, False, "PK", "US", "US", True, False, False), + (True, False, "US", "US", "US", False, False, False), + ], +) +def test_display_taxes( # noqa: PLR0913 + is_taxes_display_flag_enabled, + is_force_profile_country_flag_enabled, + user_profile_country, + user_determined_country, + tax_rate_country, + tax_rate_created, + tax_rate_enabled, + expected_taxes_display, + mocker, +): + """ + Tests that `display_taxes` returns the expected display status. + """ + mocker.patch( + "ecommerce.api.determine_visitor_country", return_value=user_determined_country + ) + settings.FEATURES["ENABLE_TAXES_DISPLAY"] = is_taxes_display_flag_enabled + settings.ECOMMERCE_FORCE_PROFILE_COUNTRY = is_force_profile_country_flag_enabled + user = UserFactory.create(legal_address__country=user_profile_country) + request = FakeRequest() + request.user = user + + if tax_rate_created: + TaxRateFactory.create(country_code=tax_rate_country, active=tax_rate_enabled) + + assert display_taxes(request) == expected_taxes_display diff --git a/ecommerce/factories.py b/ecommerce/factories.py index ffa8ed97a..7c198cc71 100644 --- a/ecommerce/factories.py +++ b/ecommerce/factories.py @@ -2,7 +2,7 @@ Factories for ecommerce models """ -from datetime import timezone +from datetime import UTC import faker from factory import Faker, LazyAttribute, SubFactory, Trait, fuzzy, post_generation @@ -128,10 +128,10 @@ class CouponPaymentVersionFactory(DjangoModelFactory): amount = fuzzy.FuzzyDecimal(0, 1, precision=5) company = SubFactory(CompanyFactory) activation_date = Faker( - "date_time_this_year", before_now=True, after_now=False, tzinfo=timezone.utc + "date_time_this_year", before_now=True, after_now=False, tzinfo=UTC ) expiration_date = Faker( - "date_time_this_year", before_now=False, after_now=True, tzinfo=timezone.utc + "date_time_this_year", before_now=False, after_now=True, tzinfo=UTC ) class Meta: diff --git a/ecommerce/mail_api.py b/ecommerce/mail_api.py index ffe4ecbc6..16f6f833d 100644 --- a/ecommerce/mail_api.py +++ b/ecommerce/mail_api.py @@ -356,9 +356,7 @@ def send_ecommerce_order_receipt(order, cyber_source_provided_email=None): "company": purchaser.get("company"), "vat_id": purchaser.get("vat_id"), }, - "enable_taxes_display": settings.FEATURES.get( - "ENABLE_TAXES_DISPLAY", False - ), + "enable_taxes_display": bool(order["tax_rate"]), }, ), ) diff --git a/ecommerce/mail_api_test.py b/ecommerce/mail_api_test.py index 5275dffa7..0233f793b 100644 --- a/ecommerce/mail_api_test.py +++ b/ecommerce/mail_api_test.py @@ -159,9 +159,7 @@ def test_send_course_run_enrollment_welcome_email(settings, mocker, enabled): enrollment = CourseRunEnrollmentFactory.create() run_start_date = enrollment.run.start_date - run_start_time = run_start_date.astimezone(datetime.timezone.utc).strftime( - EMAIL_TIME_FORMAT - ) + run_start_time = run_start_date.astimezone(datetime.UTC).strftime(EMAIL_TIME_FORMAT) run_end_date = enrollment.run.end_date date_range = ( f"{run_start_date.strftime(EMAIL_DATE_FORMAT)} - " @@ -265,7 +263,7 @@ def test_send_ecommerce_order_receipt(mocker, receipt_data, settings): """send_ecommerce_order_receipt should send a receipt email""" settings.FEATURES["ENABLE_TAXES_DISPLAY"] = False patched_mail_api = mocker.patch("ecommerce.mail_api.api") - date = datetime.datetime(2010, 1, 1, 0, tzinfo=datetime.timezone.utc) + date = datetime.datetime(2010, 1, 1, 0, tzinfo=datetime.UTC) user = UserFactory.create( name="test", email="test@example.com", diff --git a/ecommerce/management/commands/invalidate_payment_coupons.py b/ecommerce/management/commands/invalidate_payment_coupons.py index 9c1eed846..e6bd64cf1 100644 --- a/ecommerce/management/commands/invalidate_payment_coupons.py +++ b/ecommerce/management/commands/invalidate_payment_coupons.py @@ -61,7 +61,8 @@ def handle(self, *args, **kwargs): # noqa: ARG002 codes = Coupon.objects.filter(enabled=True, payment=payment).all() else: try: - with open(kwargs["codefile"], "r") as file: # noqa: PTH123, UP015 + # Note: open() defaults to read mode ("r") + with open(kwargs["codefile"]) as file: # noqa: PTH123 procCodes = [line.strip() for line in file] except Exception as e: # noqa: BLE001 raise CommandError( # noqa: B904 diff --git a/ecommerce/models_test.py b/ecommerce/models_test.py index 5e0c7021d..b4329c5b5 100644 --- a/ecommerce/models_test.py +++ b/ecommerce/models_test.py @@ -253,11 +253,12 @@ def test_product_version_save_text_id_badproduct(mocker): """ProductVersion.text_id should None if ProductVersion.product is invalid""" mock_log = mocker.patch("ecommerce.models.log") product_version = ProductVersionFactory.create( - product=ProductFactory.create(content_object=LineFactory()) + product=ProductFactory.create(content_object=LineFactory()), id=1 ) assert product_version.text_id is None - assert mock_log.called_once_with( # noqa: PGH005 - f"The content object for this ProductVersion ({product_version.id}) does not have a `text_id` property" + mock_log.error.assert_called_once_with( + "The content object for this ProductVersion (%s) does not have a `text_id` property", + str(product_version.id), ) diff --git a/ecommerce/utils.py b/ecommerce/utils.py index f3f3e1aef..ac0ab467a 100644 --- a/ecommerce/utils.py +++ b/ecommerce/utils.py @@ -171,7 +171,7 @@ def format_run_date(run_date): if run_date: from ecommerce.mail_api import EMAIL_DATE_FORMAT - formatted_date_time = run_date.astimezone(datetime.timezone.utc).strftime( + formatted_date_time = run_date.astimezone(datetime.UTC).strftime( f"{EMAIL_DATE_FORMAT}-{EMAIL_TIME_FORMAT}" ) return tuple(formatted_date_time.split("-", 1)) diff --git a/ecommerce/views_test.py b/ecommerce/views_test.py index fd086a806..6d5c98f34 100644 --- a/ecommerce/views_test.py +++ b/ecommerce/views_test.py @@ -1,7 +1,7 @@ """ecommerce tests for views""" import json -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from urllib.parse import quote_plus, urljoin import factory @@ -225,9 +225,9 @@ def test_zero_price_checkout( # noqa: PLR0913 assert CourseRunSelection.objects.filter(basket__user=user).count() == 0 assert CouponSelection.objects.filter(basket__user=user).count() == 0 if hubspot_api_key: - assert mock_hubspot_syncs.order.called_with(order.id) # noqa: PGH005 + mock_hubspot_syncs.order.assert_called_with(order.id) else: - assert mock_hubspot_syncs.order.not_called() # noqa: PGH005 + mock_hubspot_syncs.order.assert_not_called() @pytest.mark.parametrize("hubspot_api_key", [None, "fake-key"]) @@ -287,9 +287,9 @@ def test_order_fulfilled( # noqa: PLR0913 assert CouponSelection.objects.filter(basket__user=user).count() == 0 if hubspot_api_key: - assert mock_hubspot_syncs.order.called_with(order.id) # noqa: PGH005 + mock_hubspot_syncs.order.assert_called_with(order.id) else: - assert mock_hubspot_syncs.order.not_called() # noqa: PGH005 + mock_hubspot_syncs.order.assert_not_called() def test_order_affiliate(basket_client, mocker, basket_and_coupons): @@ -975,7 +975,7 @@ def test_patch_basket_data_consents(basket_and_agreement, as_owner): ) if as_owner: assert resp.json()["data_consents"][0]["consent_date"] >= datetime.now( - tz=timezone.utc + tz=UTC ).strftime("%Y-%m-%dT00:00:00Z") else: assert resp.json()["data_consents"] == [] diff --git a/hubspot_xpro/api.py b/hubspot_xpro/api.py index 8dee2712f..ac8344ee9 100644 --- a/hubspot_xpro/api.py +++ b/hubspot_xpro/api.py @@ -388,14 +388,14 @@ def get_hubspot_id_for_object( return hubspot_obj.hubspot_id if isinstance(obj, User): hubspot_obj = find_contact(obj.email) - elif isinstance(obj, (B2BOrder, Order)): + elif isinstance(obj, (B2BOrder, Order)): # noqa: UP038 serialized_deal = get_hubspot_serializer(obj).data hubspot_obj = find_deal( name=serialized_deal["dealname"], amount=serialized_deal["amount"], raise_count_error=raise_error, ) - elif isinstance(obj, (Line, B2BLine)): + elif isinstance(obj, (Line, B2BLine)): # noqa: UP038 serialized_line = get_hubspot_serializer(obj).data order_id = get_hubspot_id_for_object(obj.order) if order_id: diff --git a/hubspot_xpro/conftest.py b/hubspot_xpro/conftest.py index 93d0fe047..1035f0975 100644 --- a/hubspot_xpro/conftest.py +++ b/hubspot_xpro/conftest.py @@ -2,7 +2,7 @@ Fixtures for hubspot_xpro tests """ -from datetime import datetime, timezone +from datetime import UTC, datetime from types import SimpleNamespace import pytest @@ -16,14 +16,14 @@ from users.models import User TIMESTAMPS = [ - datetime(2017, 1, 1, tzinfo=timezone.utc), - datetime(2017, 1, 2, tzinfo=timezone.utc), - datetime(2017, 1, 3, tzinfo=timezone.utc), - datetime(2017, 1, 4, tzinfo=timezone.utc), - datetime(2017, 1, 5, tzinfo=timezone.utc), - datetime(2017, 1, 6, tzinfo=timezone.utc), - datetime(2017, 1, 7, tzinfo=timezone.utc), - datetime(2017, 1, 8, tzinfo=timezone.utc), + datetime(2017, 1, 1, tzinfo=UTC), + datetime(2017, 1, 2, tzinfo=UTC), + datetime(2017, 1, 3, tzinfo=UTC), + datetime(2017, 1, 4, tzinfo=UTC), + datetime(2017, 1, 5, tzinfo=UTC), + datetime(2017, 1, 6, tzinfo=UTC), + datetime(2017, 1, 7, tzinfo=UTC), + datetime(2017, 1, 8, tzinfo=UTC), ] FAKE_OBJECT_ID = 1234 diff --git a/hubspot_xpro/tasks.py b/hubspot_xpro/tasks.py index 562113196..821d62a26 100644 --- a/hubspot_xpro/tasks.py +++ b/hubspot_xpro/tasks.py @@ -97,7 +97,7 @@ def sync_failed_contacts(chunk: list[int]) -> list[int]: try: api.sync_contact_with_hubspot(user_id) time.sleep(settings.HUBSPOT_TASK_DELAY / 1000) - except ApiException: # noqa: PERF203 + except ApiException: failed_ids.append(user_id) return failed_ids diff --git a/localdev/seed/api.py b/localdev/seed/api.py index 50276c210..04609e589 100644 --- a/localdev/seed/api.py +++ b/localdev/seed/api.py @@ -186,7 +186,7 @@ def parse_datetime_from_string(dt_string): datetime.datetime: The parsed datetime """ return datetime.datetime.strptime(dt_string, "%Y-%m-%dT%H:%M:%S").astimezone( - datetime.timezone.utc + datetime.UTC ) @@ -203,12 +203,10 @@ def check_settings(): # check in settings if not getattr(settings, variable): missing.append(variable) - except AttributeError: # noqa: PERF203 + except AttributeError: missing.append(variable) if missing: - raise ImproperlyConfigured( - "Missing required voucher settings: {}".format(missing) # noqa: EM103, UP032 - ) + raise ImproperlyConfigured(f"Missing required voucher settings: {missing}") # noqa: EM102 SeedDataSpec = namedtuple("SeedDataSpec", ["model_cls", "data", "parent"]) # noqa: PYI024 diff --git a/localdev/seed/management/commands/delete_seed_data.py b/localdev/seed/management/commands/delete_seed_data.py index fcd79199a..f44b0c464 100644 --- a/localdev/seed/management/commands/delete_seed_data.py +++ b/localdev/seed/management/commands/delete_seed_data.py @@ -79,4 +79,4 @@ def handle(self, *args, **options): # noqa: ARG002 else: self.stdout.write(self.style.SUCCESS("RESULTS")) for k, v in results.report.items(): - self.stdout.write("{}: {}".format(k, v)) # noqa: UP032 + self.stdout.write(f"{k}: {v}") diff --git a/localdev/seed/management/commands/seed_data.py b/localdev/seed/management/commands/seed_data.py index 07a0ea7eb..0e86e6848 100644 --- a/localdev/seed/management/commands/seed_data.py +++ b/localdev/seed/management/commands/seed_data.py @@ -24,4 +24,4 @@ def handle(self, *args, **options): # noqa: ARG002 else: self.stdout.write(self.style.SUCCESS("RESULTS")) for k, v in results.report.items(): - self.stdout.write("{}: {}".format(k, v)) # noqa: UP032 + self.stdout.write(f"{k}: {v}") diff --git a/mail/api.py b/mail/api.py index c3caf5a6b..fd8695e0a 100644 --- a/mail/api.py +++ b/mail/api.py @@ -286,7 +286,7 @@ def send_messages(messages): for msg in messages: try: msg.send() - except: # noqa: E722, PERF203 + except: # noqa: E722 log.exception("Error sending email '%s' to %s", msg.subject, msg.to) @@ -315,7 +315,7 @@ def validate_email_addresses(email_addresses): for email in email_addresses: try: validate_email(email) - except ValidationError: # noqa: PERF203 + except ValidationError: invalid_emails.add(email) if invalid_emails: raise MultiEmailValidationError(invalid_emails) diff --git a/mail/templates/product_order_receipt/body.html b/mail/templates/product_order_receipt/body.html index bba7b4787..1133d48c7 100755 --- a/mail/templates/product_order_receipt/body.html +++ b/mail/templates/product_order_receipt/body.html @@ -22,7 +22,7 @@ Cambridge, MA 02139 USA
{% if enable_taxes_display %} - GSTIN: Pending + GSTIN: 9923USA29055OSB
{% endif %} Support: diff --git a/mitxpro/exceptions.py b/mitxpro/exceptions.py index 542465d04..61eba141a 100644 --- a/mitxpro/exceptions.py +++ b/mitxpro/exceptions.py @@ -5,7 +5,7 @@ def exception_handler(exc, context): """Override DRF exception_handler to slightly change format of error response""" - if isinstance(exc, exceptions.ValidationError) and isinstance( + if isinstance(exc, exceptions.ValidationError) and isinstance( # noqa: UP038 exc.detail, (list, dict) ): exc = exceptions.ValidationError( diff --git a/mitxpro/models.py b/mitxpro/models.py index 675d00a9e..e9176a96a 100644 --- a/mitxpro/models.py +++ b/mitxpro/models.py @@ -3,7 +3,7 @@ """ import copy -from typing import Iterable # noqa: UP035 +from collections.abc import Iterable from django.conf import settings from django.core.exceptions import ValidationError diff --git a/mitxpro/settings.py b/mitxpro/settings.py index ca6f1a38f..e8ca4e574 100644 --- a/mitxpro/settings.py +++ b/mitxpro/settings.py @@ -27,7 +27,7 @@ from mitxpro.celery_utils import OffsettingSchedule from mitxpro.sentry import init_sentry -VERSION = "0.158.0" +VERSION = "0.159.0" ENVIRONMENT = get_string( name="MITXPRO_ENVIRONMENT", @@ -272,7 +272,7 @@ DEFAULT_DATABASE_CONFIG = dj_database_url.parse( get_string( name="DATABASE_URL", - default="sqlite:///{0}".format(os.path.join(BASE_DIR, "db.sqlite3")), # noqa: PTH118, UP030 + default="sqlite:///{}".format(os.path.join(BASE_DIR, "db.sqlite3")), # noqa: PTH118 description="The connection url to the Postgres database", required=True, write_app_json=False, @@ -426,7 +426,7 @@ ) if CLOUDFRONT_DIST: STATIC_URL = urljoin( - "https://{dist}.cloudfront.net".format(dist=CLOUDFRONT_DIST), # noqa: UP032 + f"https://{CLOUDFRONT_DIST}.cloudfront.net", STATIC_URL, ) @@ -682,7 +682,7 @@ ) if MITXPRO_USE_S3: if CLOUDFRONT_DIST: - AWS_S3_CUSTOM_DOMAIN = "{dist}.cloudfront.net".format(dist=CLOUDFRONT_DIST) # noqa: UP032 + AWS_S3_CUSTOM_DOMAIN = f"{CLOUDFRONT_DIST}.cloudfront.net" DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" FEATURES = get_features() diff --git a/mitxpro/test_utils.py b/mitxpro/test_utils.py index ade1056d9..1ad41f752 100644 --- a/mitxpro/test_utils.py +++ b/mitxpro/test_utils.py @@ -76,7 +76,7 @@ def __init__( url=None, reason="", ): - if isinstance(content, (dict, list)): + if isinstance(content, (dict, list)): # noqa: UP038 self.content = json.dumps(content) else: self.content = str(content) @@ -170,7 +170,8 @@ def create_tempfile_csv(rows_iter): writer = csv.writer(f, delimiter=",") for row in rows_iter: writer.writerow(row) - with open(f.name, "r") as user_csv: # noqa: PTH123, UP015 + # Note: open() defaults to read mode ("r") + with open(f.name) as user_csv: # noqa: PTH123 return SimpleUploadedFile( f.name, user_csv.read().encode("utf8"), content_type="application/csv" ) diff --git a/mitxpro/utils.py b/mitxpro/utils.py index c931f6e3d..6214e74b8 100644 --- a/mitxpro/utils.py +++ b/mitxpro/utils.py @@ -72,7 +72,7 @@ def is_near_now(time): bool: True if near now, false otherwise """ - now = datetime.datetime.now(tz=datetime.timezone.utc) + now = datetime.datetime.now(tz=datetime.UTC) five_seconds = datetime.timedelta(0, 5) return now - five_seconds < time < now + five_seconds @@ -83,7 +83,7 @@ def now_in_utc(): Returns: datetime.datetime: A datetime object for the current time """ - return datetime.datetime.now(tz=datetime.timezone.utc) + return datetime.datetime.now(tz=datetime.UTC) def format_datetime_for_filename(datetime_object, include_time=False, include_ms=False): # noqa: FBT002 @@ -212,7 +212,7 @@ def find_object_with_matching_attr(iterable, attr_name, value): try: if getattr(item, attr_name) == value: return item - except AttributeError: # noqa: PERF203 + except AttributeError: pass return None @@ -226,7 +226,7 @@ def has_equal_properties(obj, property_dict): try: if getattr(obj, field) != value: return False - except AttributeError: # noqa: PERF203 + except AttributeError: return False return True @@ -581,6 +581,8 @@ def get_js_settings(request: HttpRequest): Returns: dict: the settings object """ + from ecommerce.api import display_taxes + return { "gtmTrackingID": settings.GTM_TRACKING_ID, "gaTrackingID": settings.GA_TRACKING_ID, @@ -600,7 +602,7 @@ def get_js_settings(request: HttpRequest): "course_dropdown": settings.FEATURES.get("COURSE_DROPDOWN", False), "webinars": settings.FEATURES.get("WEBINARS", False), "enable_blog": settings.FEATURES.get("ENABLE_BLOG", False), - "enable_taxes_display": settings.FEATURES.get("ENABLE_TAXES_DISPLAY", False), + "enable_taxes_display": display_taxes(request), "enable_enterprise": settings.FEATURES.get("ENABLE_ENTERPRISE", False), } @@ -639,5 +641,5 @@ def strip_datetime(date_str, date_format, date_timezone=None): if not date_str or not date_format: return None - date_timezone = date_timezone if date_timezone else datetime.timezone.utc + date_timezone = date_timezone if date_timezone else datetime.UTC return datetime.datetime.strptime(date_str, date_format).astimezone(date_timezone) diff --git a/mitxpro/utils_test.py b/mitxpro/utils_test.py index 3300f0dd0..728c4b55f 100644 --- a/mitxpro/utils_test.py +++ b/mitxpro/utils_test.py @@ -88,14 +88,14 @@ def test_now_in_utc(): """now_in_utc() should return the current time set to the UTC time zone""" now = now_in_utc() assert is_near_now(now) - assert now.tzinfo == datetime.timezone.utc + assert now.tzinfo == datetime.UTC def test_is_near_now(): """ Test is_near_now for now """ - now = datetime.datetime.now(tz=datetime.timezone.utc) + now = datetime.datetime.now(tz=datetime.UTC) assert is_near_now(now) is True later = now + datetime.timedelta(0, 6) assert is_near_now(later) is False @@ -455,7 +455,7 @@ def test_request_get_with_timeout_retry(mocker): assert result == mock_response -def test_get_js_settings(settings, rf): +def test_get_js_settings(settings, rf, user): """Test get_js_settings""" settings.GA_TRACKING_ID = "fake" settings.GTM_TRACKING_ID = "fake" @@ -477,6 +477,7 @@ def test_get_js_settings(settings, rf): settings.FEATURES["ENABLE_ENTERPRISE"] = False request = rf.get("/") + request.user = user assert get_js_settings(request) == { "gaTrackingID": "fake", @@ -531,38 +532,38 @@ def test_clean_url(url, remove_query_params, expected_url): ( "2025-06-26", "%Y-%m-%d", - datetime.timezone.utc, - datetime.datetime(2025, 6, 26, tzinfo=datetime.timezone.utc), + datetime.UTC, + datetime.datetime(2025, 6, 26, tzinfo=datetime.UTC), ), ( "2025-06-26", "%Y-%m-%d", None, - datetime.datetime(2025, 6, 26, tzinfo=datetime.timezone.utc), + datetime.datetime(2025, 6, 26, tzinfo=datetime.UTC), ), ( "21 June, 2018", "%d %B, %Y", - datetime.timezone.utc, - datetime.datetime(2018, 6, 21, tzinfo=datetime.timezone.utc), + datetime.UTC, + datetime.datetime(2018, 6, 21, tzinfo=datetime.UTC), ), ( "21 June, 2018", "%d %B, %Y", None, - datetime.datetime(2018, 6, 21, tzinfo=datetime.timezone.utc), + datetime.datetime(2018, 6, 21, tzinfo=datetime.UTC), ), ( "12/11/2018 09:15:32", "%d/%m/%Y %H:%M:%S", - datetime.timezone.utc, - datetime.datetime(2018, 11, 12, 9, 15, 32, tzinfo=datetime.timezone.utc), + datetime.UTC, + datetime.datetime(2018, 11, 12, 9, 15, 32, tzinfo=datetime.UTC), ), ( "12/11/2018 09:15:32", "%d/%m/%Y %H:%M:%S", None, - datetime.datetime(2018, 11, 12, 9, 15, 32, tzinfo=datetime.timezone.utc), + datetime.datetime(2018, 11, 12, 9, 15, 32, tzinfo=datetime.UTC), ), ], ) diff --git a/package.json b/package.json index b99ce7968..4f7052335 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "yup": "^0.31.0" }, "engines": { - "node": "20.16.0" + "node": "20.17.0" }, "scripts": { "postinstall": "./webpack_if_prod.sh", @@ -140,6 +140,7 @@ "fmt:check": "LOG_LEVEL= prettier --config .prettierrc.json --check ." }, "resolutions": { + "cheerio": "1.0.0-rc.12", "mixin-deep": "^1.3.2", "set-value": "^2.0.1", "js-yaml": "^3.13.1", diff --git a/poetry.lock b/poetry.lock index 5bc803ee4..c04d756a6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -47,17 +47,6 @@ files = [ {file = "anyascii-0.3.2.tar.gz", hash = "sha256:9d5d32ef844fe225b8bc7cba7f950534fae4da27a9bf3a6bea2cb0ea46ce4730"}, ] -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = "*" -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - [[package]] name = "asgiref" version = "3.8.1" @@ -69,9 +58,6 @@ files = [ {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, ] -[package.dependencies] -typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} - [package.extras] tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] @@ -93,17 +79,6 @@ six = ">=1.12.0" astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - [[package]] name = "attrs" version = "24.2.0" @@ -125,13 +100,13 @@ tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "authlib" -version = "1.3.1" +version = "1.3.2" description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." optional = false python-versions = ">=3.8" files = [ - {file = "Authlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:d35800b973099bbadc49b42b256ecb80041ad56b7fe1216a362c7943c088f377"}, - {file = "authlib-1.3.1.tar.gz", hash = "sha256:7ae843f03c06c5c0debd63c9db91f9fda64fa62a42a77419fa15fbb7e7a58917"}, + {file = "Authlib-1.3.2-py2.py3-none-any.whl", hash = "sha256:ede026a95e9f5cdc2d4364a52103f5405e75aa156357e831ef2bfd0bc5094dfc"}, + {file = "authlib-1.3.2.tar.gz", hash = "sha256:4b16130117f9eb82aa6eec97f6dd4673c3f960ac0283ccdae2897ee4bc030ba2"}, ] [package.dependencies] @@ -150,7 +125,6 @@ files = [ [package.dependencies] pycodestyle = ">=2.12.0" -tomli = {version = "*", markers = "python_version < \"3.11\""} [[package]] name = "beautifulsoup4" @@ -211,17 +185,17 @@ files = [ [[package]] name = "boto3" -version = "1.34.149" +version = "1.35.8" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.149-py3-none-any.whl", hash = "sha256:11edeeacdd517bda3b7615b754d8440820cdc9ddd66794cc995a9693ddeaa3be"}, - {file = "boto3-1.34.149.tar.gz", hash = "sha256:f4e6489ba9dc7fb37d53e0e82dbc97f2cb0a4969ef3970e2c88b8f94023ae81a"}, + {file = "boto3-1.35.8-py3-none-any.whl", hash = "sha256:06eac4757de2a9c6020381205cb902f05964caad80b56e58c8931284a133b4cb"}, + {file = "boto3-1.35.8.tar.gz", hash = "sha256:b9587131372a808bf6f99c5ed8b11be55cd113261cc3b437a917b4acc6c30bfe"}, ] [package.dependencies] -botocore = ">=1.34.149,<1.35.0" +botocore = ">=1.35.8,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -230,19 +204,19 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.156" +version = "1.35.8" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.156-py3-none-any.whl", hash = "sha256:c48f8c8996216dfdeeb0aa6d3c0f2c7ae25234766434a2ea3e57bdc08494bdda"}, - {file = "botocore-1.34.156.tar.gz", hash = "sha256:5d1478c41ab9681e660b3322432fe09c4055759c317984b7b8d3af9557ff769a"}, + {file = "botocore-1.35.8-py3-none-any.whl", hash = "sha256:adf389eb8fd87775f193300e3431d1353f925807ad3a39958172cb644f0d60a1"}, + {file = "botocore-1.35.8.tar.gz", hash = "sha256:4b820cf680ab5d778bd2fe4feeef1ff8a2b96d5c535d4638ab30f703ade282f8"}, ] [package.dependencies] jmespath = ">=0.7.1,<2.0.0" python-dateutil = ">=2.1,<3.0.0" -urllib3 = {version = ">=1.25.4,<1.27", markers = "python_version < \"3.10\""} +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} [package.extras] crt = ["awscrt (==0.21.2)"] @@ -272,26 +246,15 @@ jedi = ["jedi (>=0.16)"] urwid = ["urwid"] watch = ["watchdog"] -[[package]] -name = "cached-property" -version = "1.5.2" -description = "A decorator for caching properties in classes." -optional = false -python-versions = "*" -files = [ - {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, - {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, -] - [[package]] name = "cachetools" -version = "4.2.4" +version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false -python-versions = "~=3.5" +python-versions = ">=3.7" files = [ - {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"}, - {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] @@ -710,9 +673,6 @@ files = [ {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] @@ -958,19 +918,21 @@ Django = ">=3.2" [[package]] name = "django-hijack" -version = "3.4.5" -description = "django-hijack allows superusers to hijack (=login as) and work on behalf of another user." +version = "3.6.0" +description = "Enable users to hijack (=login as) and work on behalf of another user." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "django-hijack-3.4.5.tar.gz", hash = "sha256:7e45b1de786bdc130628e4230b359dde6d8744ecd3bcd668d2b27c5d614a071c"}, - {file = "django_hijack-3.4.5-py3-none-any.whl", hash = "sha256:129cbe75444b163135871a947d38ffb72181f4f2583544703fc9efe083c9ddad"}, + {file = "django_hijack-3.6.0-py3-none-any.whl", hash = "sha256:698bbe2bcc95c240e4706d465c3ea6c78c66cdcef357802d171ba7036079b075"}, + {file = "django_hijack-3.6.0.tar.gz", hash = "sha256:4554f7ceb1a5b39aecacd06fee08933a90a357a9c7eeb018d9483d25938dc637"}, ] [package.dependencies] -django = ">=3.2" +django = ">=4.2" [package.extras] +docs = ["mkdocs (==1.6.0)"] +lint = ["msgcheck (==4.0.0)", "ruff (==0.5.0)"] test = ["pytest", "pytest-cov", "pytest-django"] [[package]] @@ -1053,24 +1015,24 @@ redis = ">=3.0.0" [[package]] name = "django-robots" -version = "6.0" +version = "6.1" description = "Robots exclusion application for Django, complementing Sitemaps." optional = false python-versions = ">=3.7" files = [ - {file = "django-robots-6.0.tar.gz", hash = "sha256:a720e7736d62e2e403339e72fecc733d4e7662372583dd51a7d9c44d4522d1c4"}, - {file = "django_robots-6.0-py3-none-any.whl", hash = "sha256:108fbfcf5a43ecbcdc35cfd1ce77cbdf19f38a1ecff5c7315d31626b770966f1"}, + {file = "django-robots-6.1.tar.gz", hash = "sha256:f86bcc3d16d7d7c2a4e37af6063cb4785f50ae16943f82248b48c9e7ac034f1d"}, + {file = "django_robots-6.1-py3-none-any.whl", hash = "sha256:07e11a1bf3ddc08290123ec3c55abb45dbbffb9b38aea0a002e9b4a87bd9abcc"}, ] [[package]] name = "django-silk" -version = "5.1.0" +version = "5.2.0" description = "Silky smooth profiling for the Django Framework" optional = false python-versions = ">=3.8" files = [ - {file = "django-silk-5.1.0.tar.gz", hash = "sha256:34abb5852315f0f3303d45b7ab4a2caa9cf670102b614dbb2ac40a5d2d5cbffb"}, - {file = "django_silk-5.1.0-py3-none-any.whl", hash = "sha256:35a2051672b0be86af4ce734a0df0b6674c8c63f2df730b3756ec6e52923707d"}, + {file = "django_silk-5.2.0-py3-none-any.whl", hash = "sha256:b3f01ccbf46611073603a6ac2b84f578bde978ad44785f42994c3d6f81398fdc"}, + {file = "django_silk-5.2.0.tar.gz", hash = "sha256:39ddeda80469d5495d1cbb53590a9bdd4ce30a7474dc16ac26b6cbc0d9521f50"}, ] [package.dependencies] @@ -1239,7 +1201,6 @@ files = [ [package.dependencies] packaging = "*" -tomli = {version = "*", markers = "python_version < \"3.11\""} [package.extras] all = ["dparse[conda]", "dparse[pipenv]", "dparse[poetry]"] @@ -1291,18 +1252,6 @@ requests = ">=2.20.0" [package.extras] dev = ["flake8"] -[[package]] -name = "enum34" -version = "1.1.10" -description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" -optional = false -python-versions = "*" -files = [ - {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, - {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, - {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, -] - [[package]] name = "et-xmlfile" version = "1.1.0" @@ -1314,20 +1263,6 @@ files = [ {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, ] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "executing" version = "2.0.1" @@ -1444,66 +1379,66 @@ six = "*" [[package]] name = "google-api-core" -version = "2.10.2" +version = "2.19.2" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.10.2.tar.gz", hash = "sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320"}, - {file = "google_api_core-2.10.2-py3-none-any.whl", hash = "sha256:34f24bd1d5f72a8c4519773d99ca6bf080a6c4e041b4e9f024fe230191dda62e"}, + {file = "google_api_core-2.19.2-py3-none-any.whl", hash = "sha256:53ec0258f2837dd53bbd3d3df50f5359281b3cc13f800c941dd15a9b5a415af4"}, + {file = "google_api_core-2.19.2.tar.gz", hash = "sha256:ca07de7e8aa1c98a8bfca9321890ad2340ef7f2eb136e558cee68f24b94b0a8f"}, ] [package.dependencies] -google-auth = ">=1.25.0,<3.0dev" -googleapis-common-protos = ">=1.56.2,<2.0dev" -protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" -requests = ">=2.18.0,<3.0.0dev" +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +proto-plus = ">=1.22.3,<2.0.0dev" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" [package.extras] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] -grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0dev)"] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "1.12.11" +version = "2.50.0" description = "Google API Client Library for Python" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.6" files = [ - {file = "google-api-python-client-1.12.11.tar.gz", hash = "sha256:1b4bd42a46321e13c0542a9e4d96fa05d73626f07b39f83a73a947d70ca706a9"}, - {file = "google_api_python_client-1.12.11-py2.py3-none-any.whl", hash = "sha256:7e0a1a265c8d3088ee1987778c72683fcb376e32bada8d7767162bd9c503fd9b"}, + {file = "google-api-python-client-2.50.0.tar.gz", hash = "sha256:159aa2d5f67998f39b06f28f38d6621389dda099c56f0fde46e9070dabdd5b40"}, + {file = "google_api_python_client-2.50.0-py2.py3-none-any.whl", hash = "sha256:a45fd3f318f79b3498d31de7e7db16d70b01672a755c88f56841183db908c576"}, ] [package.dependencies] -google-api-core = {version = ">=1.21.0,<3dev", markers = "python_version >= \"3\""} -google-auth = {version = ">=1.16.0,<3dev", markers = "python_version >= \"3\""} -google-auth-httplib2 = ">=0.0.3" +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0dev" +google-auth = ">=1.16.0,<3.0.0dev" +google-auth-httplib2 = ">=0.1.0" httplib2 = ">=0.15.0,<1dev" -six = ">=1.13.0,<2dev" -uritemplate = ">=3.0.0,<4dev" +uritemplate = ">=3.0.1,<5" [[package]] name = "google-auth" -version = "1.35.0" +version = "2.15.0" description = "Google Authentication Library" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" files = [ - {file = "google-auth-1.35.0.tar.gz", hash = "sha256:b7033be9028c188ee30200b204ea00ed82ea1162e8ac1df4aa6ded19a191d88e"}, - {file = "google_auth-1.35.0-py2.py3-none-any.whl", hash = "sha256:997516b42ecb5b63e8d80f5632c1a61dddf41d2a4c2748057837e06e00014258"}, + {file = "google-auth-2.15.0.tar.gz", hash = "sha256:72f12a6cfc968d754d7bdab369c5c5c16032106e52d32c6dfd8484e4c01a6d1f"}, + {file = "google_auth-2.15.0-py2.py3-none-any.whl", hash = "sha256:6897b93556d8d807ad70701bb89f000183aea366ca7ed94680828b37437a4994"}, ] [package.dependencies] -cachetools = ">=2.0.0,<5.0" +cachetools = ">=2.0.0,<6.0" pyasn1-modules = ">=0.2.1" rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} -setuptools = ">=40.3.0" six = ">=1.9.0" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] -pyopenssl = ["pyopenssl (>=20.0.0)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] [[package]] @@ -1523,17 +1458,17 @@ httplib2 = ">=0.19.0" [[package]] name = "google-auth-oauthlib" -version = "0.5.3" +version = "1.2.1" description = "Google Authentication Library" optional = false python-versions = ">=3.6" files = [ - {file = "google-auth-oauthlib-0.5.3.tar.gz", hash = "sha256:307d21918d61a0741882ad1fd001c67e68ad81206451d05fc4d26f79de56fc90"}, - {file = "google_auth_oauthlib-0.5.3-py2.py3-none-any.whl", hash = "sha256:9e8ff4ed2b21c174a2d6cc2172c698dbf0b1f686509774c663a83c495091fe09"}, + {file = "google_auth_oauthlib-1.2.1-py2.py3-none-any.whl", hash = "sha256:2d58a27262d55aa1b87678c3ba7142a080098cbc2024f903c62355deb235d91f"}, + {file = "google_auth_oauthlib-1.2.1.tar.gz", hash = "sha256:afd0cad092a2eaa53cd8e8298557d6de1034c6cb4a740500b5357b648af97263"}, ] [package.dependencies] -google-auth = ">=1.0.0" +google-auth = ">=2.15.0" requests-oauthlib = ">=0.7.0" [package.extras] @@ -1541,13 +1476,13 @@ tool = ["click (>=6.0.0)"] [[package]] name = "googleapis-common-protos" -version = "1.63.2" +version = "1.65.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, - {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, + {file = "googleapis_common_protos-1.65.0-py2.py3-none-any.whl", hash = "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63"}, + {file = "googleapis_common_protos-1.65.0.tar.gz", hash = "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0"}, ] [package.dependencies] @@ -1695,14 +1630,13 @@ dev = ["black", "pytest"] [[package]] name = "hypothesis" -version = "4.23.4" +version = "4.24.6" description = "A library for property based testing" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ - {file = "hypothesis-4.23.4-py2-none-any.whl", hash = "sha256:c761288bb04b1c2c32fbc06981e22ba5b020bcc272aa39e749c77156301b5b13"}, - {file = "hypothesis-4.23.4-py3-none-any.whl", hash = "sha256:782520620cd42ed4f0a76ea95e05d0c4726387b5e0b0e787b5c16ca86d66f9c5"}, - {file = "hypothesis-4.23.4.tar.gz", hash = "sha256:a9708beea61b45ee11de99aa61e06fe6d559aeccabe5017f9080522449727f18"}, + {file = "hypothesis-4.24.6-py3-none-any.whl", hash = "sha256:223b7eb23f06ed00ad5227e7e5add7fecf6a8f35f36636463a9c7ad422829f3f"}, + {file = "hypothesis-4.24.6.tar.gz", hash = "sha256:15e9d9064e826d6f693a3633a3fae116832159aba0951b8eb65a47082feda4c9"}, ] [package.dependencies] @@ -1721,13 +1655,13 @@ pytz = ["pytz (>=2014.1)"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -1753,46 +1687,44 @@ files = [ ] [package.dependencies] -decorator = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} -ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\" and python_version < \"3.11\""} -tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} +decorator = {version = "*", markers = "python_version >= \"3.11\""} +ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""} [[package]] name = "ipython" -version = "8.18.1" +version = "8.26.0" description = "IPython: Productive Interactive Computing" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, - {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, + {file = "ipython-8.26.0-py3-none-any.whl", hash = "sha256:e6b347c27bdf9c32ee9d31ae85defc525755a1869f14057e900675b9e8d6e6ff"}, + {file = "ipython-8.26.0.tar.gz", hash = "sha256:1cec0fbba8404af13facebe83d04436a7434c7400e59f47acf467c64abd0956c"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} prompt-toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} +traitlets = ">=5.13.0" [package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"] kernel = ["ipykernel"] +matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] +test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] name = "isodate" @@ -1897,7 +1829,6 @@ files = [ [package.dependencies] amqp = ">=5.1.1,<6.0.0" -typing-extensions = {version = "4.12.2", markers = "python_version < \"3.10\""} vine = "5.1.0" [package.extras] @@ -1934,153 +1865,149 @@ six = "*" [[package]] name = "lxml" -version = "5.2.2" +version = "5.3.0" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=3.6" files = [ - {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:364d03207f3e603922d0d3932ef363d55bbf48e3647395765f9bfcbdf6d23632"}, - {file = "lxml-5.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50127c186f191b8917ea2fb8b206fbebe87fd414a6084d15568c27d0a21d60db"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74e4f025ef3db1c6da4460dd27c118d8cd136d0391da4e387a15e48e5c975147"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981a06a3076997adf7c743dcd0d7a0415582661e2517c7d961493572e909aa1d"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aef5474d913d3b05e613906ba4090433c515e13ea49c837aca18bde190853dff"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e275ea572389e41e8b039ac076a46cb87ee6b8542df3fff26f5baab43713bca"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5b65529bb2f21ac7861a0e94fdbf5dc0daab41497d18223b46ee8515e5ad297"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bcc98f911f10278d1daf14b87d65325851a1d29153caaf146877ec37031d5f36"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:b47633251727c8fe279f34025844b3b3a3e40cd1b198356d003aa146258d13a2"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:fbc9d316552f9ef7bba39f4edfad4a734d3d6f93341232a9dddadec4f15d425f"}, - {file = "lxml-5.2.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:13e69be35391ce72712184f69000cda04fc89689429179bc4c0ae5f0b7a8c21b"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b6a30a9ab040b3f545b697cb3adbf3696c05a3a68aad172e3fd7ca73ab3c835"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a233bb68625a85126ac9f1fc66d24337d6e8a0f9207b688eec2e7c880f012ec0"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:dfa7c241073d8f2b8e8dbc7803c434f57dbb83ae2a3d7892dd068d99e96efe2c"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a7aca7964ac4bb07680d5c9d63b9d7028cace3e2d43175cb50bba8c5ad33316"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae4073a60ab98529ab8a72ebf429f2a8cc612619a8c04e08bed27450d52103c0"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ffb2be176fed4457e445fe540617f0252a72a8bc56208fd65a690fdb1f57660b"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e290d79a4107d7d794634ce3e985b9ae4f920380a813717adf61804904dc4393"}, - {file = "lxml-5.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:96e85aa09274955bb6bd483eaf5b12abadade01010478154b0ec70284c1b1526"}, - {file = "lxml-5.2.2-cp310-cp310-win32.whl", hash = "sha256:f956196ef61369f1685d14dad80611488d8dc1ef00be57c0c5a03064005b0f30"}, - {file = "lxml-5.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:875a3f90d7eb5c5d77e529080d95140eacb3c6d13ad5b616ee8095447b1d22e7"}, - {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45f9494613160d0405682f9eee781c7e6d1bf45f819654eb249f8f46a2c22545"}, - {file = "lxml-5.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0b3f2df149efb242cee2ffdeb6674b7f30d23c9a7af26595099afaf46ef4e88"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d28cb356f119a437cc58a13f8135ab8a4c8ece18159eb9194b0d269ec4e28083"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:657a972f46bbefdbba2d4f14413c0d079f9ae243bd68193cb5061b9732fa54c1"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b9ea10063efb77a965a8d5f4182806fbf59ed068b3c3fd6f30d2ac7bee734"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07542787f86112d46d07d4f3c4e7c760282011b354d012dc4141cc12a68cef5f"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:303f540ad2dddd35b92415b74b900c749ec2010e703ab3bfd6660979d01fd4ed"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2eb2227ce1ff998faf0cd7fe85bbf086aa41dfc5af3b1d80867ecfe75fb68df3"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:1d8a701774dfc42a2f0b8ccdfe7dbc140500d1049e0632a611985d943fcf12df"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:56793b7a1a091a7c286b5f4aa1fe4ae5d1446fe742d00cdf2ffb1077865db10d"}, - {file = "lxml-5.2.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eb00b549b13bd6d884c863554566095bf6fa9c3cecb2e7b399c4bc7904cb33b5"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a2569a1f15ae6c8c64108a2cd2b4a858fc1e13d25846be0666fc144715e32ab"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:8cf85a6e40ff1f37fe0f25719aadf443686b1ac7652593dc53c7ef9b8492b115"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:d237ba6664b8e60fd90b8549a149a74fcc675272e0e95539a00522e4ca688b04"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0b3f5016e00ae7630a4b83d0868fca1e3d494c78a75b1c7252606a3a1c5fc2ad"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23441e2b5339bc54dc949e9e675fa35efe858108404ef9aa92f0456929ef6fe8"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2fb0ba3e8566548d6c8e7dd82a8229ff47bd8fb8c2da237607ac8e5a1b8312e5"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:79d1fb9252e7e2cfe4de6e9a6610c7cbb99b9708e2c3e29057f487de5a9eaefa"}, - {file = "lxml-5.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6dcc3d17eac1df7859ae01202e9bb11ffa8c98949dcbeb1069c8b9a75917e01b"}, - {file = "lxml-5.2.2-cp311-cp311-win32.whl", hash = "sha256:4c30a2f83677876465f44c018830f608fa3c6a8a466eb223535035fbc16f3438"}, - {file = "lxml-5.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:49095a38eb333aaf44c06052fd2ec3b8f23e19747ca7ec6f6c954ffea6dbf7be"}, - {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7429e7faa1a60cad26ae4227f4dd0459efde239e494c7312624ce228e04f6391"}, - {file = "lxml-5.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:50ccb5d355961c0f12f6cf24b7187dbabd5433f29e15147a67995474f27d1776"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc911208b18842a3a57266d8e51fc3cfaccee90a5351b92079beed912a7914c2"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33ce9e786753743159799fdf8e92a5da351158c4bfb6f2db0bf31e7892a1feb5"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec87c44f619380878bd49ca109669c9f221d9ae6883a5bcb3616785fa8f94c97"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08ea0f606808354eb8f2dfaac095963cb25d9d28e27edcc375d7b30ab01abbf6"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75a9632f1d4f698b2e6e2e1ada40e71f369b15d69baddb8968dcc8e683839b18"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74da9f97daec6928567b48c90ea2c82a106b2d500f397eeb8941e47d30b1ca85"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:0969e92af09c5687d769731e3f39ed62427cc72176cebb54b7a9d52cc4fa3b73"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:9164361769b6ca7769079f4d426a41df6164879f7f3568be9086e15baca61466"}, - {file = "lxml-5.2.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d26a618ae1766279f2660aca0081b2220aca6bd1aa06b2cf73f07383faf48927"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab67ed772c584b7ef2379797bf14b82df9aa5f7438c5b9a09624dd834c1c1aaf"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3d1e35572a56941b32c239774d7e9ad724074d37f90c7a7d499ab98761bd80cf"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8268cbcd48c5375f46e000adb1390572c98879eb4f77910c6053d25cc3ac2c67"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e282aedd63c639c07c3857097fc0e236f984ceb4089a8b284da1c526491e3f3d"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfdc2bfe69e9adf0df4915949c22a25b39d175d599bf98e7ddf620a13678585"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4aefd911793b5d2d7a921233a54c90329bf3d4a6817dc465f12ffdfe4fc7b8fe"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8b8df03a9e995b6211dafa63b32f9d405881518ff1ddd775db4e7b98fb545e1c"}, - {file = "lxml-5.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f11ae142f3a322d44513de1018b50f474f8f736bc3cd91d969f464b5bfef8836"}, - {file = "lxml-5.2.2-cp312-cp312-win32.whl", hash = "sha256:16a8326e51fcdffc886294c1e70b11ddccec836516a343f9ed0f82aac043c24a"}, - {file = "lxml-5.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:bbc4b80af581e18568ff07f6395c02114d05f4865c2812a1f02f2eaecf0bfd48"}, - {file = "lxml-5.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e3d9d13603410b72787579769469af730c38f2f25505573a5888a94b62b920f8"}, - {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38b67afb0a06b8575948641c1d6d68e41b83a3abeae2ca9eed2ac59892b36706"}, - {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c689d0d5381f56de7bd6966a4541bff6e08bf8d3871bbd89a0c6ab18aa699573"}, - {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:cf2a978c795b54c539f47964ec05e35c05bd045db5ca1e8366988c7f2fe6b3ce"}, - {file = "lxml-5.2.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:739e36ef7412b2bd940f75b278749106e6d025e40027c0b94a17ef7968d55d56"}, - {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d8bbcd21769594dbba9c37d3c819e2d5847656ca99c747ddb31ac1701d0c0ed9"}, - {file = "lxml-5.2.2-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:2304d3c93f2258ccf2cf7a6ba8c761d76ef84948d87bf9664e14d203da2cd264"}, - {file = "lxml-5.2.2-cp36-cp36m-win32.whl", hash = "sha256:02437fb7308386867c8b7b0e5bc4cd4b04548b1c5d089ffb8e7b31009b961dc3"}, - {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, - {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, - {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, - {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, - {file = "lxml-5.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ed07b3062b055d7a7f9d6557a251cc655eed0b3152b76de619516621c56f5d3"}, - {file = "lxml-5.2.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60fdd125d85bf9c279ffb8e94c78c51b3b6a37711464e1f5f31078b45002421"}, - {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a7e24cb69ee5f32e003f50e016d5fde438010c1022c96738b04fc2423e61706"}, - {file = "lxml-5.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cfafd56887eaed93d07bc4547abd5e09d837a002b791e9767765492a75883f"}, - {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:19b4e485cd07b7d83e3fe3b72132e7df70bfac22b14fe4bf7a23822c3a35bff5"}, - {file = "lxml-5.2.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7ce7ad8abebe737ad6143d9d3bf94b88b93365ea30a5b81f6877ec9c0dee0a48"}, - {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e49b052b768bb74f58c7dda4e0bdf7b79d43a9204ca584ffe1fb48a6f3c84c66"}, - {file = "lxml-5.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d14a0d029a4e176795cef99c056d58067c06195e0c7e2dbb293bf95c08f772a3"}, - {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:be49ad33819d7dcc28a309b86d4ed98e1a65f3075c6acd3cd4fe32103235222b"}, - {file = "lxml-5.2.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a6d17e0370d2516d5bb9062c7b4cb731cff921fc875644c3d751ad857ba9c5b1"}, - {file = "lxml-5.2.2-cp38-cp38-win32.whl", hash = "sha256:5b8c041b6265e08eac8a724b74b655404070b636a8dd6d7a13c3adc07882ef30"}, - {file = "lxml-5.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:f61efaf4bed1cc0860e567d2ecb2363974d414f7f1f124b1df368bbf183453a6"}, - {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fb91819461b1b56d06fa4bcf86617fac795f6a99d12239fb0c68dbeba41a0a30"}, - {file = "lxml-5.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d4ed0c7cbecde7194cd3228c044e86bf73e30a23505af852857c09c24e77ec5d"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54401c77a63cc7d6dc4b4e173bb484f28a5607f3df71484709fe037c92d4f0ed"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:625e3ef310e7fa3a761d48ca7ea1f9d8718a32b1542e727d584d82f4453d5eeb"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:519895c99c815a1a24a926d5b60627ce5ea48e9f639a5cd328bda0515ea0f10c"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c7079d5eb1c1315a858bbf180000757db8ad904a89476653232db835c3114001"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:343ab62e9ca78094f2306aefed67dcfad61c4683f87eee48ff2fd74902447726"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:cd9e78285da6c9ba2d5c769628f43ef66d96ac3085e59b10ad4f3707980710d3"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:546cf886f6242dff9ec206331209db9c8e1643ae642dea5fdbecae2453cb50fd"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:02f6a8eb6512fdc2fd4ca10a49c341c4e109aa6e9448cc4859af5b949622715a"}, - {file = "lxml-5.2.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:339ee4a4704bc724757cd5dd9dc8cf4d00980f5d3e6e06d5847c1b594ace68ab"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0a028b61a2e357ace98b1615fc03f76eb517cc028993964fe08ad514b1e8892d"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f90e552ecbad426eab352e7b2933091f2be77115bb16f09f78404861c8322981"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d83e2d94b69bf31ead2fa45f0acdef0757fa0458a129734f59f67f3d2eb7ef32"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a02d3c48f9bb1e10c7788d92c0c7db6f2002d024ab6e74d6f45ae33e3d0288a3"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d68ce8e7b2075390e8ac1e1d3a99e8b6372c694bbe612632606d1d546794207"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:453d037e09a5176d92ec0fd282e934ed26d806331a8b70ab431a81e2fbabf56d"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3b019d4ee84b683342af793b56bb35034bd749e4cbdd3d33f7d1107790f8c472"}, - {file = "lxml-5.2.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb3942960f0beb9f46e2a71a3aca220d1ca32feb5a398656be934320804c0df9"}, - {file = "lxml-5.2.2-cp39-cp39-win32.whl", hash = "sha256:ac6540c9fff6e3813d29d0403ee7a81897f1d8ecc09a8ff84d2eea70ede1cdbf"}, - {file = "lxml-5.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:610b5c77428a50269f38a534057444c249976433f40f53e3b47e68349cca1425"}, - {file = "lxml-5.2.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b537bd04d7ccd7c6350cdaaaad911f6312cbd61e6e6045542f781c7f8b2e99d2"}, - {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4820c02195d6dfb7b8508ff276752f6b2ff8b64ae5d13ebe02e7667e035000b9"}, - {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a09f6184f17a80897172863a655467da2b11151ec98ba8d7af89f17bf63dae"}, - {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:76acba4c66c47d27c8365e7c10b3d8016a7da83d3191d053a58382311a8bf4e1"}, - {file = "lxml-5.2.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b128092c927eaf485928cec0c28f6b8bead277e28acf56800e972aa2c2abd7a2"}, - {file = "lxml-5.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ae791f6bd43305aade8c0e22f816b34f3b72b6c820477aab4d18473a37e8090b"}, - {file = "lxml-5.2.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a2f6a1bc2460e643785a2cde17293bd7a8f990884b822f7bca47bee0a82fc66b"}, - {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e8d351ff44c1638cb6e980623d517abd9f580d2e53bfcd18d8941c052a5a009"}, - {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec4bd9133420c5c52d562469c754f27c5c9e36ee06abc169612c959bd7dbb07"}, - {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:55ce6b6d803890bd3cc89975fca9de1dff39729b43b73cb15ddd933b8bc20484"}, - {file = "lxml-5.2.2-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8ab6a358d1286498d80fe67bd3d69fcbc7d1359b45b41e74c4a26964ca99c3f8"}, - {file = "lxml-5.2.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:06668e39e1f3c065349c51ac27ae430719d7806c026fec462e5693b08b95696b"}, - {file = "lxml-5.2.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9cd5323344d8ebb9fb5e96da5de5ad4ebab993bbf51674259dbe9d7a18049525"}, - {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89feb82ca055af0fe797a2323ec9043b26bc371365847dbe83c7fd2e2f181c34"}, - {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e481bba1e11ba585fb06db666bfc23dbe181dbafc7b25776156120bf12e0d5a6"}, - {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d6c6ea6a11ca0ff9cd0390b885984ed31157c168565702959c25e2191674a14"}, - {file = "lxml-5.2.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3d98de734abee23e61f6b8c2e08a88453ada7d6486dc7cdc82922a03968928db"}, - {file = "lxml-5.2.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:69ab77a1373f1e7563e0fb5a29a8440367dec051da6c7405333699d07444f511"}, - {file = "lxml-5.2.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:34e17913c431f5ae01d8658dbf792fdc457073dcdfbb31dc0cc6ab256e664a8d"}, - {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f8757b03208c3f50097761be2dea0aba02e94f0dc7023ed73a7bb14ff11eb0"}, - {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a520b4f9974b0a0a6ed73c2154de57cdfd0c8800f4f15ab2b73238ffed0b36e"}, - {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5e097646944b66207023bc3c634827de858aebc226d5d4d6d16f0b77566ea182"}, - {file = "lxml-5.2.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b5e4ef22ff25bfd4ede5f8fb30f7b24446345f3e79d9b7455aef2836437bc38a"}, - {file = "lxml-5.2.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff69a9a0b4b17d78170c73abe2ab12084bdf1691550c5629ad1fe7849433f324"}, - {file = "lxml-5.2.2.tar.gz", hash = "sha256:bb2dc4898180bea79863d5487e5f9c7c34297414bad54bcd0f0852aee9cfdb87"}, + {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656"}, + {file = "lxml-5.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_ppc64le.whl", hash = "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_s390x.whl", hash = "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8"}, + {file = "lxml-5.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03"}, + {file = "lxml-5.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7"}, + {file = "lxml-5.3.0-cp310-cp310-win32.whl", hash = "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80"}, + {file = "lxml-5.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3"}, + {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b"}, + {file = "lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080"}, + {file = "lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec"}, + {file = "lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be"}, + {file = "lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9"}, + {file = "lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1"}, + {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859"}, + {file = "lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c"}, + {file = "lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8"}, + {file = "lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d"}, + {file = "lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30"}, + {file = "lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f"}, + {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a"}, + {file = "lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367"}, + {file = "lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb"}, + {file = "lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b"}, + {file = "lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957"}, + {file = "lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d"}, + {file = "lxml-5.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99"}, + {file = "lxml-5.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237"}, + {file = "lxml-5.3.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577"}, + {file = "lxml-5.3.0-cp36-cp36m-win32.whl", hash = "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70"}, + {file = "lxml-5.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c"}, + {file = "lxml-5.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512"}, + {file = "lxml-5.3.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b"}, + {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5"}, + {file = "lxml-5.3.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11"}, + {file = "lxml-5.3.0-cp37-cp37m-win32.whl", hash = "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84"}, + {file = "lxml-5.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e"}, + {file = "lxml-5.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15"}, + {file = "lxml-5.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920"}, + {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945"}, + {file = "lxml-5.3.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42"}, + {file = "lxml-5.3.0-cp38-cp38-win32.whl", hash = "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e"}, + {file = "lxml-5.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903"}, + {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de"}, + {file = "lxml-5.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_ppc64le.whl", hash = "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_s390x.whl", hash = "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe"}, + {file = "lxml-5.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727"}, + {file = "lxml-5.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a"}, + {file = "lxml-5.3.0-cp39-cp39-win32.whl", hash = "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff"}, + {file = "lxml-5.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83"}, + {file = "lxml-5.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f"}, + {file = "lxml-5.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8"}, + {file = "lxml-5.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9"}, + {file = "lxml-5.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c"}, + {file = "lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f"}, ] [package.extras] @@ -2088,7 +2015,7 @@ cssselect = ["cssselect (>=0.7)"] html-clean = ["lxml-html-clean"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=3.0.10)"] +source = ["Cython (>=3.0.11)"] [[package]] name = "markdown-it-py" @@ -2185,13 +2112,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.3" +version = "3.22.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, - {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, + {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, + {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, ] [package.dependencies] @@ -2199,7 +2126,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -2668,6 +2595,22 @@ docs = ["sphinx (>=4.4)", "sphinx-issues (>=3.0.1)", "sphinx-rtd-theme (>=1.0)"] tests = ["defusedxml", "numpy", "packaging", "pympler", "pytest"] tests-min = ["defusedxml", "packaging", "pytest"] +[[package]] +name = "platformdirs" +version = "4.2.2" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + [[package]] name = "pluggy" version = "1.5.0" @@ -2719,26 +2662,72 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "proto-plus" +version = "1.24.0" +description = "Beautiful, Pythonic protocol buffers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445"}, + {file = "proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12"}, +] + +[package.dependencies] +protobuf = ">=3.19.0,<6.0.0dev" + +[package.extras] +testing = ["google-api-core (>=1.31.5)"] + [[package]] name = "protobuf" -version = "4.25.4" +version = "5.27.4" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, - {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, - {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, - {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, - {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, - {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, - {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, - {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, - {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, + {file = "protobuf-5.27.4-cp310-abi3-win32.whl", hash = "sha256:10319748764b917a9a7cddef1582a0a9cd0f8f6d04e545c6236f7ccaf9b624d9"}, + {file = "protobuf-5.27.4-cp310-abi3-win_amd64.whl", hash = "sha256:f0c24374aaaf103f33662e4de7666a4a4280abebdb8a9f3f0f9b1d71b61174ec"}, + {file = "protobuf-5.27.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e85fed07013e5a0121efbaf1b14355fdc66f6e545f12fc5985b2882370410006"}, + {file = "protobuf-5.27.4-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:d5a0e229061600842e57af4ff6a8522ede5280bcfa4fe7f3a1c20589377859a6"}, + {file = "protobuf-5.27.4-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:25ba1f0633f73c3939f3b84e1636f3eb3bab7196952ebb83906d56945edd6aa8"}, + {file = "protobuf-5.27.4-cp38-cp38-win32.whl", hash = "sha256:565b051249a2f8270af04206dd4f3b73a02343e7d9e072aed57441b369b3467d"}, + {file = "protobuf-5.27.4-cp38-cp38-win_amd64.whl", hash = "sha256:e673f173cbac4e59c7817ed358e471e4c77aa9166986edf3e731156379a556c7"}, + {file = "protobuf-5.27.4-cp39-cp39-win32.whl", hash = "sha256:25169c7624d5a9e669fa6faff5a6e818f854346d51ee347b2284676beb9e85dd"}, + {file = "protobuf-5.27.4-cp39-cp39-win_amd64.whl", hash = "sha256:1fe7735902e84ce35c4152cf07981c176713935a8efad78cea547aae5f4f75cb"}, + {file = "protobuf-5.27.4-py3-none-any.whl", hash = "sha256:b97259641e8d38738eef34a173e51d2d53a453baab01a32477a64752d9ce59a3"}, + {file = "protobuf-5.27.4.tar.gz", hash = "sha256:eaa1016e353d8fc5bf08c8087e96eed15f5297aa52bb7ee1f533278bb3f3aad7"}, ] +[[package]] +name = "psutil" +version = "6.0.0" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, + {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, + {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, + {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, + {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, + {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, + {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, + {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, + {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, + {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, + {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, + {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, + {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "psycopg2" version = "2.9.9" @@ -2979,20 +2968,18 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pygsheets" -version = "2.0.2" +version = "2.0.6" description = "Google Spreadsheets Python API v4" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ - {file = "pygsheets-2.0.2-py2-none-any.whl", hash = "sha256:5f57c3bbdbf3f6c0442c1daf6aab0447f787a5078c1a68584109a43067637e02"}, - {file = "pygsheets-2.0.2-py3-none-any.whl", hash = "sha256:c2ddd56ff9dcccad6fa4da5fabb747c0b71ca53643e200e5f51a844fc815fc5c"}, - {file = "pygsheets-2.0.2.tar.gz", hash = "sha256:8f3e3c614a39d9d69e8e2184033dcad996653a62773ce41795b407400e692668"}, + {file = "pygsheets-2.0.6-py3-none-any.whl", hash = "sha256:3338c2eb8990fdee9f463b42a370ec0870c118d607d775471a6dfb8b08f6cd87"}, + {file = "pygsheets-2.0.6.tar.gz", hash = "sha256:bff46c812e99f9b8b81a09b456581365281c797620ec08530b0d0e48fa9299e2"}, ] [package.dependencies] -enum34 = "*" -google-api-python-client = ">=1.5.5" -google-auth-oauthlib = "*" +google-api-python-client = ">=2.50.0" +google-auth-oauthlib = ">=0.7.1" [package.extras] pandas = ["pandas (>=0.14.0)"] @@ -3042,13 +3029,13 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] [[package]] name = "pyparsing" -version = "3.1.2" +version = "3.1.4" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, ] [package.extras] @@ -3087,11 +3074,9 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -3145,7 +3130,6 @@ files = [ [package.dependencies] pytest = ">=7.4.3" -tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] test = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "pytest-mock (>=3.12)"] @@ -3308,9 +3292,6 @@ files = [ {file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"}, ] -[package.dependencies] -async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} - [package.extras] hiredis = ["hiredis (>=1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] @@ -3336,6 +3317,20 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-file" +version = "2.1.0" +description = "File transport adapter for Requests" +optional = false +python-versions = "*" +files = [ + {file = "requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c"}, + {file = "requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658"}, +] + +[package.dependencies] +requests = ">=1.0.0" + [[package]] name = "requests-oauthlib" version = "2.0.0" @@ -3389,13 +3384,13 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rich" -version = "13.7.1" +version = "13.8.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, + {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, ] [package.dependencies] @@ -3498,29 +3493,29 @@ files = [ [[package]] name = "ruff" -version = "0.5.6" +version = "0.5.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.6-py3-none-linux_armv6l.whl", hash = "sha256:a0ef5930799a05522985b9cec8290b185952f3fcd86c1772c3bdbd732667fdcd"}, - {file = "ruff-0.5.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b652dc14f6ef5d1552821e006f747802cc32d98d5509349e168f6bf0ee9f8f42"}, - {file = "ruff-0.5.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:80521b88d26a45e871f31e4b88938fd87db7011bb961d8afd2664982dfc3641a"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9bc8f328a9f1309ae80e4d392836e7dbc77303b38ed4a7112699e63d3b066ab"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d394940f61f7720ad371ddedf14722ee1d6250fd8d020f5ea5a86e7be217daf"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111a99cdb02f69ddb2571e2756e017a1496c2c3a2aeefe7b988ddab38b416d36"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e395daba77a79f6dc0d07311f94cc0560375ca20c06f354c7c99af3bf4560c5d"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c476acb43c3c51e3c614a2e878ee1589655fa02dab19fe2db0423a06d6a5b1b6"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2ff8003f5252fd68425fd53d27c1f08b201d7ed714bb31a55c9ac1d4c13e2eb"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c94e084ba3eaa80c2172918c2ca2eb2230c3f15925f4ed8b6297260c6ef179ad"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f77c1c3aa0669fb230b06fb24ffa3e879391a3ba3f15e3d633a752da5a3e670"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f908148c93c02873210a52cad75a6eda856b2cbb72250370ce3afef6fb99b1ed"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:563a7ae61ad284187d3071d9041c08019975693ff655438d8d4be26e492760bd"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:94fe60869bfbf0521e04fd62b74cbca21cbc5beb67cbb75ab33fe8c174f54414"}, - {file = "ruff-0.5.6-py3-none-win32.whl", hash = "sha256:e6a584c1de6f8591c2570e171cc7ce482bb983d49c70ddf014393cd39e9dfaed"}, - {file = "ruff-0.5.6-py3-none-win_amd64.whl", hash = "sha256:d7fe7dccb1a89dc66785d7aa0ac283b2269712d8ed19c63af908fdccca5ccc1a"}, - {file = "ruff-0.5.6-py3-none-win_arm64.whl", hash = "sha256:57c6c0dd997b31b536bff49b9eee5ed3194d60605a4427f735eeb1f9c1b8d264"}, - {file = "ruff-0.5.6.tar.gz", hash = "sha256:07c9e3c2a8e1fe377dd460371c3462671a728c981c3205a5217291422209f642"}, + {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"}, + {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"}, + {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"}, + {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"}, + {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"}, + {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"}, + {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, ] [[package]] @@ -3542,13 +3537,13 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] [[package]] name = "safety" -version = "3.2.4" +version = "3.2.6" description = "Checks installed dependencies for known vulnerabilities and licenses." optional = false python-versions = ">=3.7" files = [ - {file = "safety-3.2.4-py3-none-any.whl", hash = "sha256:242ff7ae448d7fb2ea455c90f44e3f2ca45be9c8559b2fe9dfc89617164a0f17"}, - {file = "safety-3.2.4.tar.gz", hash = "sha256:bac0202016d736a2118057964a0e3983fa20ff2563fd103cac3f3ac1ed3fea11"}, + {file = "safety-3.2.6-py3-none-any.whl", hash = "sha256:fcd87f8c1882a72c26aef77b1e2b0f373800fb76df1b5f44ebeba9bff4e37b5e"}, + {file = "safety-3.2.6.tar.gz", hash = "sha256:6a361e362d582f1ac52a179f1ae0fd1b5acbe05d37b1144fdeae792573201f9f"}, ] [package.dependencies] @@ -3559,11 +3554,12 @@ filelock = ">=3.12.2,<3.13.0" jinja2 = ">=3.1.0" marshmallow = ">=3.15.0" packaging = ">=21.0" +psutil = ">=6.0.0,<6.1.0" pydantic = ">=1.10.12" requests = "*" rich = "*" "ruamel.yaml" = ">=0.17.21" -safety-schemas = ">=0.0.2" +safety-schemas = ">=0.0.4" setuptools = ">=65.5.1" typer = "*" typing-extensions = ">=4.7.1" @@ -3576,13 +3572,13 @@ spdx = ["spdx-tools (>=0.8.2)"] [[package]] name = "safety-schemas" -version = "0.0.2" +version = "0.0.5" description = "Schemas for Safety tools" optional = false python-versions = ">=3.7" files = [ - {file = "safety_schemas-0.0.2-py3-none-any.whl", hash = "sha256:277c077ce6e53221874a87c29515ffdd2f3773a6db4d035a9f67cc98db3b8c7f"}, - {file = "safety_schemas-0.0.2.tar.gz", hash = "sha256:7d1b040ec06480f05cff6b45ea7a93e09c8942df864fb0d01ddeb67c323cfa8c"}, + {file = "safety_schemas-0.0.5-py3-none-any.whl", hash = "sha256:6ac9eb71e60f0d4e944597c01dd48d6d8cd3d467c94da4aba3702a05a3a6ab4f"}, + {file = "safety_schemas-0.0.5.tar.gz", hash = "sha256:0de5fc9a53d4423644a8ce9a17a2e474714aa27e57f3506146e95a41710ff104"}, ] [package.dependencies] @@ -3609,13 +3605,13 @@ doc = ["Sphinx", "sphinx-rtd-theme"] [[package]] name = "sentry-sdk" -version = "2.12.0" +version = "2.13.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = ">=3.6" files = [ - {file = "sentry_sdk-2.12.0-py2.py3-none-any.whl", hash = "sha256:7a8d5163d2ba5c5f4464628c6b68f85e86972f7c636acc78aed45c61b98b7a5e"}, - {file = "sentry_sdk-2.12.0.tar.gz", hash = "sha256:8763840497b817d44c49b3fe3f5f7388d083f2337ffedf008b2cdb63b5c86dc6"}, + {file = "sentry_sdk-2.13.0-py2.py3-none-any.whl", hash = "sha256:6beede8fc2ab4043da7f69d95534e320944690680dd9a963178a49de71d726c6"}, + {file = "sentry_sdk-2.13.0.tar.gz", hash = "sha256:8d4a576f7a98eb2fdb40e13106e41f330e5c79d72a68be1316e7852cf4995260"}, ] [package.dependencies] @@ -3642,6 +3638,7 @@ httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] huggingface-hub = ["huggingface-hub (>=0.22)"] langchain = ["langchain (>=0.0.210)"] +litestar = ["litestar (>=2.0.0)"] loguru = ["loguru (>=0.5)"] openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] @@ -3659,19 +3656,23 @@ tornado = ["tornado (>=6)"] [[package]] name = "setuptools" -version = "72.1.0" +version = "74.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-74.0.0-py3-none-any.whl", hash = "sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f"}, + {file = "setuptools-74.0.0.tar.gz", hash = "sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] [[package]] name = "shellingham" @@ -3738,13 +3739,13 @@ saml = ["python3-saml (>=1.5.0)"] [[package]] name = "soupsieve" -version = "2.5" +version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] [[package]] @@ -3810,17 +3811,6 @@ files = [ doc = ["reno", "sphinx"] test = ["pytest", "tornado (>=4.5)", "typeguard"] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "toolz" version = "0.12.1" @@ -3849,13 +3839,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typer" -version = "0.12.3" +version = "0.12.5" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ - {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, - {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, + {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, + {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, ] [package.dependencies] @@ -3910,30 +3900,31 @@ files = [ [[package]] name = "uritemplate" -version = "3.0.1" -description = "URI templates" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" files = [ - {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, - {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, ] [[package]] name = "urllib3" -version = "1.26.19" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-1.26.19-py2.py3-none-any.whl", hash = "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3"}, - {file = "urllib3-1.26.19.tar.gz", hash = "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] -brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "user-agents" @@ -4087,7 +4078,7 @@ files = [ [package.dependencies] defusedxml = ">=0.7,<1.0" filetype = ">=1.0.10,<1.1.0 || >1.1.0" -pillow-heif = {version = ">=0.10.0,<1.0.0", optional = true, markers = "extra == \"heif\" and python_version < \"3.12\""} +pillow-heif = {version = ">=0.13.0,<1.0.0", optional = true, markers = "extra == \"heif\" and python_version >= \"3.12\""} [package.extras] docs = ["Sphinx (>=7.0)", "sphinx-wagtail-theme (==6.0.0)", "sphinx_copybutton (>=0.5)", "sphinxcontrib-spelling (>=8.0,<9.0)"] @@ -4124,35 +4115,32 @@ files = [ [[package]] name = "zeep" -version = "3.4.0" -description = "A modern/fast Python SOAP client based on lxml / requests" +version = "4.2.1" +description = "A Python SOAP client" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "zeep-3.4.0-py2.py3-none-any.whl", hash = "sha256:59a4068ab3817b589ee21c6c34166c35baa8f2be7ad69f045d005076a29911c1"}, - {file = "zeep-3.4.0.tar.gz", hash = "sha256:0e98669cfeb60756231ae185498f9ae21b30b2681786b8de58ed34c3b93e41dd"}, + {file = "zeep-4.2.1-py3-none-any.whl", hash = "sha256:6754feb4c34a4b6d65fbc359252bf6654dcce3937bf1d95aae4402a60a8f5939"}, + {file = "zeep-4.2.1.tar.gz", hash = "sha256:72093acfdb1d8360ed400869b73fbf1882b95c4287f798084c42ee0c1ff0e425"}, ] [package.dependencies] -appdirs = ">=1.4.0" attrs = ">=17.2.0" -cached-property = ">=1.3.0" -defusedxml = ">=0.4.1" isodate = ">=0.5.4" -lxml = ">=3.1.0" +lxml = ">=4.6.0" +platformdirs = ">=1.4.0" pytz = "*" requests = ">=2.7.0" +requests-file = ">=1.5.1" requests-toolbelt = ">=0.7.1" -six = ">=1.9.0" [package.extras] -async = ["aiohttp (>=1.0)"] +async = ["httpx (>=0.15.0)"] docs = ["sphinx (>=1.4.0)"] -test = ["aioresponses (>=0.4.1)", "flake8 (==3.3.0)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==1.4.0)", "flake8-imports (==0.1.1)", "freezegun (==0.3.8)", "isort (==4.2.15)", "mock (==2.0.0)", "pretend (==1.0.8)", "pytest (==3.1.3)", "pytest-cov (==2.5.1)", "pytest-tornado (==0.4.5)", "requests-mock (>=0.7.0)"] -tornado = ["tornado (>=4.0.2,<5)"] +test = ["coverage[toml] (==5.2.1)", "flake8 (==3.8.3)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==3.2.1)", "flake8-imports (==0.1.1)", "freezegun (==0.3.15)", "isort (==5.3.2)", "pretend (==1.0.9)", "pytest (==6.2.5)", "pytest-asyncio", "pytest-cov (==2.8.1)", "pytest-httpx", "requests-mock (>=0.7.0)"] xmlsec = ["xmlsec (>=0.6.1)"] [metadata] lock-version = "2.0" -python-versions = "3.9.15" -content-hash = "d24645f9007b00d57c1599df30450c0fe58693ab561e21ece11e8118b5309d04" +python-versions = "3.12.5" +content-hash = "42e8b0f9135818244fac2de94471bbeb7b4b30bb565ae9b9fc180a574bb3cd2b" diff --git a/pyproject.toml b/pyproject.toml index b1d27a80f..2ace048b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,23 +8,23 @@ readme = "README.md" packages = [] [tool.poetry.dependencies] -python = "3.9.15" +python = "3.12.5" Pillow = "10.4.0" PyNaCl = "1.5.0" beautifulsoup4 = "4.8.2" -boto3 = "1.34.149" +boto3 = "1.35.8" celery = "5.4.0" celery-redbeat = "2.2.0" dj-database-url = "0.5.0" django = "4.2.15" django-anymail = { version = "8.6", extras = ["mailgun"] } django-filter = "^23.4" -django-hijack = "3.4.5" +django-hijack = "3.6.0" django-ipware = "3.0.7" django-oauth-toolkit = "1.7.1" django-redis = "5.0.0" -django-robots = "6.0" +django-robots = "6.1" django-silk = "^5.0.3" django-storages = "1.14.4" django-user-agents = "0.4.0" @@ -33,8 +33,8 @@ djangorestframework = "3.15.2" drf-flex-fields = "0.9.9" edx-api-client = "1.10.0" flaky = "3.8.1" -google-api-python-client = "1.12.11" -google-auth = "1.35.0" +google-api-python-client = "2.50.0" +google-auth = "2.15.0" hubspot-api-client = "^6.1.0" ipython = "^8.10.0" mitol-django-authentication = "2023.12.19" @@ -47,7 +47,7 @@ newrelic = "^8.4.0" pdftotext = "^2.1.1" psycopg2 = "2.9.9" pycountry = "19.8.18" -pygsheets = "2.0.2" +pygsheets = "2.0.6" redis = "4.6.0" requests = "^2.31.0" sentry-sdk = "^2.0.0" @@ -60,7 +60,7 @@ uwsgi = "2.0.26" wagtail = "5.2.6" wagtail-metadata = "5.0.0" xmltodict = "^0.13.0" -zeep = "3.4.0" +zeep = "4.2.1" [tool.poetry.group.dev.dependencies] bpython = "*" @@ -68,7 +68,7 @@ django-debug-toolbar = "*" factory-boy = "3.3.0" faker = "13.16.0" freezegun = "0.3.15" -hypothesis = "4.23.4" +hypothesis = "4.24.6" ipdb = "*" nplusone = ">=0.8.1" pdbpp = "*" @@ -89,7 +89,7 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.ruff] -target-version = "py39" +target-version = "py312" line-length = 88 [tool.ruff.lint] diff --git a/repl.py b/repl.py index 5d909a5a8..8c807b0b3 100755 --- a/repl.py +++ b/repl.py @@ -23,7 +23,7 @@ for app in settings.INSTALLED_APPS: try: # noqa: SIM105 exec( # noqa: S102 - "from {app}.models import *".format(app=app) # noqa: UP032 + f"from {app}.models import *" ) - except ModuleNotFoundError: # noqa: PERF203 + except ModuleNotFoundError: pass diff --git a/sheets/api.py b/sheets/api.py index 602090442..1ed5a2039 100644 --- a/sheets/api.py +++ b/sheets/api.py @@ -185,7 +185,7 @@ def get_metadata_for_matching_files(self, query, file_fields="id, name"): ) return self.pygsheets_client.drive.list( **extra_list_params, - fields="files({})".format(file_fields), # noqa: UP032 + fields=f"files({file_fields})", q=query, ) diff --git a/sheets/api_test.py b/sheets/api_test.py index 309a638d3..86a30df27 100644 --- a/sheets/api_test.py +++ b/sheets/api_test.py @@ -26,7 +26,7 @@ def test_get_credentials_service_account(mocker, settings): get_credentials() settings.SHEETS_ADMIN_EMAILS.append( - "service-account@mitxpro.{}".format(GOOGLE_SERVICE_ACCOUNT_EMAIL_DOMAIN) # noqa: UP032 + f"service-account@mitxpro.{GOOGLE_SERVICE_ACCOUNT_EMAIL_DOMAIN}" ) creds = get_credentials() diff --git a/sheets/coupon_assign_api.py b/sheets/coupon_assign_api.py index 925755ba7..31155d25b 100644 --- a/sheets/coupon_assign_api.py +++ b/sheets/coupon_assign_api.py @@ -823,7 +823,7 @@ def get_desired_coupon_assignments(cls, assignment_rows): "codes listed in the Sheet. There may be an invalid coupon code in the Sheet." ) product_coupon_dict = dict(product_coupon_tuples) - return set((row.email, product_coupon_dict[row.code]) for row in valid_rows) # noqa: C401 + return {(row.email, product_coupon_dict[row.code]) for row in valid_rows} @staticmethod def get_assignments_to_create_and_remove( @@ -872,10 +872,10 @@ def get_assignments_to_create_and_remove( ) # If any of the assignments we want to create have the same product coupon as one # of these already-redeemed assignments, filter them out and log an info message. - product_coupon_ids = set( # noqa: C401 + product_coupon_ids = { assignment.product_coupon_id for assignment in already_redeemed_assignments - ) + } adjusted_create_iter, cannot_create_iter = partition( tuple_set_to_create, lambda assignment_tuple: assignment_tuple[1] in product_coupon_ids, @@ -988,7 +988,7 @@ def process_assignment_spreadsheet(self): # Validate emails before assignment so we can filter out and report on any bad emails try: validate_email_addresses( - (assignment_tuple[0] for assignment_tuple in assignments_to_create) # noqa: UP034 + assignment_tuple[0] for assignment_tuple in assignments_to_create ) except MultiEmailValidationError as exc: invalid_emails = exc.invalid_emails diff --git a/sheets/coupon_request_api.py b/sheets/coupon_request_api.py index e697e76e9..62948f363 100644 --- a/sheets/coupon_request_api.py +++ b/sheets/coupon_request_api.py @@ -273,7 +273,7 @@ def create_assignment_sheet(self, coupon_req_row): worksheet = bulk_coupon_sheet.sheet1 # Add headers worksheet.update_values( - crange="A1:{}1".format(assign_sheet_metadata.LAST_COL_LETTER), # noqa: UP032 + crange=f"A1:{assign_sheet_metadata.LAST_COL_LETTER}1", values=[assign_sheet_metadata.column_headers], ) # Write enrollment codes to the appropriate column of the worksheet @@ -311,7 +311,7 @@ def create_assignment_sheet(self, coupon_req_row): # Format header cells with bold text header_range = worksheet.get_values( start="A1", - end="{}1".format(assign_sheet_metadata.LAST_COL_LETTER), # noqa: UP032 + end=f"{assign_sheet_metadata.LAST_COL_LETTER}1", returnas="range", ) first_cell = header_range.cells[0][0] @@ -444,7 +444,7 @@ def process_row(self, row_index, row_data): row_db_record=coupon_gen_request, row_object=None, result_type=ResultType.FAILED, - message="Parsing failure: {}".format(str(exc)), # noqa: UP032 + message=f"Parsing failure: {exc!s}", ) is_unchanged_error_row = ( coupon_req_row.errors and not request_created and not request_updated diff --git a/sheets/deferral_request_api.py b/sheets/deferral_request_api.py index c3136841a..d6df76dde 100644 --- a/sheets/deferral_request_api.py +++ b/sheets/deferral_request_api.py @@ -134,7 +134,7 @@ def process_row( # noqa: C901, PLR0911 row_db_record=deferral_request, row_object=None, result_type=ResultType.FAILED, - message="Parsing failure: {}".format(str(exc)), # noqa: UP032 + message=f"Parsing failure: {exc!s}", ) is_unchanged_error_row = ( deferral_req_row.errors and not request_created and not request_updated @@ -202,7 +202,7 @@ def process_row( # noqa: C901, PLR0911 row_db_record=deferral_request, row_object=None, result_type=ResultType.FAILED, - message="Invalid deferral: {}".format(exc), # noqa: UP032 + message=f"Invalid deferral: {exc}", ) except EdxEnrollmentCreateError as exc: return RowResult( @@ -210,7 +210,7 @@ def process_row( # noqa: C901, PLR0911 row_db_record=deferral_request, row_object=None, result_type=ResultType.FAILED, - message="Unable to defer enrollment: {}".format(exc), # noqa: UP032 + message=f"Unable to defer enrollment: {exc}", ) deferral_request.date_completed = now_in_utc() diff --git a/sheets/factories.py b/sheets/factories.py index e0e2c8b5b..c7d37cf85 100644 --- a/sheets/factories.py +++ b/sheets/factories.py @@ -29,12 +29,8 @@ class Meta: class GoogleFileWatchFactory(DjangoModelFactory): file_id = Faker("pystr", max_chars=15) channel_id = fuzzy.FuzzyText(prefix="Channel ") - activation_date = Faker( - "past_datetime", start_date="-30d", tzinfo=datetime.timezone.utc - ) - expiration_date = Faker( - "future_datetime", end_date="+30d", tzinfo=datetime.timezone.utc - ) + activation_date = Faker("past_datetime", start_date="-30d", tzinfo=datetime.UTC) + expiration_date = Faker("future_datetime", end_date="+30d", tzinfo=datetime.UTC) class Meta: model = models.GoogleFileWatch diff --git a/sheets/mail_api.py b/sheets/mail_api.py index df583e95e..ec117f73a 100644 --- a/sheets/mail_api.py +++ b/sheets/mail_api.py @@ -69,8 +69,8 @@ def get_bulk_assignment_messages(event=None, begin=None, end=None): raw_next_url = resp_data["paging"]["next"] # The "next" url in the paging section does not contain necessary auth. Fill it in here. url = raw_next_url.replace( - "/{}/".format(MAILGUN_API_DOMAIN), # noqa: UP032 - "/api:{}@{}/".format(settings.MAILGUN_KEY, MAILGUN_API_DOMAIN), # noqa: UP032 + f"/{MAILGUN_API_DOMAIN}/", + f"/api:{settings.MAILGUN_KEY}@{MAILGUN_API_DOMAIN}/", ) resp = request_get_with_timeout_retry( url, retries=MAILGUN_API_TIMEOUT_RETRIES diff --git a/sheets/management/commands/process_coupon_requests.py b/sheets/management/commands/process_coupon_requests.py index e99ef07ef..5f5160c12 100644 --- a/sheets/management/commands/process_coupon_requests.py +++ b/sheets/management/commands/process_coupon_requests.py @@ -28,5 +28,5 @@ def handle(self, *args, **options): # noqa: ARG002 limit_row_index=options.get("row", None) ) self.stdout.write( - self.style.SUCCESS("Coupon generation succeeded.\n{}".format(results)) # noqa: UP032 + self.style.SUCCESS(f"Coupon generation succeeded.\n{results}") ) diff --git a/sheets/management/commands/process_deferral_requests.py b/sheets/management/commands/process_deferral_requests.py index 0b947c487..017185c90 100644 --- a/sheets/management/commands/process_deferral_requests.py +++ b/sheets/management/commands/process_deferral_requests.py @@ -28,7 +28,5 @@ def handle(self, *args, **options): # noqa: ARG002 limit_row_index=options.get("row", None) ) self.stdout.write( - self.style.SUCCESS( - "Deferral sheet successfully processed.\n{}".format(results) # noqa: UP032 - ) + self.style.SUCCESS(f"Deferral sheet successfully processed.\n{results}") ) diff --git a/sheets/management/commands/process_refund_requests.py b/sheets/management/commands/process_refund_requests.py index 79a5347ea..2a245f804 100644 --- a/sheets/management/commands/process_refund_requests.py +++ b/sheets/management/commands/process_refund_requests.py @@ -28,7 +28,5 @@ def handle(self, *args, **options): # noqa: ARG002 limit_row_index=options.get("row", None) ) self.stdout.write( - self.style.SUCCESS( - "Refund sheet successfully processed.\n{}".format(results) # noqa: UP032 - ) + self.style.SUCCESS(f"Refund sheet successfully processed.\n{results}") ) diff --git a/sheets/management/commands/setup_file_watch.py b/sheets/management/commands/setup_file_watch.py index 44caec990..78fca2c56 100644 --- a/sheets/management/commands/setup_file_watch.py +++ b/sheets/management/commands/setup_file_watch.py @@ -134,9 +134,7 @@ def handle( # noqa: C901 if renewal_attempt is None else f"\n[{renewal_attempt.result_status_code}] {renewal_attempt.result}" ) - self.style.ERROR( - "Failed to create/update file watch.{}".format(error_msg) # noqa: UP032 - ) + self.style.ERROR(f"Failed to create/update file watch.{error_msg}") continue if file_watch_result.created: desc = "created" @@ -146,7 +144,7 @@ def handle( # noqa: C901 desc = "found (unexpired)" file_id_desc = "" if file_watch_result.metadata.sheet_type == SHEET_TYPE_COUPON_ASSIGN: - file_id_desc = " (file id: {})".format(file_watch.file_id) # noqa: UP032 + file_id_desc = f" (file id: {file_watch.file_id})" self.stdout.write( self.style.SUCCESS( @@ -192,9 +190,7 @@ def handle( # noqa: C901 ) ) else: - self.stdout.write( - self.style.ERROR("Request failed: {}".format(exc)) # noqa: UP032 - ) + self.stdout.write(self.style.ERROR(f"Request failed: {exc}")) sys.exit(1) else: self.stdout.write( diff --git a/sheets/migrations/0007_fill_in_gen_request_date_completed.py b/sheets/migrations/0007_fill_in_gen_request_date_completed.py index 928303f72..1c57581d8 100644 --- a/sheets/migrations/0007_fill_in_gen_request_date_completed.py +++ b/sheets/migrations/0007_fill_in_gen_request_date_completed.py @@ -11,7 +11,7 @@ def now_in_utc(): Returns: datetime.datetime: A datetime object for the current time """ - return datetime.datetime.now(tz=datetime.timezone.utc) + return datetime.datetime.now(tz=datetime.UTC) def set_date_completed_to_none(apps, schema_editor): diff --git a/sheets/migrations/0010_fill_in_gen_request_coupon_name.py b/sheets/migrations/0010_fill_in_gen_request_coupon_name.py index 5a157d743..05f623cc1 100644 --- a/sheets/migrations/0010_fill_in_gen_request_coupon_name.py +++ b/sheets/migrations/0010_fill_in_gen_request_coupon_name.py @@ -29,7 +29,7 @@ def fill_in_coupon_name(apps, schema_editor): "raw_data is either not a list, or does not include a valid coupon name" # noqa: EM101 ) except Exception: # noqa: BLE001 - coupon_name = "COUPON NAME NEEDED ({})".format(coupon_gen_request.id) # noqa: UP032 + coupon_name = f"COUPON NAME NEEDED ({coupon_gen_request.id})" else: coupon_name = raw_data[1] coupon_gen_request.coupon_name = coupon_name diff --git a/sheets/refund_request_api.py b/sheets/refund_request_api.py index af943e8f7..0da6e7f3c 100644 --- a/sheets/refund_request_api.py +++ b/sheets/refund_request_api.py @@ -226,7 +226,7 @@ def process_row(self, row_index, row_data): row_db_record=refund_request, row_object=None, result_type=ResultType.FAILED, - message="Parsing failure: {}".format(str(exc)), # noqa: UP032 + message=f"Parsing failure: {exc!s}", ) is_unchanged_error_row = ( refund_req_row.errors and not request_created and not request_updated @@ -263,7 +263,7 @@ def process_row(self, row_index, row_data): ) elif isinstance(exc, Order.DoesNotExist): message = f"Order with id {refund_req_row.order_id} and purchaser '{refund_req_row.learner_email}' not found" - elif isinstance( + elif isinstance( # noqa: UP038 exc, (ProgramEnrollment.DoesNotExist, CourseRunEnrollment.DoesNotExist) ): message = f"Program/Course run enrollment does not exist for product '{refund_req_row.product_id}' and order {refund_req_row.order_id}" diff --git a/sheets/sheet_handler_api.py b/sheets/sheet_handler_api.py index 6f3b23996..5331ae2fa 100644 --- a/sheets/sheet_handler_api.py +++ b/sheets/sheet_handler_api.py @@ -214,7 +214,7 @@ def process_sheet(self, limit_row_index=None): row_db_record=None, row_object=None, result_type=ResultType.FAILED, - message="Error: {}".format(str(exc)), # noqa: UP032 + message=f"Error: {exc!s}", ) finally: if row_result: diff --git a/sheets/utils.py b/sheets/utils.py index 710d57e4c..ecf365377 100644 --- a/sheets/utils.py +++ b/sheets/utils.py @@ -460,8 +460,8 @@ def _parse_sheet_date_str(date_str, date_format): ) return ( dt - if settings.SHEETS_DATE_TIMEZONE == datetime.timezone.utc # noqa: SIM300 - else dt.astimezone(datetime.timezone.utc) + if settings.SHEETS_DATE_TIMEZONE == datetime.UTC + else dt.astimezone(datetime.UTC) ) @@ -503,7 +503,7 @@ def google_timestamp_to_datetime(google_timestamp): """ # Google timestamps are expressed in milliseconds, hence the '/ 1000' timestamp_in_seconds = int(google_timestamp) / 1000 - return datetime.datetime.fromtimestamp(timestamp_in_seconds, datetime.timezone.utc) + return datetime.datetime.fromtimestamp(timestamp_in_seconds, datetime.UTC) def google_date_string_to_datetime(google_date_str): @@ -518,7 +518,7 @@ def google_date_string_to_datetime(google_date_str): """ return datetime.datetime.strptime( google_date_str, "%Y-%m-%dT%H:%M:%S.%fZ" - ).astimezone(datetime.timezone.utc) + ).astimezone(datetime.UTC) def mailgun_timestamp_to_datetime(timestamp): @@ -531,7 +531,7 @@ def mailgun_timestamp_to_datetime(timestamp): Returns: datetime.datetime: The parsed timestamp """ - return datetime.datetime.fromtimestamp(timestamp, datetime.timezone.utc) + return datetime.datetime.fromtimestamp(timestamp, datetime.UTC) def build_multi_cell_update_request_body( diff --git a/static/js/containers/pages/ReceiptPage.js b/static/js/containers/pages/ReceiptPage.js index c21241279..9d28d41b7 100644 --- a/static/js/containers/pages/ReceiptPage.js +++ b/static/js/containers/pages/ReceiptPage.js @@ -106,9 +106,11 @@ export class ReceiptPage extends React.Component { NE49-2000
Cambridge, MA 02139 USA - {SETTINGS.enable_taxes_display ? ( -
GSTIN: Pending
- ) : null} + {orderReceipt.order.tax_rate ? ( +
GSTIN: 9923USA29055OSB
+ ) : ( +
+ )} Support:{" "}
{SETTINGS.support_email} @@ -251,10 +253,10 @@ export class ReceiptPage extends React.Component { Quantity Unit Price Discount - {SETTINGS.enable_taxes_display ? ( + {orderReceipt.order.tax_rate ? ( Total Before Tax ) : null} - {SETTINGS.enable_taxes_display ? ( + {orderReceipt.order.tax_rate ? ( Tax ({formatNumber(orderReceipt.order.tax_rate)}%) @@ -294,14 +296,14 @@ export class ReceiptPage extends React.Component {

Discount

{formatDiscount(line.discount)}
- {SETTINGS.enable_taxes_display ? ( + {orderReceipt.order.tax_rate ? (

Total Before Tax

{formatPrice(line.total_before_tax)}
) : null} - {SETTINGS.enable_taxes_display ? ( + {orderReceipt.order.tax_rate ? (

Tax ( @@ -319,11 +321,11 @@ export class ReceiptPage extends React.Component { })} - {SETTINGS.enable_taxes_display ? ( + {orderReceipt.order.tax_rate ? (

HSN: 9992
) : null} - {SETTINGS.enable_taxes_display ? ( + {orderReceipt.order.tax_rate ? (