diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2f3c3328..cb783c5c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: rev: 'v1.18.2' hooks: - id: mypy - args: [--strict, --ignore-missing-imports, --check-untyped-defs] + args: [--strict, --ignore-missing-imports, --check-untyped-defs, --allow-untyped-decorators] additional_dependencies: - types-click - types-PyYAML diff --git a/poetry.lock b/poetry.lock index 4429bf3b..e0e6712c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand. [[package]] name = "alabaster" @@ -95,6 +95,443 @@ s3transfer = ">=0.16.0,<0.17.0" [package.extras] crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] +[[package]] +name = "boto3-stubs" +version = "1.42.25" +description = "Type annotations for boto3 1.42.25 generated with mypy-boto3-builder 8.12.0" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "boto3_stubs-1.42.25-py3-none-any.whl", hash = "sha256:a61a4caaf2199d11510bb18d044254fa0fd1929a6b07817f00faa8e23437adc5"}, + {file = "boto3_stubs-1.42.25.tar.gz", hash = "sha256:fd40c758991ae1bcbd1adbb153d513a028bf525642f193f9a77f71220c493cf6"}, +] + +[package.dependencies] +botocore-stubs = "*" +mypy-boto3-s3 = {version = ">=1.42.0,<1.43.0", optional = true, markers = "extra == \"s3\""} +types-s3transfer = "*" + +[package.extras] +accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.42.0,<1.43.0)"] +account = ["mypy-boto3-account (>=1.42.0,<1.43.0)"] +acm = ["mypy-boto3-acm (>=1.42.0,<1.43.0)"] +acm-pca = ["mypy-boto3-acm-pca (>=1.42.0,<1.43.0)"] +aiops = ["mypy-boto3-aiops (>=1.42.0,<1.43.0)"] +all = ["mypy-boto3-accessanalyzer (>=1.42.0,<1.43.0)", "mypy-boto3-account (>=1.42.0,<1.43.0)", "mypy-boto3-acm (>=1.42.0,<1.43.0)", "mypy-boto3-acm-pca (>=1.42.0,<1.43.0)", "mypy-boto3-aiops (>=1.42.0,<1.43.0)", "mypy-boto3-amp (>=1.42.0,<1.43.0)", "mypy-boto3-amplify (>=1.42.0,<1.43.0)", "mypy-boto3-amplifybackend (>=1.42.0,<1.43.0)", "mypy-boto3-amplifyuibuilder (>=1.42.0,<1.43.0)", "mypy-boto3-apigateway (>=1.42.0,<1.43.0)", "mypy-boto3-apigatewaymanagementapi (>=1.42.0,<1.43.0)", "mypy-boto3-apigatewayv2 (>=1.42.0,<1.43.0)", "mypy-boto3-appconfig (>=1.42.0,<1.43.0)", "mypy-boto3-appconfigdata (>=1.42.0,<1.43.0)", "mypy-boto3-appfabric (>=1.42.0,<1.43.0)", "mypy-boto3-appflow (>=1.42.0,<1.43.0)", "mypy-boto3-appintegrations (>=1.42.0,<1.43.0)", "mypy-boto3-application-autoscaling (>=1.42.0,<1.43.0)", "mypy-boto3-application-insights (>=1.42.0,<1.43.0)", "mypy-boto3-application-signals (>=1.42.0,<1.43.0)", "mypy-boto3-applicationcostprofiler (>=1.42.0,<1.43.0)", "mypy-boto3-appmesh (>=1.42.0,<1.43.0)", "mypy-boto3-apprunner (>=1.42.0,<1.43.0)", "mypy-boto3-appstream (>=1.42.0,<1.43.0)", "mypy-boto3-appsync (>=1.42.0,<1.43.0)", "mypy-boto3-arc-region-switch (>=1.42.0,<1.43.0)", "mypy-boto3-arc-zonal-shift (>=1.42.0,<1.43.0)", "mypy-boto3-artifact (>=1.42.0,<1.43.0)", "mypy-boto3-athena (>=1.42.0,<1.43.0)", "mypy-boto3-auditmanager (>=1.42.0,<1.43.0)", "mypy-boto3-autoscaling (>=1.42.0,<1.43.0)", "mypy-boto3-autoscaling-plans (>=1.42.0,<1.43.0)", "mypy-boto3-b2bi (>=1.42.0,<1.43.0)", "mypy-boto3-backup (>=1.42.0,<1.43.0)", "mypy-boto3-backup-gateway (>=1.42.0,<1.43.0)", "mypy-boto3-backupsearch (>=1.42.0,<1.43.0)", "mypy-boto3-batch (>=1.42.0,<1.43.0)", "mypy-boto3-bcm-dashboards (>=1.42.0,<1.43.0)", "mypy-boto3-bcm-data-exports (>=1.42.0,<1.43.0)", "mypy-boto3-bcm-pricing-calculator (>=1.42.0,<1.43.0)", "mypy-boto3-bcm-recommended-actions (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock-agent (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock-agent-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock-agentcore (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock-agentcore-control (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock-data-automation (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock-data-automation-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-bedrock-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-billing (>=1.42.0,<1.43.0)", "mypy-boto3-billingconductor (>=1.42.0,<1.43.0)", "mypy-boto3-braket (>=1.42.0,<1.43.0)", "mypy-boto3-budgets (>=1.42.0,<1.43.0)", "mypy-boto3-ce (>=1.42.0,<1.43.0)", "mypy-boto3-chatbot (>=1.42.0,<1.43.0)", "mypy-boto3-chime (>=1.42.0,<1.43.0)", "mypy-boto3-chime-sdk-identity (>=1.42.0,<1.43.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.42.0,<1.43.0)", "mypy-boto3-chime-sdk-meetings (>=1.42.0,<1.43.0)", "mypy-boto3-chime-sdk-messaging (>=1.42.0,<1.43.0)", "mypy-boto3-chime-sdk-voice (>=1.42.0,<1.43.0)", "mypy-boto3-cleanrooms (>=1.42.0,<1.43.0)", "mypy-boto3-cleanroomsml (>=1.42.0,<1.43.0)", "mypy-boto3-cloud9 (>=1.42.0,<1.43.0)", "mypy-boto3-cloudcontrol (>=1.42.0,<1.43.0)", "mypy-boto3-clouddirectory (>=1.42.0,<1.43.0)", "mypy-boto3-cloudformation (>=1.42.0,<1.43.0)", "mypy-boto3-cloudfront (>=1.42.0,<1.43.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.42.0,<1.43.0)", "mypy-boto3-cloudhsm (>=1.42.0,<1.43.0)", "mypy-boto3-cloudhsmv2 (>=1.42.0,<1.43.0)", "mypy-boto3-cloudsearch (>=1.42.0,<1.43.0)", "mypy-boto3-cloudsearchdomain (>=1.42.0,<1.43.0)", "mypy-boto3-cloudtrail (>=1.42.0,<1.43.0)", "mypy-boto3-cloudtrail-data (>=1.42.0,<1.43.0)", "mypy-boto3-cloudwatch (>=1.42.0,<1.43.0)", "mypy-boto3-codeartifact (>=1.42.0,<1.43.0)", "mypy-boto3-codebuild (>=1.42.0,<1.43.0)", "mypy-boto3-codecatalyst (>=1.42.0,<1.43.0)", "mypy-boto3-codecommit (>=1.42.0,<1.43.0)", "mypy-boto3-codeconnections (>=1.42.0,<1.43.0)", "mypy-boto3-codedeploy (>=1.42.0,<1.43.0)", "mypy-boto3-codeguru-reviewer (>=1.42.0,<1.43.0)", "mypy-boto3-codeguru-security (>=1.42.0,<1.43.0)", "mypy-boto3-codeguruprofiler (>=1.42.0,<1.43.0)", "mypy-boto3-codepipeline (>=1.42.0,<1.43.0)", "mypy-boto3-codestar-connections (>=1.42.0,<1.43.0)", "mypy-boto3-codestar-notifications (>=1.42.0,<1.43.0)", "mypy-boto3-cognito-identity (>=1.42.0,<1.43.0)", "mypy-boto3-cognito-idp (>=1.42.0,<1.43.0)", "mypy-boto3-cognito-sync (>=1.42.0,<1.43.0)", "mypy-boto3-comprehend (>=1.42.0,<1.43.0)", "mypy-boto3-comprehendmedical (>=1.42.0,<1.43.0)", "mypy-boto3-compute-optimizer (>=1.42.0,<1.43.0)", "mypy-boto3-compute-optimizer-automation (>=1.42.0,<1.43.0)", "mypy-boto3-config (>=1.42.0,<1.43.0)", "mypy-boto3-connect (>=1.42.0,<1.43.0)", "mypy-boto3-connect-contact-lens (>=1.42.0,<1.43.0)", "mypy-boto3-connectcampaigns (>=1.42.0,<1.43.0)", "mypy-boto3-connectcampaignsv2 (>=1.42.0,<1.43.0)", "mypy-boto3-connectcases (>=1.42.0,<1.43.0)", "mypy-boto3-connectparticipant (>=1.42.0,<1.43.0)", "mypy-boto3-controlcatalog (>=1.42.0,<1.43.0)", "mypy-boto3-controltower (>=1.42.0,<1.43.0)", "mypy-boto3-cost-optimization-hub (>=1.42.0,<1.43.0)", "mypy-boto3-cur (>=1.42.0,<1.43.0)", "mypy-boto3-customer-profiles (>=1.42.0,<1.43.0)", "mypy-boto3-databrew (>=1.42.0,<1.43.0)", "mypy-boto3-dataexchange (>=1.42.0,<1.43.0)", "mypy-boto3-datapipeline (>=1.42.0,<1.43.0)", "mypy-boto3-datasync (>=1.42.0,<1.43.0)", "mypy-boto3-datazone (>=1.42.0,<1.43.0)", "mypy-boto3-dax (>=1.42.0,<1.43.0)", "mypy-boto3-deadline (>=1.42.0,<1.43.0)", "mypy-boto3-detective (>=1.42.0,<1.43.0)", "mypy-boto3-devicefarm (>=1.42.0,<1.43.0)", "mypy-boto3-devops-guru (>=1.42.0,<1.43.0)", "mypy-boto3-directconnect (>=1.42.0,<1.43.0)", "mypy-boto3-discovery (>=1.42.0,<1.43.0)", "mypy-boto3-dlm (>=1.42.0,<1.43.0)", "mypy-boto3-dms (>=1.42.0,<1.43.0)", "mypy-boto3-docdb (>=1.42.0,<1.43.0)", "mypy-boto3-docdb-elastic (>=1.42.0,<1.43.0)", "mypy-boto3-drs (>=1.42.0,<1.43.0)", "mypy-boto3-ds (>=1.42.0,<1.43.0)", "mypy-boto3-ds-data (>=1.42.0,<1.43.0)", "mypy-boto3-dsql (>=1.42.0,<1.43.0)", "mypy-boto3-dynamodb (>=1.42.0,<1.43.0)", "mypy-boto3-dynamodbstreams (>=1.42.0,<1.43.0)", "mypy-boto3-ebs (>=1.42.0,<1.43.0)", "mypy-boto3-ec2 (>=1.42.0,<1.43.0)", "mypy-boto3-ec2-instance-connect (>=1.42.0,<1.43.0)", "mypy-boto3-ecr (>=1.42.0,<1.43.0)", "mypy-boto3-ecr-public (>=1.42.0,<1.43.0)", "mypy-boto3-ecs (>=1.42.0,<1.43.0)", "mypy-boto3-efs (>=1.42.0,<1.43.0)", "mypy-boto3-eks (>=1.42.0,<1.43.0)", "mypy-boto3-eks-auth (>=1.42.0,<1.43.0)", "mypy-boto3-elasticache (>=1.42.0,<1.43.0)", "mypy-boto3-elasticbeanstalk (>=1.42.0,<1.43.0)", "mypy-boto3-elb (>=1.42.0,<1.43.0)", "mypy-boto3-elbv2 (>=1.42.0,<1.43.0)", "mypy-boto3-emr (>=1.42.0,<1.43.0)", "mypy-boto3-emr-containers (>=1.42.0,<1.43.0)", "mypy-boto3-emr-serverless (>=1.42.0,<1.43.0)", "mypy-boto3-entityresolution (>=1.42.0,<1.43.0)", "mypy-boto3-es (>=1.42.0,<1.43.0)", "mypy-boto3-events (>=1.42.0,<1.43.0)", "mypy-boto3-evidently (>=1.42.0,<1.43.0)", "mypy-boto3-evs (>=1.42.0,<1.43.0)", "mypy-boto3-finspace (>=1.42.0,<1.43.0)", "mypy-boto3-finspace-data (>=1.42.0,<1.43.0)", "mypy-boto3-firehose (>=1.42.0,<1.43.0)", "mypy-boto3-fis (>=1.42.0,<1.43.0)", "mypy-boto3-fms (>=1.42.0,<1.43.0)", "mypy-boto3-forecast (>=1.42.0,<1.43.0)", "mypy-boto3-forecastquery (>=1.42.0,<1.43.0)", "mypy-boto3-frauddetector (>=1.42.0,<1.43.0)", "mypy-boto3-freetier (>=1.42.0,<1.43.0)", "mypy-boto3-fsx (>=1.42.0,<1.43.0)", "mypy-boto3-gamelift (>=1.42.0,<1.43.0)", "mypy-boto3-gameliftstreams (>=1.42.0,<1.43.0)", "mypy-boto3-geo-maps (>=1.42.0,<1.43.0)", "mypy-boto3-geo-places (>=1.42.0,<1.43.0)", "mypy-boto3-geo-routes (>=1.42.0,<1.43.0)", "mypy-boto3-glacier (>=1.42.0,<1.43.0)", "mypy-boto3-globalaccelerator (>=1.42.0,<1.43.0)", "mypy-boto3-glue (>=1.42.0,<1.43.0)", "mypy-boto3-grafana (>=1.42.0,<1.43.0)", "mypy-boto3-greengrass (>=1.42.0,<1.43.0)", "mypy-boto3-greengrassv2 (>=1.42.0,<1.43.0)", "mypy-boto3-groundstation (>=1.42.0,<1.43.0)", "mypy-boto3-guardduty (>=1.42.0,<1.43.0)", "mypy-boto3-health (>=1.42.0,<1.43.0)", "mypy-boto3-healthlake (>=1.42.0,<1.43.0)", "mypy-boto3-iam (>=1.42.0,<1.43.0)", "mypy-boto3-identitystore (>=1.42.0,<1.43.0)", "mypy-boto3-imagebuilder (>=1.42.0,<1.43.0)", "mypy-boto3-importexport (>=1.42.0,<1.43.0)", "mypy-boto3-inspector (>=1.42.0,<1.43.0)", "mypy-boto3-inspector-scan (>=1.42.0,<1.43.0)", "mypy-boto3-inspector2 (>=1.42.0,<1.43.0)", "mypy-boto3-internetmonitor (>=1.42.0,<1.43.0)", "mypy-boto3-invoicing (>=1.42.0,<1.43.0)", "mypy-boto3-iot (>=1.42.0,<1.43.0)", "mypy-boto3-iot-data (>=1.42.0,<1.43.0)", "mypy-boto3-iot-jobs-data (>=1.42.0,<1.43.0)", "mypy-boto3-iot-managed-integrations (>=1.42.0,<1.43.0)", "mypy-boto3-iotanalytics (>=1.42.0,<1.43.0)", "mypy-boto3-iotdeviceadvisor (>=1.42.0,<1.43.0)", "mypy-boto3-iotevents (>=1.42.0,<1.43.0)", "mypy-boto3-iotevents-data (>=1.42.0,<1.43.0)", "mypy-boto3-iotfleetwise (>=1.42.0,<1.43.0)", "mypy-boto3-iotsecuretunneling (>=1.42.0,<1.43.0)", "mypy-boto3-iotsitewise (>=1.42.0,<1.43.0)", "mypy-boto3-iotthingsgraph (>=1.42.0,<1.43.0)", "mypy-boto3-iottwinmaker (>=1.42.0,<1.43.0)", "mypy-boto3-iotwireless (>=1.42.0,<1.43.0)", "mypy-boto3-ivs (>=1.42.0,<1.43.0)", "mypy-boto3-ivs-realtime (>=1.42.0,<1.43.0)", "mypy-boto3-ivschat (>=1.42.0,<1.43.0)", "mypy-boto3-kafka (>=1.42.0,<1.43.0)", "mypy-boto3-kafkaconnect (>=1.42.0,<1.43.0)", "mypy-boto3-kendra (>=1.42.0,<1.43.0)", "mypy-boto3-kendra-ranking (>=1.42.0,<1.43.0)", "mypy-boto3-keyspaces (>=1.42.0,<1.43.0)", "mypy-boto3-keyspacesstreams (>=1.42.0,<1.43.0)", "mypy-boto3-kinesis (>=1.42.0,<1.43.0)", "mypy-boto3-kinesis-video-archived-media (>=1.42.0,<1.43.0)", "mypy-boto3-kinesis-video-media (>=1.42.0,<1.43.0)", "mypy-boto3-kinesis-video-signaling (>=1.42.0,<1.43.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.42.0,<1.43.0)", "mypy-boto3-kinesisanalytics (>=1.42.0,<1.43.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.42.0,<1.43.0)", "mypy-boto3-kinesisvideo (>=1.42.0,<1.43.0)", "mypy-boto3-kms (>=1.42.0,<1.43.0)", "mypy-boto3-lakeformation (>=1.42.0,<1.43.0)", "mypy-boto3-lambda (>=1.42.0,<1.43.0)", "mypy-boto3-launch-wizard (>=1.42.0,<1.43.0)", "mypy-boto3-lex-models (>=1.42.0,<1.43.0)", "mypy-boto3-lex-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-lexv2-models (>=1.42.0,<1.43.0)", "mypy-boto3-lexv2-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-license-manager (>=1.42.0,<1.43.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.42.0,<1.43.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.42.0,<1.43.0)", "mypy-boto3-lightsail (>=1.42.0,<1.43.0)", "mypy-boto3-location (>=1.42.0,<1.43.0)", "mypy-boto3-logs (>=1.42.0,<1.43.0)", "mypy-boto3-lookoutequipment (>=1.42.0,<1.43.0)", "mypy-boto3-m2 (>=1.42.0,<1.43.0)", "mypy-boto3-machinelearning (>=1.42.0,<1.43.0)", "mypy-boto3-macie2 (>=1.42.0,<1.43.0)", "mypy-boto3-mailmanager (>=1.42.0,<1.43.0)", "mypy-boto3-managedblockchain (>=1.42.0,<1.43.0)", "mypy-boto3-managedblockchain-query (>=1.42.0,<1.43.0)", "mypy-boto3-marketplace-agreement (>=1.42.0,<1.43.0)", "mypy-boto3-marketplace-catalog (>=1.42.0,<1.43.0)", "mypy-boto3-marketplace-deployment (>=1.42.0,<1.43.0)", "mypy-boto3-marketplace-entitlement (>=1.42.0,<1.43.0)", "mypy-boto3-marketplace-reporting (>=1.42.0,<1.43.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.42.0,<1.43.0)", "mypy-boto3-mediaconnect (>=1.42.0,<1.43.0)", "mypy-boto3-mediaconvert (>=1.42.0,<1.43.0)", "mypy-boto3-medialive (>=1.42.0,<1.43.0)", "mypy-boto3-mediapackage (>=1.42.0,<1.43.0)", "mypy-boto3-mediapackage-vod (>=1.42.0,<1.43.0)", "mypy-boto3-mediapackagev2 (>=1.42.0,<1.43.0)", "mypy-boto3-mediastore (>=1.42.0,<1.43.0)", "mypy-boto3-mediastore-data (>=1.42.0,<1.43.0)", "mypy-boto3-mediatailor (>=1.42.0,<1.43.0)", "mypy-boto3-medical-imaging (>=1.42.0,<1.43.0)", "mypy-boto3-memorydb (>=1.42.0,<1.43.0)", "mypy-boto3-meteringmarketplace (>=1.42.0,<1.43.0)", "mypy-boto3-mgh (>=1.42.0,<1.43.0)", "mypy-boto3-mgn (>=1.42.0,<1.43.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.42.0,<1.43.0)", "mypy-boto3-migrationhub-config (>=1.42.0,<1.43.0)", "mypy-boto3-migrationhuborchestrator (>=1.42.0,<1.43.0)", "mypy-boto3-migrationhubstrategy (>=1.42.0,<1.43.0)", "mypy-boto3-mpa (>=1.42.0,<1.43.0)", "mypy-boto3-mq (>=1.42.0,<1.43.0)", "mypy-boto3-mturk (>=1.42.0,<1.43.0)", "mypy-boto3-mwaa (>=1.42.0,<1.43.0)", "mypy-boto3-mwaa-serverless (>=1.42.0,<1.43.0)", "mypy-boto3-neptune (>=1.42.0,<1.43.0)", "mypy-boto3-neptune-graph (>=1.42.0,<1.43.0)", "mypy-boto3-neptunedata (>=1.42.0,<1.43.0)", "mypy-boto3-network-firewall (>=1.42.0,<1.43.0)", "mypy-boto3-networkflowmonitor (>=1.42.0,<1.43.0)", "mypy-boto3-networkmanager (>=1.42.0,<1.43.0)", "mypy-boto3-networkmonitor (>=1.42.0,<1.43.0)", "mypy-boto3-notifications (>=1.42.0,<1.43.0)", "mypy-boto3-notificationscontacts (>=1.42.0,<1.43.0)", "mypy-boto3-nova-act (>=1.42.0,<1.43.0)", "mypy-boto3-oam (>=1.42.0,<1.43.0)", "mypy-boto3-observabilityadmin (>=1.42.0,<1.43.0)", "mypy-boto3-odb (>=1.42.0,<1.43.0)", "mypy-boto3-omics (>=1.42.0,<1.43.0)", "mypy-boto3-opensearch (>=1.42.0,<1.43.0)", "mypy-boto3-opensearchserverless (>=1.42.0,<1.43.0)", "mypy-boto3-organizations (>=1.42.0,<1.43.0)", "mypy-boto3-osis (>=1.42.0,<1.43.0)", "mypy-boto3-outposts (>=1.42.0,<1.43.0)", "mypy-boto3-panorama (>=1.42.0,<1.43.0)", "mypy-boto3-partnercentral-account (>=1.42.0,<1.43.0)", "mypy-boto3-partnercentral-benefits (>=1.42.0,<1.43.0)", "mypy-boto3-partnercentral-channel (>=1.42.0,<1.43.0)", "mypy-boto3-partnercentral-selling (>=1.42.0,<1.43.0)", "mypy-boto3-payment-cryptography (>=1.42.0,<1.43.0)", "mypy-boto3-payment-cryptography-data (>=1.42.0,<1.43.0)", "mypy-boto3-pca-connector-ad (>=1.42.0,<1.43.0)", "mypy-boto3-pca-connector-scep (>=1.42.0,<1.43.0)", "mypy-boto3-pcs (>=1.42.0,<1.43.0)", "mypy-boto3-personalize (>=1.42.0,<1.43.0)", "mypy-boto3-personalize-events (>=1.42.0,<1.43.0)", "mypy-boto3-personalize-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-pi (>=1.42.0,<1.43.0)", "mypy-boto3-pinpoint (>=1.42.0,<1.43.0)", "mypy-boto3-pinpoint-email (>=1.42.0,<1.43.0)", "mypy-boto3-pinpoint-sms-voice (>=1.42.0,<1.43.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.42.0,<1.43.0)", "mypy-boto3-pipes (>=1.42.0,<1.43.0)", "mypy-boto3-polly (>=1.42.0,<1.43.0)", "mypy-boto3-pricing (>=1.42.0,<1.43.0)", "mypy-boto3-proton (>=1.42.0,<1.43.0)", "mypy-boto3-qapps (>=1.42.0,<1.43.0)", "mypy-boto3-qbusiness (>=1.42.0,<1.43.0)", "mypy-boto3-qconnect (>=1.42.0,<1.43.0)", "mypy-boto3-quicksight (>=1.42.0,<1.43.0)", "mypy-boto3-ram (>=1.42.0,<1.43.0)", "mypy-boto3-rbin (>=1.42.0,<1.43.0)", "mypy-boto3-rds (>=1.42.0,<1.43.0)", "mypy-boto3-rds-data (>=1.42.0,<1.43.0)", "mypy-boto3-redshift (>=1.42.0,<1.43.0)", "mypy-boto3-redshift-data (>=1.42.0,<1.43.0)", "mypy-boto3-redshift-serverless (>=1.42.0,<1.43.0)", "mypy-boto3-rekognition (>=1.42.0,<1.43.0)", "mypy-boto3-repostspace (>=1.42.0,<1.43.0)", "mypy-boto3-resiliencehub (>=1.42.0,<1.43.0)", "mypy-boto3-resource-explorer-2 (>=1.42.0,<1.43.0)", "mypy-boto3-resource-groups (>=1.42.0,<1.43.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.42.0,<1.43.0)", "mypy-boto3-rolesanywhere (>=1.42.0,<1.43.0)", "mypy-boto3-route53 (>=1.42.0,<1.43.0)", "mypy-boto3-route53-recovery-cluster (>=1.42.0,<1.43.0)", "mypy-boto3-route53-recovery-control-config (>=1.42.0,<1.43.0)", "mypy-boto3-route53-recovery-readiness (>=1.42.0,<1.43.0)", "mypy-boto3-route53domains (>=1.42.0,<1.43.0)", "mypy-boto3-route53globalresolver (>=1.42.0,<1.43.0)", "mypy-boto3-route53profiles (>=1.42.0,<1.43.0)", "mypy-boto3-route53resolver (>=1.42.0,<1.43.0)", "mypy-boto3-rtbfabric (>=1.42.0,<1.43.0)", "mypy-boto3-rum (>=1.42.0,<1.43.0)", "mypy-boto3-s3 (>=1.42.0,<1.43.0)", "mypy-boto3-s3control (>=1.42.0,<1.43.0)", "mypy-boto3-s3outposts (>=1.42.0,<1.43.0)", "mypy-boto3-s3tables (>=1.42.0,<1.43.0)", "mypy-boto3-s3vectors (>=1.42.0,<1.43.0)", "mypy-boto3-sagemaker (>=1.42.0,<1.43.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-sagemaker-edge (>=1.42.0,<1.43.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-sagemaker-geospatial (>=1.42.0,<1.43.0)", "mypy-boto3-sagemaker-metrics (>=1.42.0,<1.43.0)", "mypy-boto3-sagemaker-runtime (>=1.42.0,<1.43.0)", "mypy-boto3-savingsplans (>=1.42.0,<1.43.0)", "mypy-boto3-scheduler (>=1.42.0,<1.43.0)", "mypy-boto3-schemas (>=1.42.0,<1.43.0)", "mypy-boto3-sdb (>=1.42.0,<1.43.0)", "mypy-boto3-secretsmanager (>=1.42.0,<1.43.0)", "mypy-boto3-security-ir (>=1.42.0,<1.43.0)", "mypy-boto3-securityhub (>=1.42.0,<1.43.0)", "mypy-boto3-securitylake (>=1.42.0,<1.43.0)", "mypy-boto3-serverlessrepo (>=1.42.0,<1.43.0)", "mypy-boto3-service-quotas (>=1.42.0,<1.43.0)", "mypy-boto3-servicecatalog (>=1.42.0,<1.43.0)", "mypy-boto3-servicecatalog-appregistry (>=1.42.0,<1.43.0)", "mypy-boto3-servicediscovery (>=1.42.0,<1.43.0)", "mypy-boto3-ses (>=1.42.0,<1.43.0)", "mypy-boto3-sesv2 (>=1.42.0,<1.43.0)", "mypy-boto3-shield (>=1.42.0,<1.43.0)", "mypy-boto3-signer (>=1.42.0,<1.43.0)", "mypy-boto3-signin (>=1.42.0,<1.43.0)", "mypy-boto3-simspaceweaver (>=1.42.0,<1.43.0)", "mypy-boto3-snow-device-management (>=1.42.0,<1.43.0)", "mypy-boto3-snowball (>=1.42.0,<1.43.0)", "mypy-boto3-sns (>=1.42.0,<1.43.0)", "mypy-boto3-socialmessaging (>=1.42.0,<1.43.0)", "mypy-boto3-sqs (>=1.42.0,<1.43.0)", "mypy-boto3-ssm (>=1.42.0,<1.43.0)", "mypy-boto3-ssm-contacts (>=1.42.0,<1.43.0)", "mypy-boto3-ssm-guiconnect (>=1.42.0,<1.43.0)", "mypy-boto3-ssm-incidents (>=1.42.0,<1.43.0)", "mypy-boto3-ssm-quicksetup (>=1.42.0,<1.43.0)", "mypy-boto3-ssm-sap (>=1.42.0,<1.43.0)", "mypy-boto3-sso (>=1.42.0,<1.43.0)", "mypy-boto3-sso-admin (>=1.42.0,<1.43.0)", "mypy-boto3-sso-oidc (>=1.42.0,<1.43.0)", "mypy-boto3-stepfunctions (>=1.42.0,<1.43.0)", "mypy-boto3-storagegateway (>=1.42.0,<1.43.0)", "mypy-boto3-sts (>=1.42.0,<1.43.0)", "mypy-boto3-supplychain (>=1.42.0,<1.43.0)", "mypy-boto3-support (>=1.42.0,<1.43.0)", "mypy-boto3-support-app (>=1.42.0,<1.43.0)", "mypy-boto3-swf (>=1.42.0,<1.43.0)", "mypy-boto3-synthetics (>=1.42.0,<1.43.0)", "mypy-boto3-taxsettings (>=1.42.0,<1.43.0)", "mypy-boto3-textract (>=1.42.0,<1.43.0)", "mypy-boto3-timestream-influxdb (>=1.42.0,<1.43.0)", "mypy-boto3-timestream-query (>=1.42.0,<1.43.0)", "mypy-boto3-timestream-write (>=1.42.0,<1.43.0)", "mypy-boto3-tnb (>=1.42.0,<1.43.0)", "mypy-boto3-transcribe (>=1.42.0,<1.43.0)", "mypy-boto3-transfer (>=1.42.0,<1.43.0)", "mypy-boto3-translate (>=1.42.0,<1.43.0)", "mypy-boto3-trustedadvisor (>=1.42.0,<1.43.0)", "mypy-boto3-verifiedpermissions (>=1.42.0,<1.43.0)", "mypy-boto3-voice-id (>=1.42.0,<1.43.0)", "mypy-boto3-vpc-lattice (>=1.42.0,<1.43.0)", "mypy-boto3-waf (>=1.42.0,<1.43.0)", "mypy-boto3-waf-regional (>=1.42.0,<1.43.0)", "mypy-boto3-wafv2 (>=1.42.0,<1.43.0)", "mypy-boto3-wellarchitected (>=1.42.0,<1.43.0)", "mypy-boto3-wickr (>=1.42.0,<1.43.0)", "mypy-boto3-wisdom (>=1.42.0,<1.43.0)", "mypy-boto3-workdocs (>=1.42.0,<1.43.0)", "mypy-boto3-workmail (>=1.42.0,<1.43.0)", "mypy-boto3-workmailmessageflow (>=1.42.0,<1.43.0)", "mypy-boto3-workspaces (>=1.42.0,<1.43.0)", "mypy-boto3-workspaces-instances (>=1.42.0,<1.43.0)", "mypy-boto3-workspaces-thin-client (>=1.42.0,<1.43.0)", "mypy-boto3-workspaces-web (>=1.42.0,<1.43.0)", "mypy-boto3-xray (>=1.42.0,<1.43.0)"] +amp = ["mypy-boto3-amp (>=1.42.0,<1.43.0)"] +amplify = ["mypy-boto3-amplify (>=1.42.0,<1.43.0)"] +amplifybackend = ["mypy-boto3-amplifybackend (>=1.42.0,<1.43.0)"] +amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.42.0,<1.43.0)"] +apigateway = ["mypy-boto3-apigateway (>=1.42.0,<1.43.0)"] +apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.42.0,<1.43.0)"] +apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.42.0,<1.43.0)"] +appconfig = ["mypy-boto3-appconfig (>=1.42.0,<1.43.0)"] +appconfigdata = ["mypy-boto3-appconfigdata (>=1.42.0,<1.43.0)"] +appfabric = ["mypy-boto3-appfabric (>=1.42.0,<1.43.0)"] +appflow = ["mypy-boto3-appflow (>=1.42.0,<1.43.0)"] +appintegrations = ["mypy-boto3-appintegrations (>=1.42.0,<1.43.0)"] +application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.42.0,<1.43.0)"] +application-insights = ["mypy-boto3-application-insights (>=1.42.0,<1.43.0)"] +application-signals = ["mypy-boto3-application-signals (>=1.42.0,<1.43.0)"] +applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.42.0,<1.43.0)"] +appmesh = ["mypy-boto3-appmesh (>=1.42.0,<1.43.0)"] +apprunner = ["mypy-boto3-apprunner (>=1.42.0,<1.43.0)"] +appstream = ["mypy-boto3-appstream (>=1.42.0,<1.43.0)"] +appsync = ["mypy-boto3-appsync (>=1.42.0,<1.43.0)"] +arc-region-switch = ["mypy-boto3-arc-region-switch (>=1.42.0,<1.43.0)"] +arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.42.0,<1.43.0)"] +artifact = ["mypy-boto3-artifact (>=1.42.0,<1.43.0)"] +athena = ["mypy-boto3-athena (>=1.42.0,<1.43.0)"] +auditmanager = ["mypy-boto3-auditmanager (>=1.42.0,<1.43.0)"] +autoscaling = ["mypy-boto3-autoscaling (>=1.42.0,<1.43.0)"] +autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.42.0,<1.43.0)"] +b2bi = ["mypy-boto3-b2bi (>=1.42.0,<1.43.0)"] +backup = ["mypy-boto3-backup (>=1.42.0,<1.43.0)"] +backup-gateway = ["mypy-boto3-backup-gateway (>=1.42.0,<1.43.0)"] +backupsearch = ["mypy-boto3-backupsearch (>=1.42.0,<1.43.0)"] +batch = ["mypy-boto3-batch (>=1.42.0,<1.43.0)"] +bcm-dashboards = ["mypy-boto3-bcm-dashboards (>=1.42.0,<1.43.0)"] +bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.42.0,<1.43.0)"] +bcm-pricing-calculator = ["mypy-boto3-bcm-pricing-calculator (>=1.42.0,<1.43.0)"] +bcm-recommended-actions = ["mypy-boto3-bcm-recommended-actions (>=1.42.0,<1.43.0)"] +bedrock = ["mypy-boto3-bedrock (>=1.42.0,<1.43.0)"] +bedrock-agent = ["mypy-boto3-bedrock-agent (>=1.42.0,<1.43.0)"] +bedrock-agent-runtime = ["mypy-boto3-bedrock-agent-runtime (>=1.42.0,<1.43.0)"] +bedrock-agentcore = ["mypy-boto3-bedrock-agentcore (>=1.42.0,<1.43.0)"] +bedrock-agentcore-control = ["mypy-boto3-bedrock-agentcore-control (>=1.42.0,<1.43.0)"] +bedrock-data-automation = ["mypy-boto3-bedrock-data-automation (>=1.42.0,<1.43.0)"] +bedrock-data-automation-runtime = ["mypy-boto3-bedrock-data-automation-runtime (>=1.42.0,<1.43.0)"] +bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.42.0,<1.43.0)"] +billing = ["mypy-boto3-billing (>=1.42.0,<1.43.0)"] +billingconductor = ["mypy-boto3-billingconductor (>=1.42.0,<1.43.0)"] +boto3 = ["boto3 (==1.42.25)"] +braket = ["mypy-boto3-braket (>=1.42.0,<1.43.0)"] +budgets = ["mypy-boto3-budgets (>=1.42.0,<1.43.0)"] +ce = ["mypy-boto3-ce (>=1.42.0,<1.43.0)"] +chatbot = ["mypy-boto3-chatbot (>=1.42.0,<1.43.0)"] +chime = ["mypy-boto3-chime (>=1.42.0,<1.43.0)"] +chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.42.0,<1.43.0)"] +chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.42.0,<1.43.0)"] +chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.42.0,<1.43.0)"] +chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.42.0,<1.43.0)"] +chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.42.0,<1.43.0)"] +cleanrooms = ["mypy-boto3-cleanrooms (>=1.42.0,<1.43.0)"] +cleanroomsml = ["mypy-boto3-cleanroomsml (>=1.42.0,<1.43.0)"] +cloud9 = ["mypy-boto3-cloud9 (>=1.42.0,<1.43.0)"] +cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.42.0,<1.43.0)"] +clouddirectory = ["mypy-boto3-clouddirectory (>=1.42.0,<1.43.0)"] +cloudformation = ["mypy-boto3-cloudformation (>=1.42.0,<1.43.0)"] +cloudfront = ["mypy-boto3-cloudfront (>=1.42.0,<1.43.0)"] +cloudfront-keyvaluestore = ["mypy-boto3-cloudfront-keyvaluestore (>=1.42.0,<1.43.0)"] +cloudhsm = ["mypy-boto3-cloudhsm (>=1.42.0,<1.43.0)"] +cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.42.0,<1.43.0)"] +cloudsearch = ["mypy-boto3-cloudsearch (>=1.42.0,<1.43.0)"] +cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.42.0,<1.43.0)"] +cloudtrail = ["mypy-boto3-cloudtrail (>=1.42.0,<1.43.0)"] +cloudtrail-data = ["mypy-boto3-cloudtrail-data (>=1.42.0,<1.43.0)"] +cloudwatch = ["mypy-boto3-cloudwatch (>=1.42.0,<1.43.0)"] +codeartifact = ["mypy-boto3-codeartifact (>=1.42.0,<1.43.0)"] +codebuild = ["mypy-boto3-codebuild (>=1.42.0,<1.43.0)"] +codecatalyst = ["mypy-boto3-codecatalyst (>=1.42.0,<1.43.0)"] +codecommit = ["mypy-boto3-codecommit (>=1.42.0,<1.43.0)"] +codeconnections = ["mypy-boto3-codeconnections (>=1.42.0,<1.43.0)"] +codedeploy = ["mypy-boto3-codedeploy (>=1.42.0,<1.43.0)"] +codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.42.0,<1.43.0)"] +codeguru-security = ["mypy-boto3-codeguru-security (>=1.42.0,<1.43.0)"] +codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.42.0,<1.43.0)"] +codepipeline = ["mypy-boto3-codepipeline (>=1.42.0,<1.43.0)"] +codestar-connections = ["mypy-boto3-codestar-connections (>=1.42.0,<1.43.0)"] +codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.42.0,<1.43.0)"] +cognito-identity = ["mypy-boto3-cognito-identity (>=1.42.0,<1.43.0)"] +cognito-idp = ["mypy-boto3-cognito-idp (>=1.42.0,<1.43.0)"] +cognito-sync = ["mypy-boto3-cognito-sync (>=1.42.0,<1.43.0)"] +comprehend = ["mypy-boto3-comprehend (>=1.42.0,<1.43.0)"] +comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.42.0,<1.43.0)"] +compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.42.0,<1.43.0)"] +compute-optimizer-automation = ["mypy-boto3-compute-optimizer-automation (>=1.42.0,<1.43.0)"] +config = ["mypy-boto3-config (>=1.42.0,<1.43.0)"] +connect = ["mypy-boto3-connect (>=1.42.0,<1.43.0)"] +connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.42.0,<1.43.0)"] +connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.42.0,<1.43.0)"] +connectcampaignsv2 = ["mypy-boto3-connectcampaignsv2 (>=1.42.0,<1.43.0)"] +connectcases = ["mypy-boto3-connectcases (>=1.42.0,<1.43.0)"] +connectparticipant = ["mypy-boto3-connectparticipant (>=1.42.0,<1.43.0)"] +controlcatalog = ["mypy-boto3-controlcatalog (>=1.42.0,<1.43.0)"] +controltower = ["mypy-boto3-controltower (>=1.42.0,<1.43.0)"] +cost-optimization-hub = ["mypy-boto3-cost-optimization-hub (>=1.42.0,<1.43.0)"] +cur = ["mypy-boto3-cur (>=1.42.0,<1.43.0)"] +customer-profiles = ["mypy-boto3-customer-profiles (>=1.42.0,<1.43.0)"] +databrew = ["mypy-boto3-databrew (>=1.42.0,<1.43.0)"] +dataexchange = ["mypy-boto3-dataexchange (>=1.42.0,<1.43.0)"] +datapipeline = ["mypy-boto3-datapipeline (>=1.42.0,<1.43.0)"] +datasync = ["mypy-boto3-datasync (>=1.42.0,<1.43.0)"] +datazone = ["mypy-boto3-datazone (>=1.42.0,<1.43.0)"] +dax = ["mypy-boto3-dax (>=1.42.0,<1.43.0)"] +deadline = ["mypy-boto3-deadline (>=1.42.0,<1.43.0)"] +detective = ["mypy-boto3-detective (>=1.42.0,<1.43.0)"] +devicefarm = ["mypy-boto3-devicefarm (>=1.42.0,<1.43.0)"] +devops-guru = ["mypy-boto3-devops-guru (>=1.42.0,<1.43.0)"] +directconnect = ["mypy-boto3-directconnect (>=1.42.0,<1.43.0)"] +discovery = ["mypy-boto3-discovery (>=1.42.0,<1.43.0)"] +dlm = ["mypy-boto3-dlm (>=1.42.0,<1.43.0)"] +dms = ["mypy-boto3-dms (>=1.42.0,<1.43.0)"] +docdb = ["mypy-boto3-docdb (>=1.42.0,<1.43.0)"] +docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.42.0,<1.43.0)"] +drs = ["mypy-boto3-drs (>=1.42.0,<1.43.0)"] +ds = ["mypy-boto3-ds (>=1.42.0,<1.43.0)"] +ds-data = ["mypy-boto3-ds-data (>=1.42.0,<1.43.0)"] +dsql = ["mypy-boto3-dsql (>=1.42.0,<1.43.0)"] +dynamodb = ["mypy-boto3-dynamodb (>=1.42.0,<1.43.0)"] +dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.42.0,<1.43.0)"] +ebs = ["mypy-boto3-ebs (>=1.42.0,<1.43.0)"] +ec2 = ["mypy-boto3-ec2 (>=1.42.0,<1.43.0)"] +ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.42.0,<1.43.0)"] +ecr = ["mypy-boto3-ecr (>=1.42.0,<1.43.0)"] +ecr-public = ["mypy-boto3-ecr-public (>=1.42.0,<1.43.0)"] +ecs = ["mypy-boto3-ecs (>=1.42.0,<1.43.0)"] +efs = ["mypy-boto3-efs (>=1.42.0,<1.43.0)"] +eks = ["mypy-boto3-eks (>=1.42.0,<1.43.0)"] +eks-auth = ["mypy-boto3-eks-auth (>=1.42.0,<1.43.0)"] +elasticache = ["mypy-boto3-elasticache (>=1.42.0,<1.43.0)"] +elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.42.0,<1.43.0)"] +elb = ["mypy-boto3-elb (>=1.42.0,<1.43.0)"] +elbv2 = ["mypy-boto3-elbv2 (>=1.42.0,<1.43.0)"] +emr = ["mypy-boto3-emr (>=1.42.0,<1.43.0)"] +emr-containers = ["mypy-boto3-emr-containers (>=1.42.0,<1.43.0)"] +emr-serverless = ["mypy-boto3-emr-serverless (>=1.42.0,<1.43.0)"] +entityresolution = ["mypy-boto3-entityresolution (>=1.42.0,<1.43.0)"] +es = ["mypy-boto3-es (>=1.42.0,<1.43.0)"] +essential = ["mypy-boto3-cloudformation (>=1.42.0,<1.43.0)", "mypy-boto3-dynamodb (>=1.42.0,<1.43.0)", "mypy-boto3-ec2 (>=1.42.0,<1.43.0)", "mypy-boto3-lambda (>=1.42.0,<1.43.0)", "mypy-boto3-rds (>=1.42.0,<1.43.0)", "mypy-boto3-s3 (>=1.42.0,<1.43.0)", "mypy-boto3-sqs (>=1.42.0,<1.43.0)"] +events = ["mypy-boto3-events (>=1.42.0,<1.43.0)"] +evidently = ["mypy-boto3-evidently (>=1.42.0,<1.43.0)"] +evs = ["mypy-boto3-evs (>=1.42.0,<1.43.0)"] +finspace = ["mypy-boto3-finspace (>=1.42.0,<1.43.0)"] +finspace-data = ["mypy-boto3-finspace-data (>=1.42.0,<1.43.0)"] +firehose = ["mypy-boto3-firehose (>=1.42.0,<1.43.0)"] +fis = ["mypy-boto3-fis (>=1.42.0,<1.43.0)"] +fms = ["mypy-boto3-fms (>=1.42.0,<1.43.0)"] +forecast = ["mypy-boto3-forecast (>=1.42.0,<1.43.0)"] +forecastquery = ["mypy-boto3-forecastquery (>=1.42.0,<1.43.0)"] +frauddetector = ["mypy-boto3-frauddetector (>=1.42.0,<1.43.0)"] +freetier = ["mypy-boto3-freetier (>=1.42.0,<1.43.0)"] +fsx = ["mypy-boto3-fsx (>=1.42.0,<1.43.0)"] +full = ["boto3-stubs-full (>=1.42.0,<1.43.0)"] +gamelift = ["mypy-boto3-gamelift (>=1.42.0,<1.43.0)"] +gameliftstreams = ["mypy-boto3-gameliftstreams (>=1.42.0,<1.43.0)"] +geo-maps = ["mypy-boto3-geo-maps (>=1.42.0,<1.43.0)"] +geo-places = ["mypy-boto3-geo-places (>=1.42.0,<1.43.0)"] +geo-routes = ["mypy-boto3-geo-routes (>=1.42.0,<1.43.0)"] +glacier = ["mypy-boto3-glacier (>=1.42.0,<1.43.0)"] +globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.42.0,<1.43.0)"] +glue = ["mypy-boto3-glue (>=1.42.0,<1.43.0)"] +grafana = ["mypy-boto3-grafana (>=1.42.0,<1.43.0)"] +greengrass = ["mypy-boto3-greengrass (>=1.42.0,<1.43.0)"] +greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.42.0,<1.43.0)"] +groundstation = ["mypy-boto3-groundstation (>=1.42.0,<1.43.0)"] +guardduty = ["mypy-boto3-guardduty (>=1.42.0,<1.43.0)"] +health = ["mypy-boto3-health (>=1.42.0,<1.43.0)"] +healthlake = ["mypy-boto3-healthlake (>=1.42.0,<1.43.0)"] +iam = ["mypy-boto3-iam (>=1.42.0,<1.43.0)"] +identitystore = ["mypy-boto3-identitystore (>=1.42.0,<1.43.0)"] +imagebuilder = ["mypy-boto3-imagebuilder (>=1.42.0,<1.43.0)"] +importexport = ["mypy-boto3-importexport (>=1.42.0,<1.43.0)"] +inspector = ["mypy-boto3-inspector (>=1.42.0,<1.43.0)"] +inspector-scan = ["mypy-boto3-inspector-scan (>=1.42.0,<1.43.0)"] +inspector2 = ["mypy-boto3-inspector2 (>=1.42.0,<1.43.0)"] +internetmonitor = ["mypy-boto3-internetmonitor (>=1.42.0,<1.43.0)"] +invoicing = ["mypy-boto3-invoicing (>=1.42.0,<1.43.0)"] +iot = ["mypy-boto3-iot (>=1.42.0,<1.43.0)"] +iot-data = ["mypy-boto3-iot-data (>=1.42.0,<1.43.0)"] +iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.42.0,<1.43.0)"] +iot-managed-integrations = ["mypy-boto3-iot-managed-integrations (>=1.42.0,<1.43.0)"] +iotanalytics = ["mypy-boto3-iotanalytics (>=1.42.0,<1.43.0)"] +iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.42.0,<1.43.0)"] +iotevents = ["mypy-boto3-iotevents (>=1.42.0,<1.43.0)"] +iotevents-data = ["mypy-boto3-iotevents-data (>=1.42.0,<1.43.0)"] +iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.42.0,<1.43.0)"] +iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.42.0,<1.43.0)"] +iotsitewise = ["mypy-boto3-iotsitewise (>=1.42.0,<1.43.0)"] +iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.42.0,<1.43.0)"] +iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.42.0,<1.43.0)"] +iotwireless = ["mypy-boto3-iotwireless (>=1.42.0,<1.43.0)"] +ivs = ["mypy-boto3-ivs (>=1.42.0,<1.43.0)"] +ivs-realtime = ["mypy-boto3-ivs-realtime (>=1.42.0,<1.43.0)"] +ivschat = ["mypy-boto3-ivschat (>=1.42.0,<1.43.0)"] +kafka = ["mypy-boto3-kafka (>=1.42.0,<1.43.0)"] +kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.42.0,<1.43.0)"] +kendra = ["mypy-boto3-kendra (>=1.42.0,<1.43.0)"] +kendra-ranking = ["mypy-boto3-kendra-ranking (>=1.42.0,<1.43.0)"] +keyspaces = ["mypy-boto3-keyspaces (>=1.42.0,<1.43.0)"] +keyspacesstreams = ["mypy-boto3-keyspacesstreams (>=1.42.0,<1.43.0)"] +kinesis = ["mypy-boto3-kinesis (>=1.42.0,<1.43.0)"] +kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.42.0,<1.43.0)"] +kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.42.0,<1.43.0)"] +kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.42.0,<1.43.0)"] +kinesis-video-webrtc-storage = ["mypy-boto3-kinesis-video-webrtc-storage (>=1.42.0,<1.43.0)"] +kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.42.0,<1.43.0)"] +kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.42.0,<1.43.0)"] +kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.42.0,<1.43.0)"] +kms = ["mypy-boto3-kms (>=1.42.0,<1.43.0)"] +lakeformation = ["mypy-boto3-lakeformation (>=1.42.0,<1.43.0)"] +lambda = ["mypy-boto3-lambda (>=1.42.0,<1.43.0)"] +launch-wizard = ["mypy-boto3-launch-wizard (>=1.42.0,<1.43.0)"] +lex-models = ["mypy-boto3-lex-models (>=1.42.0,<1.43.0)"] +lex-runtime = ["mypy-boto3-lex-runtime (>=1.42.0,<1.43.0)"] +lexv2-models = ["mypy-boto3-lexv2-models (>=1.42.0,<1.43.0)"] +lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.42.0,<1.43.0)"] +license-manager = ["mypy-boto3-license-manager (>=1.42.0,<1.43.0)"] +license-manager-linux-subscriptions = ["mypy-boto3-license-manager-linux-subscriptions (>=1.42.0,<1.43.0)"] +license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.42.0,<1.43.0)"] +lightsail = ["mypy-boto3-lightsail (>=1.42.0,<1.43.0)"] +location = ["mypy-boto3-location (>=1.42.0,<1.43.0)"] +logs = ["mypy-boto3-logs (>=1.42.0,<1.43.0)"] +lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.42.0,<1.43.0)"] +m2 = ["mypy-boto3-m2 (>=1.42.0,<1.43.0)"] +machinelearning = ["mypy-boto3-machinelearning (>=1.42.0,<1.43.0)"] +macie2 = ["mypy-boto3-macie2 (>=1.42.0,<1.43.0)"] +mailmanager = ["mypy-boto3-mailmanager (>=1.42.0,<1.43.0)"] +managedblockchain = ["mypy-boto3-managedblockchain (>=1.42.0,<1.43.0)"] +managedblockchain-query = ["mypy-boto3-managedblockchain-query (>=1.42.0,<1.43.0)"] +marketplace-agreement = ["mypy-boto3-marketplace-agreement (>=1.42.0,<1.43.0)"] +marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.42.0,<1.43.0)"] +marketplace-deployment = ["mypy-boto3-marketplace-deployment (>=1.42.0,<1.43.0)"] +marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.42.0,<1.43.0)"] +marketplace-reporting = ["mypy-boto3-marketplace-reporting (>=1.42.0,<1.43.0)"] +marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.42.0,<1.43.0)"] +mediaconnect = ["mypy-boto3-mediaconnect (>=1.42.0,<1.43.0)"] +mediaconvert = ["mypy-boto3-mediaconvert (>=1.42.0,<1.43.0)"] +medialive = ["mypy-boto3-medialive (>=1.42.0,<1.43.0)"] +mediapackage = ["mypy-boto3-mediapackage (>=1.42.0,<1.43.0)"] +mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.42.0,<1.43.0)"] +mediapackagev2 = ["mypy-boto3-mediapackagev2 (>=1.42.0,<1.43.0)"] +mediastore = ["mypy-boto3-mediastore (>=1.42.0,<1.43.0)"] +mediastore-data = ["mypy-boto3-mediastore-data (>=1.42.0,<1.43.0)"] +mediatailor = ["mypy-boto3-mediatailor (>=1.42.0,<1.43.0)"] +medical-imaging = ["mypy-boto3-medical-imaging (>=1.42.0,<1.43.0)"] +memorydb = ["mypy-boto3-memorydb (>=1.42.0,<1.43.0)"] +meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.42.0,<1.43.0)"] +mgh = ["mypy-boto3-mgh (>=1.42.0,<1.43.0)"] +mgn = ["mypy-boto3-mgn (>=1.42.0,<1.43.0)"] +migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.42.0,<1.43.0)"] +migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.42.0,<1.43.0)"] +migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.42.0,<1.43.0)"] +migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.42.0,<1.43.0)"] +mpa = ["mypy-boto3-mpa (>=1.42.0,<1.43.0)"] +mq = ["mypy-boto3-mq (>=1.42.0,<1.43.0)"] +mturk = ["mypy-boto3-mturk (>=1.42.0,<1.43.0)"] +mwaa = ["mypy-boto3-mwaa (>=1.42.0,<1.43.0)"] +mwaa-serverless = ["mypy-boto3-mwaa-serverless (>=1.42.0,<1.43.0)"] +neptune = ["mypy-boto3-neptune (>=1.42.0,<1.43.0)"] +neptune-graph = ["mypy-boto3-neptune-graph (>=1.42.0,<1.43.0)"] +neptunedata = ["mypy-boto3-neptunedata (>=1.42.0,<1.43.0)"] +network-firewall = ["mypy-boto3-network-firewall (>=1.42.0,<1.43.0)"] +networkflowmonitor = ["mypy-boto3-networkflowmonitor (>=1.42.0,<1.43.0)"] +networkmanager = ["mypy-boto3-networkmanager (>=1.42.0,<1.43.0)"] +networkmonitor = ["mypy-boto3-networkmonitor (>=1.42.0,<1.43.0)"] +notifications = ["mypy-boto3-notifications (>=1.42.0,<1.43.0)"] +notificationscontacts = ["mypy-boto3-notificationscontacts (>=1.42.0,<1.43.0)"] +nova-act = ["mypy-boto3-nova-act (>=1.42.0,<1.43.0)"] +oam = ["mypy-boto3-oam (>=1.42.0,<1.43.0)"] +observabilityadmin = ["mypy-boto3-observabilityadmin (>=1.42.0,<1.43.0)"] +odb = ["mypy-boto3-odb (>=1.42.0,<1.43.0)"] +omics = ["mypy-boto3-omics (>=1.42.0,<1.43.0)"] +opensearch = ["mypy-boto3-opensearch (>=1.42.0,<1.43.0)"] +opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.42.0,<1.43.0)"] +organizations = ["mypy-boto3-organizations (>=1.42.0,<1.43.0)"] +osis = ["mypy-boto3-osis (>=1.42.0,<1.43.0)"] +outposts = ["mypy-boto3-outposts (>=1.42.0,<1.43.0)"] +panorama = ["mypy-boto3-panorama (>=1.42.0,<1.43.0)"] +partnercentral-account = ["mypy-boto3-partnercentral-account (>=1.42.0,<1.43.0)"] +partnercentral-benefits = ["mypy-boto3-partnercentral-benefits (>=1.42.0,<1.43.0)"] +partnercentral-channel = ["mypy-boto3-partnercentral-channel (>=1.42.0,<1.43.0)"] +partnercentral-selling = ["mypy-boto3-partnercentral-selling (>=1.42.0,<1.43.0)"] +payment-cryptography = ["mypy-boto3-payment-cryptography (>=1.42.0,<1.43.0)"] +payment-cryptography-data = ["mypy-boto3-payment-cryptography-data (>=1.42.0,<1.43.0)"] +pca-connector-ad = ["mypy-boto3-pca-connector-ad (>=1.42.0,<1.43.0)"] +pca-connector-scep = ["mypy-boto3-pca-connector-scep (>=1.42.0,<1.43.0)"] +pcs = ["mypy-boto3-pcs (>=1.42.0,<1.43.0)"] +personalize = ["mypy-boto3-personalize (>=1.42.0,<1.43.0)"] +personalize-events = ["mypy-boto3-personalize-events (>=1.42.0,<1.43.0)"] +personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.42.0,<1.43.0)"] +pi = ["mypy-boto3-pi (>=1.42.0,<1.43.0)"] +pinpoint = ["mypy-boto3-pinpoint (>=1.42.0,<1.43.0)"] +pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.42.0,<1.43.0)"] +pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.42.0,<1.43.0)"] +pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.42.0,<1.43.0)"] +pipes = ["mypy-boto3-pipes (>=1.42.0,<1.43.0)"] +polly = ["mypy-boto3-polly (>=1.42.0,<1.43.0)"] +pricing = ["mypy-boto3-pricing (>=1.42.0,<1.43.0)"] +proton = ["mypy-boto3-proton (>=1.42.0,<1.43.0)"] +qapps = ["mypy-boto3-qapps (>=1.42.0,<1.43.0)"] +qbusiness = ["mypy-boto3-qbusiness (>=1.42.0,<1.43.0)"] +qconnect = ["mypy-boto3-qconnect (>=1.42.0,<1.43.0)"] +quicksight = ["mypy-boto3-quicksight (>=1.42.0,<1.43.0)"] +ram = ["mypy-boto3-ram (>=1.42.0,<1.43.0)"] +rbin = ["mypy-boto3-rbin (>=1.42.0,<1.43.0)"] +rds = ["mypy-boto3-rds (>=1.42.0,<1.43.0)"] +rds-data = ["mypy-boto3-rds-data (>=1.42.0,<1.43.0)"] +redshift = ["mypy-boto3-redshift (>=1.42.0,<1.43.0)"] +redshift-data = ["mypy-boto3-redshift-data (>=1.42.0,<1.43.0)"] +redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.42.0,<1.43.0)"] +rekognition = ["mypy-boto3-rekognition (>=1.42.0,<1.43.0)"] +repostspace = ["mypy-boto3-repostspace (>=1.42.0,<1.43.0)"] +resiliencehub = ["mypy-boto3-resiliencehub (>=1.42.0,<1.43.0)"] +resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.42.0,<1.43.0)"] +resource-groups = ["mypy-boto3-resource-groups (>=1.42.0,<1.43.0)"] +resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.42.0,<1.43.0)"] +rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.42.0,<1.43.0)"] +route53 = ["mypy-boto3-route53 (>=1.42.0,<1.43.0)"] +route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.42.0,<1.43.0)"] +route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.42.0,<1.43.0)"] +route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.42.0,<1.43.0)"] +route53domains = ["mypy-boto3-route53domains (>=1.42.0,<1.43.0)"] +route53globalresolver = ["mypy-boto3-route53globalresolver (>=1.42.0,<1.43.0)"] +route53profiles = ["mypy-boto3-route53profiles (>=1.42.0,<1.43.0)"] +route53resolver = ["mypy-boto3-route53resolver (>=1.42.0,<1.43.0)"] +rtbfabric = ["mypy-boto3-rtbfabric (>=1.42.0,<1.43.0)"] +rum = ["mypy-boto3-rum (>=1.42.0,<1.43.0)"] +s3 = ["mypy-boto3-s3 (>=1.42.0,<1.43.0)"] +s3control = ["mypy-boto3-s3control (>=1.42.0,<1.43.0)"] +s3outposts = ["mypy-boto3-s3outposts (>=1.42.0,<1.43.0)"] +s3tables = ["mypy-boto3-s3tables (>=1.42.0,<1.43.0)"] +s3vectors = ["mypy-boto3-s3vectors (>=1.42.0,<1.43.0)"] +sagemaker = ["mypy-boto3-sagemaker (>=1.42.0,<1.43.0)"] +sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.42.0,<1.43.0)"] +sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.42.0,<1.43.0)"] +sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.42.0,<1.43.0)"] +sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.42.0,<1.43.0)"] +sagemaker-metrics = ["mypy-boto3-sagemaker-metrics (>=1.42.0,<1.43.0)"] +sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.42.0,<1.43.0)"] +savingsplans = ["mypy-boto3-savingsplans (>=1.42.0,<1.43.0)"] +scheduler = ["mypy-boto3-scheduler (>=1.42.0,<1.43.0)"] +schemas = ["mypy-boto3-schemas (>=1.42.0,<1.43.0)"] +sdb = ["mypy-boto3-sdb (>=1.42.0,<1.43.0)"] +secretsmanager = ["mypy-boto3-secretsmanager (>=1.42.0,<1.43.0)"] +security-ir = ["mypy-boto3-security-ir (>=1.42.0,<1.43.0)"] +securityhub = ["mypy-boto3-securityhub (>=1.42.0,<1.43.0)"] +securitylake = ["mypy-boto3-securitylake (>=1.42.0,<1.43.0)"] +serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.42.0,<1.43.0)"] +service-quotas = ["mypy-boto3-service-quotas (>=1.42.0,<1.43.0)"] +servicecatalog = ["mypy-boto3-servicecatalog (>=1.42.0,<1.43.0)"] +servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.42.0,<1.43.0)"] +servicediscovery = ["mypy-boto3-servicediscovery (>=1.42.0,<1.43.0)"] +ses = ["mypy-boto3-ses (>=1.42.0,<1.43.0)"] +sesv2 = ["mypy-boto3-sesv2 (>=1.42.0,<1.43.0)"] +shield = ["mypy-boto3-shield (>=1.42.0,<1.43.0)"] +signer = ["mypy-boto3-signer (>=1.42.0,<1.43.0)"] +signin = ["mypy-boto3-signin (>=1.42.0,<1.43.0)"] +simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.42.0,<1.43.0)"] +snow-device-management = ["mypy-boto3-snow-device-management (>=1.42.0,<1.43.0)"] +snowball = ["mypy-boto3-snowball (>=1.42.0,<1.43.0)"] +sns = ["mypy-boto3-sns (>=1.42.0,<1.43.0)"] +socialmessaging = ["mypy-boto3-socialmessaging (>=1.42.0,<1.43.0)"] +sqs = ["mypy-boto3-sqs (>=1.42.0,<1.43.0)"] +ssm = ["mypy-boto3-ssm (>=1.42.0,<1.43.0)"] +ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.42.0,<1.43.0)"] +ssm-guiconnect = ["mypy-boto3-ssm-guiconnect (>=1.42.0,<1.43.0)"] +ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.42.0,<1.43.0)"] +ssm-quicksetup = ["mypy-boto3-ssm-quicksetup (>=1.42.0,<1.43.0)"] +ssm-sap = ["mypy-boto3-ssm-sap (>=1.42.0,<1.43.0)"] +sso = ["mypy-boto3-sso (>=1.42.0,<1.43.0)"] +sso-admin = ["mypy-boto3-sso-admin (>=1.42.0,<1.43.0)"] +sso-oidc = ["mypy-boto3-sso-oidc (>=1.42.0,<1.43.0)"] +stepfunctions = ["mypy-boto3-stepfunctions (>=1.42.0,<1.43.0)"] +storagegateway = ["mypy-boto3-storagegateway (>=1.42.0,<1.43.0)"] +sts = ["mypy-boto3-sts (>=1.42.0,<1.43.0)"] +supplychain = ["mypy-boto3-supplychain (>=1.42.0,<1.43.0)"] +support = ["mypy-boto3-support (>=1.42.0,<1.43.0)"] +support-app = ["mypy-boto3-support-app (>=1.42.0,<1.43.0)"] +swf = ["mypy-boto3-swf (>=1.42.0,<1.43.0)"] +synthetics = ["mypy-boto3-synthetics (>=1.42.0,<1.43.0)"] +taxsettings = ["mypy-boto3-taxsettings (>=1.42.0,<1.43.0)"] +textract = ["mypy-boto3-textract (>=1.42.0,<1.43.0)"] +timestream-influxdb = ["mypy-boto3-timestream-influxdb (>=1.42.0,<1.43.0)"] +timestream-query = ["mypy-boto3-timestream-query (>=1.42.0,<1.43.0)"] +timestream-write = ["mypy-boto3-timestream-write (>=1.42.0,<1.43.0)"] +tnb = ["mypy-boto3-tnb (>=1.42.0,<1.43.0)"] +transcribe = ["mypy-boto3-transcribe (>=1.42.0,<1.43.0)"] +transfer = ["mypy-boto3-transfer (>=1.42.0,<1.43.0)"] +translate = ["mypy-boto3-translate (>=1.42.0,<1.43.0)"] +trustedadvisor = ["mypy-boto3-trustedadvisor (>=1.42.0,<1.43.0)"] +verifiedpermissions = ["mypy-boto3-verifiedpermissions (>=1.42.0,<1.43.0)"] +voice-id = ["mypy-boto3-voice-id (>=1.42.0,<1.43.0)"] +vpc-lattice = ["mypy-boto3-vpc-lattice (>=1.42.0,<1.43.0)"] +waf = ["mypy-boto3-waf (>=1.42.0,<1.43.0)"] +waf-regional = ["mypy-boto3-waf-regional (>=1.42.0,<1.43.0)"] +wafv2 = ["mypy-boto3-wafv2 (>=1.42.0,<1.43.0)"] +wellarchitected = ["mypy-boto3-wellarchitected (>=1.42.0,<1.43.0)"] +wickr = ["mypy-boto3-wickr (>=1.42.0,<1.43.0)"] +wisdom = ["mypy-boto3-wisdom (>=1.42.0,<1.43.0)"] +workdocs = ["mypy-boto3-workdocs (>=1.42.0,<1.43.0)"] +workmail = ["mypy-boto3-workmail (>=1.42.0,<1.43.0)"] +workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.42.0,<1.43.0)"] +workspaces = ["mypy-boto3-workspaces (>=1.42.0,<1.43.0)"] +workspaces-instances = ["mypy-boto3-workspaces-instances (>=1.42.0,<1.43.0)"] +workspaces-thin-client = ["mypy-boto3-workspaces-thin-client (>=1.42.0,<1.43.0)"] +workspaces-web = ["mypy-boto3-workspaces-web (>=1.42.0,<1.43.0)"] +xray = ["mypy-boto3-xray (>=1.42.0,<1.43.0)"] + [[package]] name = "botocore" version = "1.42.10" @@ -115,6 +552,24 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > [package.extras] crt = ["awscrt (==0.29.2)"] +[[package]] +name = "botocore-stubs" +version = "1.42.25" +description = "Type annotations and code completion for botocore" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "botocore_stubs-1.42.25-py3-none-any.whl", hash = "sha256:49d15529002bd1099a9a099a77d70b7b52859153783440e96eb55791e8147d1b"}, + {file = "botocore_stubs-1.42.25.tar.gz", hash = "sha256:70a8a53ba2684ff462c44d5996acd85fc5c7eb969e2cf3c25274441269524298"}, +] + +[package.dependencies] +types-awscrt = "*" + +[package.extras] +botocore = ["botocore"] + [[package]] name = "certifi" version = "2025.11.12" @@ -958,6 +1413,90 @@ ssm = ["PyYAML (>=5.1)"] stepfunctions = ["antlr4-python3-runtime", "jsonpath_ng"] xray = ["aws-xray-sdk (>=0.93,!=0.96)", "setuptools"] +[[package]] +name = "mypy" +version = "1.18.2" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c"}, + {file = "mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e"}, + {file = "mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b"}, + {file = "mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66"}, + {file = "mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428"}, + {file = "mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed"}, + {file = "mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f"}, + {file = "mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341"}, + {file = "mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d"}, + {file = "mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86"}, + {file = "mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37"}, + {file = "mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8"}, + {file = "mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34"}, + {file = "mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764"}, + {file = "mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893"}, + {file = "mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914"}, + {file = "mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8"}, + {file = "mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074"}, + {file = "mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc"}, + {file = "mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e"}, + {file = "mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986"}, + {file = "mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d"}, + {file = "mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba"}, + {file = "mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544"}, + {file = "mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce"}, + {file = "mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d"}, + {file = "mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c"}, + {file = "mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb"}, + {file = "mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075"}, + {file = "mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf"}, + {file = "mypy-1.18.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:25a9c8fb67b00599f839cf472713f54249a62efd53a54b565eb61956a7e3296b"}, + {file = "mypy-1.18.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2b9c7e284ee20e7598d6f42e13ca40b4928e6957ed6813d1ab6348aa3f47133"}, + {file = "mypy-1.18.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6985ed057513e344e43a26cc1cd815c7a94602fb6a3130a34798625bc2f07b6"}, + {file = "mypy-1.18.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22f27105f1525ec024b5c630c0b9f36d5c1cc4d447d61fe51ff4bd60633f47ac"}, + {file = "mypy-1.18.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:030c52d0ea8144e721e49b1f68391e39553d7451f0c3f8a7565b59e19fcb608b"}, + {file = "mypy-1.18.2-cp39-cp39-win_amd64.whl", hash = "sha256:aa5e07ac1a60a253445797e42b8b2963c9675563a94f11291ab40718b016a7a0"}, + {file = "mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e"}, + {file = "mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b"}, +] + +[package.dependencies] +mypy_extensions = ">=1.0.0" +pathspec = ">=0.9.0" +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-boto3-s3" +version = "1.42.21" +description = "Type annotations for boto3 S3 1.42.21 service generated with mypy-boto3-builder 8.12.0" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "mypy_boto3_s3-1.42.21-py3-none-any.whl", hash = "sha256:f5b7d1ed718ba5b00f67e95a9a38c6a021159d3071ea235e6cf496e584115ded"}, + {file = "mypy_boto3_s3-1.42.21.tar.gz", hash = "sha256:cab71c918aac7d98c4d742544c722e37d8e7178acb8bc88a0aead7b1035026d2"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + [[package]] name = "networkx" version = "3.6.1" @@ -1027,6 +1566,24 @@ files = [ {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] +[[package]] +name = "pathspec" +version = "1.0.3" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c"}, + {file = "pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + [[package]] name = "platformdirs" version = "4.5.1" @@ -1808,6 +2365,81 @@ files = [ {file = "stevedore-5.6.0.tar.gz", hash = "sha256:f22d15c6ead40c5bbfa9ca54aa7e7b4a07d59b36ae03ed12ced1a54cf0b51945"}, ] +[[package]] +name = "types-awscrt" +version = "0.31.0" +description = "Type annotations and code completion for awscrt" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "types_awscrt-0.31.0-py3-none-any.whl", hash = "sha256:009cfe5b9af8c75e8304243490e20a5229e7a56203f1d41481f5522233453f51"}, + {file = "types_awscrt-0.31.0.tar.gz", hash = "sha256:aa8b42148af0847be14e2b8ea3637a3518ffab038f8d3be7083950f3ce87d3ff"}, +] + +[[package]] +name = "types-click" +version = "7.1.8" +description = "Typing stubs for click" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "types-click-7.1.8.tar.gz", hash = "sha256:b6604968be6401dc516311ca50708a0a28baa7a0cb840efd7412f0dbbff4e092"}, + {file = "types_click-7.1.8-py3-none-any.whl", hash = "sha256:8cb030a669e2e927461be9827375f83c16b8178c365852c060a34e24871e7e81"}, +] + +[[package]] +name = "types-pyyaml" +version = "6.0.12.20250915" +description = "Typing stubs for PyYAML" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6"}, + {file = "types_pyyaml-6.0.12.20250915.tar.gz", hash = "sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3"}, +] + +[[package]] +name = "types-requests" +version = "2.32.4.20260107" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d"}, + {file = "types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f"}, +] + +[package.dependencies] +urllib3 = ">=2" + +[[package]] +name = "types-s3transfer" +version = "0.16.0" +description = "Type annotations and code completion for s3transfer" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "types_s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:1c0cd111ecf6e21437cb410f5cddb631bfb2263b77ad973e79b9c6d0cb24e0ef"}, + {file = "types_s3transfer-0.16.0.tar.gz", hash = "sha256:b4636472024c5e2b62278c5b759661efeb52a81851cde5f092f24100b1ecb443"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + [[package]] name = "urllib3" version = "2.6.2" @@ -1883,4 +2515,4 @@ test = ["pytest", "pytest-cov"] [metadata] lock-version = "2.1" python-versions = ">=3.13, <3.14" -content-hash = "72c619d1320245804ef38e5d9d45f0e026329d19e2ccbff02d65ec69dc8939f4" +content-hash = "ecda6b78f55473432a4125c4b7c74b03e26abf18dee0c87f242fc164410a82c0" diff --git a/pyproject.toml b/pyproject.toml index 7fd177bd..1a79e0c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,11 @@ pytest = "^9.0.2" pytest-cov = "^7.0.0" isort = "^7.0.0" requests-mock = "^1.12.1" +mypy = "1.18.2" +types-click = "^7.1.8" +types-pyyaml = "^6.0.12.20250915" +types-requests = "^2.32.4.20260107" +boto3-stubs = { extras = ["s3"], version = "^1.42.25" } [tool.poetry.group.docs.dependencies] sphinx-rtd-theme = "^3.0.2" diff --git a/src/gardenlinux/distro_version/__init__.py b/src/gardenlinux/distro_version/__init__.py index 4ee39ef3..5a9a6c8c 100644 --- a/src/gardenlinux/distro_version/__init__.py +++ b/src/gardenlinux/distro_version/__init__.py @@ -1,3 +1,6 @@ +from typing import Self + + class UnsupportedDistroVersion(Exception): pass @@ -6,46 +9,24 @@ class NotAPatchRelease(Exception): pass -def DistroVersion(maybe_distro_version): - version_components = maybe_distro_version.split(".") - if len(version_components) > 3 or len(version_components) < 2: - raise UnsupportedDistroVersion( - f"Unexpected version number format {maybe_distro_version}" - ) - - if not all(map(lambda x: x.isdigit(), version_components)): - raise UnsupportedDistroVersion( - f"Unexpected version number format {maybe_distro_version}" - ) - - if len(version_components) == 2: - return LegacyDistroVersion(*(int(c) for c in version_components)) - elif len(version_components) == 3: - return SemverDistroVersion(*(int(c) for c in version_components)) - else: - raise UnsupportedDistroVersion( - f"Unexpected number of version components: {maybe_distro_version}" - ) - - class BaseDistroVersion: - major = None - minor = None - patch = None + major: int = 0 + minor: int = 0 + patch: int = 0 - def is_patch_release(self): + def is_patch_release(self) -> int: return self.patch and self.patch > 0 class LegacyDistroVersion(BaseDistroVersion): - def __init__(self, major, patch): + def __init__(self: Self, major: int, patch: int) -> None: self.major = major self.patch = patch - def __str__(self): + def __str__(self) -> str: return f"{self.major}.{self.patch}" - def previous_patch_release(self): + def previous_patch_release(self) -> "LegacyDistroVersion": if not self.is_patch_release(): raise NotAPatchRelease(f"{self} is not a patch release") @@ -53,16 +34,40 @@ def previous_patch_release(self): class SemverDistroVersion(BaseDistroVersion): - def __init__(self, major, minor, patch): + def __init__(self, major: int, minor: int, patch: int) -> None: self.major = major self.minor = minor self.patch = patch - def __str__(self): + def __str__(self) -> str: return f"{self.major}.{self.minor}.{self.patch}" - def previous_patch_release(self): + def previous_patch_release(self) -> "SemverDistroVersion": if not self.is_patch_release(): raise NotAPatchRelease(f"{self} is not a patch release") return SemverDistroVersion(self.major, self.minor, self.patch - 1) + + +def DistroVersion( + maybe_distro_version: str, +) -> LegacyDistroVersion | SemverDistroVersion: + version_components = maybe_distro_version.split(".") + if len(version_components) > 3 or len(version_components) < 2: + raise UnsupportedDistroVersion( + f"Unexpected version number format {maybe_distro_version}" + ) + + if not all(map(lambda x: x.isdigit(), version_components)): + raise UnsupportedDistroVersion( + f"Unexpected version number format {maybe_distro_version}" + ) + + if len(version_components) == 2: + return LegacyDistroVersion(*(int(c) for c in version_components)) + elif len(version_components) == 3: + return SemverDistroVersion(*(int(c) for c in version_components)) + else: + raise UnsupportedDistroVersion( + f"Unexpected number of version components: {maybe_distro_version}" + ) diff --git a/src/gardenlinux/git/repository.py b/src/gardenlinux/git/repository.py index 6b98ec7c..08f81075 100755 --- a/src/gardenlinux/git/repository.py +++ b/src/gardenlinux/git/repository.py @@ -74,12 +74,12 @@ def root(self) -> Path: :since: 0.10.0 """ - root_dir = self.workdir + root_dir: Path = Path(self.workdir) if self.is_bare: - root_dir = self.path + root_dir = Path(self.path) - usual_git_dir = Path(root_dir, ".git") + usual_git_dir = root_dir / ".git" # Possible submodule Git repository. Validate repository containing `.git` directory. if self.path != str(usual_git_dir): diff --git a/src/gardenlinux/github/release/__init__.py b/src/gardenlinux/github/release/__init__.py index 18dd9cb8..db8549fc 100644 --- a/src/gardenlinux/github/release/__init__.py +++ b/src/gardenlinux/github/release/__init__.py @@ -1,4 +1,5 @@ import json +import logging import os import sys @@ -7,10 +8,12 @@ from gardenlinux.constants import RELEASE_ID_FILE, REQUESTS_TIMEOUTS from gardenlinux.logger import LoggerSetup -LOGGER = LoggerSetup.get_logger("gardenlinux.github.release", "INFO") +LOGGER = LoggerSetup.get_logger("gardenlinux.github.release", logging.INFO) -def create_github_release(owner, repo, tag, commitish, latest, body): +def create_github_release( + owner: str, repo: str, tag: str, commitish: str, latest: bool, body: str +) -> int | None: token = os.environ.get("GITHUB_TOKEN") if not token: raise ValueError("GITHUB_TOKEN environment variable not set") @@ -40,17 +43,19 @@ def create_github_release(owner, repo, tag, commitish, latest, body): if response.status_code == 201: LOGGER.info("Release created successfully") response_json = response.json() - return response_json.get("id") + return int(response_json.get("id")) # Will raise KeyError if missing else: LOGGER.error("Failed to create release") LOGGER.debug(response.json()) response.raise_for_status() + return None # Simply to make mypy happy. should not be reached. -def write_to_release_id_file(release_id): + +def write_to_release_id_file(release_id: str | int) -> None: try: with open(RELEASE_ID_FILE, "w") as file: - file.write(release_id) + file.write(str(release_id)) LOGGER.info(f"Created {RELEASE_ID_FILE} successfully.") except IOError as e: LOGGER.error(f"Could not create {RELEASE_ID_FILE} file: {e}") @@ -58,8 +63,12 @@ def write_to_release_id_file(release_id): def upload_to_github_release_page( - github_owner, github_repo, gardenlinux_release_id, file_to_upload, dry_run -): + github_owner: str, + github_repo: str, + gardenlinux_release_id: str | int, + file_to_upload: str, + dry_run: bool, +) -> None: if dry_run: LOGGER.info( f"Dry run: would upload {file_to_upload} to release {gardenlinux_release_id} in repo {github_owner}/{github_repo}" diff --git a/src/gardenlinux/github/release/__main__.py b/src/gardenlinux/github/release/__main__.py index 61c37338..29c6d2fd 100644 --- a/src/gardenlinux/github/release/__main__.py +++ b/src/gardenlinux/github/release/__main__.py @@ -1,4 +1,5 @@ import argparse +import logging from gardenlinux.constants import GARDENLINUX_GITHUB_RELEASE_BUCKET_NAME from gardenlinux.logger import LoggerSetup @@ -10,10 +11,10 @@ write_to_release_id_file, ) -LOGGER = LoggerSetup.get_logger("gardenlinux.github", "INFO") +LOGGER = LoggerSetup.get_logger("gardenlinux.github", logging.INFO) -def main(): +def main() -> None: parser = argparse.ArgumentParser(description="GitHub Release Script") subparsers = parser.add_subparsers(dest="command") diff --git a/src/gardenlinux/github/release_notes/__init__.py b/src/gardenlinux/github/release_notes/__init__.py index d1ab7e35..6e0f7603 100644 --- a/src/gardenlinux/github/release_notes/__init__.py +++ b/src/gardenlinux/github/release_notes/__init__.py @@ -8,8 +8,8 @@ def create_github_release_notes( - gardenlinux_version, commitish, releases_s3_bucket_name -): + gardenlinux_version: str, commitish: str, releases_s3_bucket_name: str +) -> str: package_list = get_package_list(gardenlinux_version) output = "" diff --git a/src/gardenlinux/github/release_notes/deployment_platform/__init__.py b/src/gardenlinux/github/release_notes/deployment_platform/__init__.py index 9882dc53..0ed84069 100644 --- a/src/gardenlinux/github/release_notes/deployment_platform/__init__.py +++ b/src/gardenlinux/github/release_notes/deployment_platform/__init__.py @@ -1,16 +1,20 @@ +from typing import Any, Dict + from gardenlinux.constants import GARDENLINUX_GITHUB_RELEASE_BUCKET_NAME class DeploymentPlatform: artifacts_bucket_name = GARDENLINUX_GITHUB_RELEASE_BUCKET_NAME - def short_name(self): + def short_name(self) -> str: return "generic" - def full_name(self): + def full_name(self) -> str: return "Generic Deployment Platform" - def published_images_by_regions(self, image_metadata): + def published_images_by_regions( + self, image_metadata: Dict[str, Any] + ) -> Dict[str, Any]: published_image_metadata = image_metadata["published_image_metadata"] flavor_name = image_metadata["s3_key"].split("/")[-1] @@ -21,10 +25,10 @@ def published_images_by_regions(self, image_metadata): return {"flavor": flavor_name, "regions": regions} - def image_extension(self): + def image_extension(self) -> str: return "raw" - def artifact_for_flavor(self, flavor, markdown_format=True): + def artifact_for_flavor(self, flavor: str, markdown_format: bool = True) -> str: base_url = ( f"https://{self.__class__.artifacts_bucket_name}.s3.amazonaws.com/objects" ) @@ -35,11 +39,11 @@ def artifact_for_flavor(self, flavor, markdown_format=True): else: return download_url - def region_details(self, image_metadata): + def region_details(self, image_metadata: Dict[str, Any]) -> str: """ Generate the detailed region information for the collapsible section """ - details = "" + details: str = "" match self.published_images_by_regions(image_metadata): case {"regions": regions}: @@ -81,7 +85,7 @@ def region_details(self, image_metadata): return details - def summary_text(self, image_metadata): + def summary_text(self, image_metadata: Dict[str, Any]) -> str: """ Generate the summary text for the collapsible section """ diff --git a/src/gardenlinux/github/release_notes/deployment_platform/ali_cloud.py b/src/gardenlinux/github/release_notes/deployment_platform/ali_cloud.py index cf40945f..451a4e35 100644 --- a/src/gardenlinux/github/release_notes/deployment_platform/ali_cloud.py +++ b/src/gardenlinux/github/release_notes/deployment_platform/ali_cloud.py @@ -3,11 +3,11 @@ class AliCloud(DeploymentPlatform): - def short_name(self): + def short_name(self) -> str: return "ali" - def full_name(self): + def full_name(self) -> str: return "Alibaba Cloud" - def image_extension(self): + def image_extension(self) -> str: return "qcow2" diff --git a/src/gardenlinux/github/release_notes/deployment_platform/amazon_web_services.py b/src/gardenlinux/github/release_notes/deployment_platform/amazon_web_services.py index 5013d08c..3ca061d7 100644 --- a/src/gardenlinux/github/release_notes/deployment_platform/amazon_web_services.py +++ b/src/gardenlinux/github/release_notes/deployment_platform/amazon_web_services.py @@ -1,15 +1,18 @@ -# pyright: reportIncompatibleMethodOverride=false +from typing import Any, Dict + from . import DeploymentPlatform class AmazonWebServices(DeploymentPlatform): - def short_name(self): + def short_name(self) -> str: return "aws" - def full_name(self): + def full_name(self) -> str: return "Amazon Web Services" - def published_images_by_regions(self, image_metadata): + def published_images_by_regions( + self, image_metadata: Dict[str, Any] + ) -> Dict[str, Any]: published_image_metadata = image_metadata["published_image_metadata"] flavor_name = image_metadata["s3_key"].split("/")[-1] diff --git a/src/gardenlinux/github/release_notes/deployment_platform/azure.py b/src/gardenlinux/github/release_notes/deployment_platform/azure.py index 58201a3e..4a0cae1f 100644 --- a/src/gardenlinux/github/release_notes/deployment_platform/azure.py +++ b/src/gardenlinux/github/release_notes/deployment_platform/azure.py @@ -1,18 +1,21 @@ -# pyright: reportIncompatibleMethodOverride=false +from typing import Any, Dict + from . import DeploymentPlatform class Azure(DeploymentPlatform): - def short_name(self): + def short_name(self) -> str: return "azure" - def full_name(self): + def full_name(self) -> str: return "Microsoft Azure" - def image_extension(self): + def image_extension(self) -> str: return "vhd" - def published_images_by_regions(self, image_metadata): + def published_images_by_regions( + self, image_metadata: Dict[str, Any] + ) -> Dict[str, Any]: published_image_metadata = image_metadata["published_image_metadata"] flavor_name = image_metadata["s3_key"].split("/")[-1] diff --git a/src/gardenlinux/github/release_notes/deployment_platform/google_cloud.py b/src/gardenlinux/github/release_notes/deployment_platform/google_cloud.py index bbad8563..e7b4034a 100644 --- a/src/gardenlinux/github/release_notes/deployment_platform/google_cloud.py +++ b/src/gardenlinux/github/release_notes/deployment_platform/google_cloud.py @@ -1,18 +1,22 @@ # pyright: reportIncompatibleMethodOverride=false +from typing import Any, Dict + from . import DeploymentPlatform class GoogleCloud(DeploymentPlatform): - def short_name(self): + def short_name(self) -> str: return "gcp" - def full_name(self): + def full_name(self) -> str: return "Google Cloud Platform" - def image_extension(self): + def image_extension(self) -> str: return "gcpimage.tar.gz" - def published_images_by_regions(self, image_metadata): + def published_images_by_regions( + self, image_metadata: Dict[str, Any] + ) -> Dict[str, Any]: published_image_metadata = image_metadata["published_image_metadata"] flavor_name = image_metadata["s3_key"].split("/")[-1] diff --git a/src/gardenlinux/github/release_notes/deployment_platform/openstack.py b/src/gardenlinux/github/release_notes/deployment_platform/openstack.py index 82a1c6ac..970fb508 100644 --- a/src/gardenlinux/github/release_notes/deployment_platform/openstack.py +++ b/src/gardenlinux/github/release_notes/deployment_platform/openstack.py @@ -1,15 +1,19 @@ # pyright: reportIncompatibleMethodOverride=false +from typing import Any, Dict + from . import DeploymentPlatform class OpenStack(DeploymentPlatform): - def short_name(self): + def short_name(self) -> str: return "openstack" - def full_name(self): + def full_name(self) -> str: return "OpenStack" - def published_images_by_regions(self, image_metadata): + def published_images_by_regions( + self, image_metadata: Dict[str, Any] + ) -> Dict[str, Any]: published_image_metadata = image_metadata["published_image_metadata"] flavor_name = image_metadata["s3_key"].split("/")[-1] diff --git a/src/gardenlinux/github/release_notes/deployment_platform/openstack_baremetal.py b/src/gardenlinux/github/release_notes/deployment_platform/openstack_baremetal.py index 17d1c80b..a253182a 100644 --- a/src/gardenlinux/github/release_notes/deployment_platform/openstack_baremetal.py +++ b/src/gardenlinux/github/release_notes/deployment_platform/openstack_baremetal.py @@ -3,8 +3,8 @@ class OpenStackBareMetal(OpenStack): - def short_name(self): + def short_name(self) -> str: return "openstackbaremetal" - def full_name(self): + def full_name(self) -> str: return "OpenStack Baremetal" diff --git a/src/gardenlinux/github/release_notes/helpers.py b/src/gardenlinux/github/release_notes/helpers.py index 69103dac..a6dddd4d 100644 --- a/src/gardenlinux/github/release_notes/helpers.py +++ b/src/gardenlinux/github/release_notes/helpers.py @@ -1,7 +1,9 @@ import gzip import io +import logging import shutil from pathlib import Path +from typing import Any import requests from git import Repo @@ -18,10 +20,12 @@ from gardenlinux.logger import LoggerSetup from gardenlinux.s3 import S3Artifacts -LOGGER = LoggerSetup.get_logger("gardenlinux.github.release_notes.helpers", "INFO") +LOGGER = LoggerSetup.get_logger( + "gardenlinux.github.release_notes.helpers", logging.INFO +) -def get_package_list(gardenlinux_version): +def get_package_list(gardenlinux_version: str) -> DebsrcFile: url = f"{GL_DEB_REPO_BASE_URL}/dists/{gardenlinux_version}/main/binary-amd64/Packages.gz" response = requests.get(url, timeout=REQUESTS_TIMEOUTS) response.raise_for_status() @@ -35,7 +39,7 @@ def get_package_list(gardenlinux_version): return d -def compare_apt_repo_versions(previous_version, current_version): +def compare_apt_repo_versions(previous_version: Any, current_version: Any) -> str: previous_repo = GardenLinuxRepo(previous_version) current_repo = GardenLinuxRepo(current_version) pkg_diffs = sorted(compare_repo(previous_repo, current_repo), key=lambda t: t[0]) @@ -48,7 +52,9 @@ def compare_apt_repo_versions(previous_version, current_version): return output -def download_all_metadata_files(version, commitish, s3_bucket_name): +def download_all_metadata_files( + version: Any, commitish: str, s3_bucket_name: str +) -> list[str]: repo = Repo(".") commit = repo.commit(commitish) flavors_data = commit.tree["flavors.yaml"].data_stream.read().decode("utf-8") @@ -95,7 +101,13 @@ def download_all_metadata_files(version, commitish, s3_bucket_name): return [str(artifact) for artifact in local_dest_path.iterdir()] -def download_metadata_file(s3_artifacts, cname, version, commit_short, artifacts_dir): +def download_metadata_file( + s3_artifacts: S3Artifacts, + cname: CName, + version: Any, + commit_short: str, + artifacts_dir: Path, +) -> None: """ Download metadata file (s3_metadata.yaml) """ @@ -111,11 +123,11 @@ def download_metadata_file(s3_artifacts, cname, version, commit_short, artifacts s3_artifacts.bucket.download_file( release_object.key, - artifacts_dir.joinpath(f"{cname.cname}.s3_metadata.yaml"), + str(artifacts_dir.joinpath(f"{cname.cname}.s3_metadata.yaml")), ) -def get_variant_from_flavor(flavor_name): +def get_variant_from_flavor(flavor_name: str) -> str: """ Determine the variant from a flavor name by checking for variant suffixes. Returns the variant key (e.g., 'legacy', 'usi', 'tpm2_trustedboot'). diff --git a/src/gardenlinux/github/release_notes/sections.py b/src/gardenlinux/github/release_notes/sections.py index 1c889495..a6af90aa 100644 --- a/src/gardenlinux/github/release_notes/sections.py +++ b/src/gardenlinux/github/release_notes/sections.py @@ -1,5 +1,7 @@ +import logging import re import textwrap +from typing import Any, Dict import requests import yaml @@ -17,7 +19,7 @@ from .deployment_platform.openstack_baremetal import OpenStackBareMetal from .helpers import compare_apt_repo_versions, get_variant_from_flavor -LOGGER = LoggerSetup.get_logger("gardenlinux.github.release_notes", "INFO") +LOGGER = LoggerSetup.get_logger("gardenlinux.github.release_notes", logging.INFO) IMAGE_IDS_VARIANT_ORDER = ["legacy", "usi", "tpm2_trustedboot"] IMAGE_IDS_VARIANT_TABLE_NAMES = { @@ -40,7 +42,7 @@ } -def release_notes_changes_section(gardenlinux_version): +def release_notes_changes_section(gardenlinux_version: str) -> str: """ Get list of fixed CVEs, grouped by upgraded package. Note: This result is not perfect, feel free to edit the generated release notes and @@ -87,7 +89,7 @@ def release_notes_changes_section(gardenlinux_version): ) -def release_notes_software_components_section(package_list): +def release_notes_software_components_section(package_list: Dict[str, Any]) -> str: output = "## Software Component Versions\n" output += "```" output += "\n" @@ -102,7 +104,9 @@ def release_notes_software_components_section(package_list): return output -def release_notes_compare_package_versions_section(gardenlinux_version, package_list): +def release_notes_compare_package_versions_section( + gardenlinux_version: str, package_list: dict[str, Any] +) -> str: version = DistroVersion(gardenlinux_version) output = "" @@ -126,7 +130,9 @@ def release_notes_compare_package_versions_section(gardenlinux_version, package_ return output -def generate_table_format(grouped_data): +def generate_table_format( + grouped_data: dict[str, dict[str, dict[str, list[dict[str, Any]]]]], +) -> str: """ Generate the table format with collapsible region details """ @@ -165,7 +171,9 @@ def generate_table_format(grouped_data): return output -def generate_detailed_format(grouped_data): +def generate_detailed_format( + grouped_data: dict[str, dict[str, dict[str, list[dict[str, Any]]]]], +) -> str: """ Generate the old detailed format with YAML """ @@ -243,12 +251,12 @@ def generate_detailed_format(grouped_data): return output -def release_notes_image_ids_section(metadata_files): +def release_notes_image_ids_section(metadata_files: list[str]) -> str: """ Groups metadata files by image variant, then platform, then architecture """ # Group metadata by variant, platform, and architecture - grouped_data = {} + grouped_data: dict[str, dict[str, dict[str, list[dict[str, Any]]]]] = {} for metadata_file_path in metadata_files: with open(metadata_file_path) as f: diff --git a/src/gardenlinux/s3/bucket.py b/src/gardenlinux/s3/bucket.py index 85142255..37d0e3b4 100644 --- a/src/gardenlinux/s3/bucket.py +++ b/src/gardenlinux/s3/bucket.py @@ -9,10 +9,14 @@ from os import PathLike from pathlib import Path from time import time -from typing import Any, BinaryIO, List, Optional +from typing import TYPE_CHECKING, Any, BinaryIO, List, Optional import boto3 +if TYPE_CHECKING: + # Only import when type checking is enabled i.e. in a dev environment or CI. + from mypy_boto3_s3.service_resource import BucketObjectsCollection + from ..logger import LoggerSetup @@ -62,7 +66,7 @@ def __init__( self._logger = logger @property - def objects(self) -> List[Any]: + def objects(self) -> "BucketObjectsCollection": """ Returns a list of all objects in a bucket. @@ -72,7 +76,7 @@ def objects(self) -> List[Any]: self._logger.debug(f"Returning all S3 bucket objects for {self._bucket.name}") - return self._bucket.objects.all() # type: ignore[no-any-return] + return self._bucket.objects.all() def __getattr__(self, name: str) -> Any: """ diff --git a/src/gardenlinux/s3/s3_artifacts.py b/src/gardenlinux/s3/s3_artifacts.py index 446206ea..8bf686f6 100644 --- a/src/gardenlinux/s3/s3_artifacts.py +++ b/src/gardenlinux/s3/s3_artifacts.py @@ -84,14 +84,14 @@ def download_to_directory( raise RuntimeError(f"Artifacts directory given is invalid: {artifacts_dir}") release_object = list( - self._bucket.objects.filter(Prefix=f"meta/singles/{cname}") # type: ignore[attr-defined] + self._bucket.objects.filter(Prefix=f"meta/singles/{cname}") )[0] self._bucket.download_file( release_object.key, str(artifacts_dir.joinpath(f"{cname}.s3_metadata.yaml")) ) - for s3_object in self._bucket.objects.filter(Prefix=f"objects/{cname}").all(): # type: ignore[attr-defined] + for s3_object in self._bucket.objects.filter(Prefix=f"objects/{cname}").all(): self._bucket.download_file( s3_object.key, str(artifacts_dir.joinpath(basename(s3_object.key))) ) diff --git a/tests/apt/test_package_repo_info.py b/tests/apt/test_package_repo_info.py index 917a38b0..4eb19c44 100644 --- a/tests/apt/test_package_repo_info.py +++ b/tests/apt/test_package_repo_info.py @@ -1,17 +1,16 @@ from types import SimpleNamespace -from typing import List +from typing import List, Tuple import pytest -from apt_repo import BinaryPackage import gardenlinux.apt.package_repo_info as repoinfo class FakeAPTRepo: """ - Fake replacement for apt_repo.APTTRepository. + Fake replacement for apt_repo.APTRepository. - - stores the contructor args for assertions + - stores the constructor args for assertions - exposes `.packages` and `get_packages_by_name(name)` """ @@ -20,12 +19,22 @@ def __init__(self, url: str, dist: str, components: List[str]) -> None: self.dist = dist self.components = components # list of objects with .package and .version attributes - self.packages: List[BinaryPackage] = [] + self.packages: List[SimpleNamespace] = [] - def get_packages_by_name(self, name: str) -> BinaryPackage: + def get_packages_by_name(self, name: str) -> List[SimpleNamespace]: return [p for p in self.packages if p.package == name] +# Fake GardenLinuxRepo subclass to avoid incomplete type issues in compare tests +class FakeRepo(repoinfo.GardenLinuxRepo): + def __init__(self, versions: List[Tuple[str, str]]) -> None: + # Skip calling the real constructor + self._versions = versions + + def get_packages_versions(self) -> List[Tuple[str, str]]: + return self._versions + + def test_gardenlinuxrepo_init(monkeypatch: pytest.MonkeyPatch) -> None: """ Test if GardenLinuxRepo creates an internal APTRepo @@ -91,11 +100,11 @@ def test_compare_repo_union_returns_all() -> None: - names in both but with different versions """ # Arrange - a = SimpleNamespace(get_packages_versions=lambda: [("a", "1"), ("b", "2")]) - b = SimpleNamespace(get_packages_versions=lambda: [("b", "3"), ("c", "4")]) + a = FakeRepo([("a", "1"), ("b", "2")]) + b = FakeRepo([("b", "3"), ("c", "4")]) # Act - result = repoinfo.compare_repo(a, b, available_in_both=False) # type: ignore + result = repoinfo.compare_repo(a, b, available_in_both=False) # Assert expected = { @@ -111,12 +120,12 @@ def test_compare_repo_intersection_only() -> None: When available_in_both=True, only intersection names are considered; differences are only returned if versions differ. """ - # Arrange (both share 'b' with different versions) - a = SimpleNamespace(get_packages_versions=lambda: [("a", "1"), ("b", "2")]) - b = SimpleNamespace(get_packages_versions=lambda: [("b", "3"), ("c", "4")]) + # Arrange + a = FakeRepo([("a", "1"), ("b", "2")]) + b = FakeRepo([("b", "3"), ("c", "4")]) # Act - result = repoinfo.compare_repo(a, b, available_in_both=True) # type: ignore + result = repoinfo.compare_repo(a, b, available_in_both=True) # Assert assert set(result) == {("b", "2", "3")} @@ -127,11 +136,11 @@ def test_compare_same_returns_empty() -> None: When both sets are identical, compare_repo should return an empty set. """ # Arrange - a = SimpleNamespace(get_packages_versions=lambda: [("a", "1"), ("b", "2")]) - b = SimpleNamespace(get_packages_versions=lambda: [("a", "1"), ("b", "2")]) + a = FakeRepo([("a", "1"), ("b", "2")]) + b = FakeRepo([("a", "1"), ("b", "2")]) # Act / Assert - assert repoinfo.compare_repo(a, b, available_in_both=False) == [] # type: ignore + assert repoinfo.compare_repo(a, b, available_in_both=False) == [] def test_compare_empty_returns_empty() -> None: @@ -139,8 +148,8 @@ def test_compare_empty_returns_empty() -> None: If both sets are empty, compare_repo should return an empty set. """ # Arrange - a = SimpleNamespace(get_packages_versions=lambda: []) - b = SimpleNamespace(get_packages_versions=lambda: []) + a = FakeRepo([]) + b = FakeRepo([]) # Act / Assert - assert repoinfo.compare_repo(a, b, available_in_both=True) == [] # type: ignore + assert repoinfo.compare_repo(a, b, available_in_both=True) == [] diff --git a/tests/conftest.py b/tests/conftest.py index 6892fb46..093ad3dc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -129,7 +129,7 @@ def write_zot_config(config_dict: Dict[str, Any], fd: int) -> None: json.dump(config_dict, fp, indent=4) -@pytest.fixture(autouse=False, scope="function") # type: ignore[misc] +@pytest.fixture(autouse=False, scope="function") def zot_session() -> Generator[subprocess.Popen[Any]]: load_dotenv() print("start zot session") diff --git a/tests/distro_version/test_distro_version.py b/tests/distro_version/test_distro_version.py index 7ab59861..8daee9c8 100644 --- a/tests/distro_version/test_distro_version.py +++ b/tests/distro_version/test_distro_version.py @@ -9,39 +9,39 @@ ) -def test_distro_version_unrecognizable_non_numeric_version(): +def test_distro_version_unrecognizable_non_numeric_version() -> None: with pytest.raises(UnsupportedDistroVersion): DistroVersion("garden.linux") -def test_distro_version_unrecognizable_numeric_version(): +def test_distro_version_unrecognizable_numeric_version() -> None: with pytest.raises(UnsupportedDistroVersion): DistroVersion("1.2.3.4") with pytest.raises(UnsupportedDistroVersion): DistroVersion("1.100.-10") -def test_distro_version_unrecognizable_too_short_version(): +def test_distro_version_unrecognizable_too_short_version() -> None: with pytest.raises(UnsupportedDistroVersion): DistroVersion("1") -def test_distro_version_legacy_version_is_parsable(): +def test_distro_version_legacy_version_is_parsable() -> None: assert isinstance(DistroVersion("1.2"), LegacyDistroVersion) -def test_distro_version_semver_version_is_parsable(): +def test_distro_version_semver_version_is_parsable() -> None: assert isinstance(DistroVersion("1.2.3"), SemverDistroVersion) -def test_distro_version_patch_release_is_recognized(): +def test_distro_version_patch_release_is_recognized() -> None: assert DistroVersion("1.1").is_patch_release() assert DistroVersion("1.1.100").is_patch_release() assert not DistroVersion("1.0").is_patch_release() assert not DistroVersion("1.0.0").is_patch_release() -def test_distro_version_previous_patch_release_is_recognized(): +def test_distro_version_previous_patch_release_is_recognized() -> None: assert DistroVersion("1.1").previous_patch_release().__str__() == "1.0" assert DistroVersion("1.1.100").previous_patch_release().__str__() == "1.1.99" with pytest.raises(NotAPatchRelease): diff --git a/tests/features/test_cname.py b/tests/features/test_cname.py index 9d70e2ce..3d173844 100644 --- a/tests/features/test_cname.py +++ b/tests/features/test_cname.py @@ -3,7 +3,7 @@ from gardenlinux.features import CName -@pytest.mark.parametrize( # type: ignore[misc] +@pytest.mark.parametrize( "input_cname, expected_output", [ ( diff --git a/tests/features/test_cname_main.py b/tests/features/test_cname_main.py index d831225a..5a40c5fb 100644 --- a/tests/features/test_cname_main.py +++ b/tests/features/test_cname_main.py @@ -11,7 +11,7 @@ def test_main_happy( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: """ Test the "Happy Path" of the main() function. @@ -49,7 +49,7 @@ def sort_graph_nodes(graph: networkx.Graph) -> List[str]: def test_main_version_from_file( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: """ "Happy Path" test for grabbing the version and commit id from file in main(). diff --git a/tests/features/test_main.py b/tests/features/test_main.py index dcebd537..a0df4326 100644 --- a/tests/features/test_main.py +++ b/tests/features/test_main.py @@ -88,7 +88,7 @@ def test_get_version_missing_file_raises(tmp_path: Path) -> None: # Tests for main() # ------------------------------- def test_main_prints_arch( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = ["prog", "--arch", "amd64", "--cname", "flav", "--version", "1.0", "arch"] @@ -104,7 +104,7 @@ def test_main_prints_arch( def test_main_prints_container_name( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = [ @@ -129,7 +129,7 @@ def test_main_prints_container_name( def test_main_prints_container_tag( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = [ @@ -156,7 +156,7 @@ def test_main_prints_container_tag( def test_main_prints_commit_id( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = ["prog", "--arch", "amd64", "--cname", "flav", "commit_id"] @@ -179,7 +179,7 @@ def test_main_prints_commit_id( def test_main_prints_flags_elements_platforms( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = [ @@ -210,7 +210,7 @@ def __init__(self, *a: Any, **k: Any): def test_main_prints_version( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = ["prog", "--arch", "amd64", "--cname", "flav", "version"] @@ -233,7 +233,7 @@ def test_main_prints_version( def test_main_prints_version_and_commit_id( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = ["prog", "--arch", "amd64", "--cname", "flav", "version_and_commit_id"] @@ -297,7 +297,7 @@ def test_main_raises_no_arch_no_default(monkeypatch: pytest.MonkeyPatch) -> None def test_main_raises_missing_commit_id( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange argv = [ @@ -319,7 +319,7 @@ def test_main_raises_missing_commit_id( def test_main_with_exclude_cname_print_elements( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange monkeypatch.setattr( @@ -353,7 +353,7 @@ def test_main_with_exclude_cname_print_elements( def test_main_with_exclude_cname_print_features( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: # Arrange monkeypatch.setattr( @@ -390,7 +390,7 @@ def test_main_with_exclude_cname_print_features( def test_cname_release_file( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: """ Test validation between release metadata and arguments given diff --git a/tests/features/test_metadata_main.py b/tests/features/test_metadata_main.py index fb9c3b8f..b8f0fc69 100644 --- a/tests/features/test_metadata_main.py +++ b/tests/features/test_metadata_main.py @@ -10,7 +10,7 @@ def test_main_output( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: """ Test successful "output-release-metadata" @@ -37,7 +37,7 @@ def test_main_output( def test_main_write( - monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: """ Test successful "write" diff --git a/tests/features/test_parser.py b/tests/features/test_parser.py index 67e277ab..f58cbb99 100644 --- a/tests/features/test_parser.py +++ b/tests/features/test_parser.py @@ -7,7 +7,7 @@ from ..constants import GL_ROOT_DIR -@pytest.mark.parametrize( # type: ignore[misc] +@pytest.mark.parametrize( "input_cname, expected_output", [ ( diff --git a/tests/flavors/test_parser.py b/tests/flavors/test_parser.py index 86e94dd5..26196900 100644 --- a/tests/flavors/test_parser.py +++ b/tests/flavors/test_parser.py @@ -6,7 +6,7 @@ from gardenlinux.flavors.parser import Parser -@pytest.fixture # type: ignore[misc] +@pytest.fixture def valid_data() -> Dict[str, Any]: """Minimal data for valid GL_FLAVORS_SCHEMA.""" return { @@ -97,7 +97,7 @@ def test_filter_category_and_exclude(valid_data: str) -> None: assert all("android" in name for _, name in android_combos) -@pytest.mark.parametrize("flag", ["only_build", "only_test", "only_publish"]) # type: ignore[misc] +@pytest.mark.parametrize("flag", ["only_build", "only_test", "only_publish"]) def test_filter_with_flags(valid_data: str, flag: str) -> None: # Arrange parser = make_parser(valid_data) diff --git a/tests/github/conftest.py b/tests/github/conftest.py index d15d4f3d..114adcfc 100644 --- a/tests/github/conftest.py +++ b/tests/github/conftest.py @@ -1,6 +1,7 @@ import os import shutil from pathlib import Path +from typing import Any, Generator, List import boto3 import pytest @@ -12,21 +13,21 @@ @pytest.fixture -def downloads_dir(): +def downloads_dir() -> Generator[None, None, None]: os.makedirs(S3_DOWNLOADS_DIR, exist_ok=True) yield shutil.rmtree(S3_DOWNLOADS_DIR) @pytest.fixture -def github_token(): +def github_token() -> Generator[None, None, None]: os.environ["GITHUB_TOKEN"] = "foobarbazquux" yield del os.environ["GITHUB_TOKEN"] @pytest.fixture -def artifact_for_upload(downloads_dir): +def artifact_for_upload(downloads_dir: None) -> Generator[Path, None, None]: artifact = S3_DOWNLOADS_DIR / "artifact.log" artifact.touch() yield artifact @@ -34,14 +35,14 @@ def artifact_for_upload(downloads_dir): @pytest.fixture -def release_id_file(): +def release_id_file() -> Generator[Path, None, None]: f = Path(RELEASE_ID_FILE) yield f f.unlink() @pytest.fixture -def release_s3_bucket(): +def release_s3_bucket() -> Generator[Any, None, None]: with mock_aws(): s3 = boto3.resource("s3", region_name="eu-central-1") s3.create_bucket( # pyright: ignore[reportAttributeAccessIssue] @@ -54,30 +55,30 @@ def release_s3_bucket(): @pytest.fixture -def blackhole_s3_bucket(): +def blackhole_s3_bucket() -> Generator[Any, None, None]: """This fixture yields an object that behaves as an S3 bucket where any object can be found, but downloading a file always raises an exception. This is needed to test the retry mechanism as the object also counts how many times an exception was raised.""" class BlackHoleObject: - def __init__(self, bucket_name, key): + def __init__(self, bucket_name: str, key: str) -> None: self.bucket_name = bucket_name self.key = key class BlackHoleObjects: - def __init__(self, bucket_name): + def __init__(self, bucket_name: str) -> None: self.bucket_name = bucket_name - def filter(self, Prefix): + def filter(self, Prefix: str) -> List[BlackHoleObject]: return [BlackHoleObject(self.bucket_name, Prefix)] class BlackHoleS3Bucket: - def __init__(self, bucket_name): + def __init__(self, bucket_name: str) -> None: self.objects = BlackHoleObjects(bucket_name) self.download_attempts = 0 - def download_file(self, x, y): + def download_file(self, x: str, y: str) -> None: self.download_attempts += 1 raise IOError(f"Download attempt # {self.download_attempts} failed") @@ -85,5 +86,5 @@ def download_file(self, x, y): @pytest.fixture -def metadata_file(): +def metadata_file() -> None: pass diff --git a/tests/github/test_create_github_release.py b/tests/github/test_create_github_release.py index 23af3492..b702183d 100644 --- a/tests/github/test_create_github_release.py +++ b/tests/github/test_create_github_release.py @@ -1,16 +1,15 @@ +from pathlib import Path + import pytest import requests import requests_mock from gardenlinux.github.release import create_github_release, write_to_release_id_file -from ..constants import ( - TEST_GARDENLINUX_COMMIT, - TEST_GARDENLINUX_RELEASE, -) +from ..constants import TEST_GARDENLINUX_COMMIT, TEST_GARDENLINUX_RELEASE -def test_create_github_release_needs_github_token(): +def test_create_github_release_needs_github_token() -> None: with requests_mock.Mocker(): with pytest.raises(ValueError) as exn: create_github_release( @@ -26,7 +25,9 @@ def test_create_github_release_needs_github_token(): ) -def test_create_github_release_raise_on_failure(caplog, github_token): +def test_create_github_release_raise_on_failure( + caplog: pytest.LogCaptureFixture, github_token: None +) -> None: with requests_mock.Mocker() as m: with pytest.raises(requests.exceptions.HTTPError): m.post( @@ -47,7 +48,9 @@ def test_create_github_release_raise_on_failure(caplog, github_token): ), "Expected a failure log record" -def test_create_github_release(caplog, github_token): +def test_create_github_release( + caplog: pytest.LogCaptureFixture, github_token: None +) -> None: with requests_mock.Mocker() as m: m.post( "https://api.github.com/repos/gardenlinux/gardenlinux/releases", @@ -71,12 +74,14 @@ def test_create_github_release(caplog, github_token): ), "Expected a success log record" -def test_write_to_release_id_file(release_id_file): +def test_write_to_release_id_file(release_id_file: Path) -> None: write_to_release_id_file(TEST_GARDENLINUX_RELEASE) assert release_id_file.read_text() == TEST_GARDENLINUX_RELEASE -def test_write_to_release_id_file_broken_file_permissions(release_id_file, caplog): +def test_write_to_release_id_file_broken_file_permissions( + release_id_file: Path, caplog: pytest.LogCaptureFixture +) -> None: release_id_file.touch(0) # this will make the file unwritable with pytest.raises(SystemExit): diff --git a/tests/github/test_create_github_release_notes.py b/tests/github/test_create_github_release_notes.py index 55b08a73..71b4af7f 100644 --- a/tests/github/test_create_github_release_notes.py +++ b/tests/github/test_create_github_release_notes.py @@ -5,12 +5,13 @@ from gardenlinux.constants import GLVD_BASE_URL from gardenlinux.distro_version import UnsupportedDistroVersion -from gardenlinux.github.release_notes import ( - release_notes_changes_section, +from gardenlinux.github.release_notes import release_notes_changes_section # type: ignore[attr-defined] +from gardenlinux.github.release_notes import ( # type: ignore[attr-defined] release_notes_compare_package_versions_section, ) from gardenlinux.github.release_notes.deployment_platform import DeploymentPlatform from gardenlinux.github.release_notes.helpers import get_variant_from_flavor +from gardenlinux.s3.bucket import Bucket from ..constants import ( RELEASE_ARTIFACTS_METADATA_FILES, @@ -29,7 +30,7 @@ ] -def test_release_notes_changes_section_empty_packagelist(): +def test_release_notes_changes_section_empty_packagelist() -> None: with requests_mock.Mocker() as m: m.get( f"{GLVD_BASE_URL}/releaseNotes/{TEST_GARDENLINUX_RELEASE}", @@ -41,7 +42,7 @@ def test_release_notes_changes_section_empty_packagelist(): ) -def test_release_notes_changes_section_broken_glvd_response(): +def test_release_notes_changes_section_broken_glvd_response() -> None: with requests_mock.Mocker() as m: m.get( f"{GLVD_BASE_URL}/releaseNotes/{TEST_GARDENLINUX_RELEASE}", @@ -55,7 +56,9 @@ def test_release_notes_changes_section_broken_glvd_response(): ) -def test_release_notes_compare_package_versions_section_legacy_versioning_is_recognized(): +def test_release_notes_compare_package_versions_section_legacy_versioning_is_recognized() -> ( + None +): assert ( "Full List of Packages in Garden Linux version 1.0" in release_notes_compare_package_versions_section("1.0", {}) @@ -63,9 +66,11 @@ def test_release_notes_compare_package_versions_section_legacy_versioning_is_rec def test_release_notes_compare_package_versions_section_legacy_versioning_patch_release_is_recognized( - monkeypatch, -): - def mock_compare_apt_repo_versions(previous_version, current_version): + monkeypatch: pytest.MonkeyPatch, +) -> None: + def mock_compare_apt_repo_versions( + previous_version: str, current_version: str + ) -> str: output = f"| Package | {previous_version} | {current_version} |\n" output += "|---------|--------------------|-------------------|\n" output += "|containerd|1.0|1.1|\n" @@ -81,7 +86,7 @@ def mock_compare_apt_repo_versions(previous_version, current_version): ), "Legacy versioning patch releases are supported" -def test_release_notes_compare_package_versions_section_semver_is_recognized(): +def test_release_notes_compare_package_versions_section_semver_is_recognized() -> None: assert ( "Full List of Packages in Garden Linux version 1.20.0" in release_notes_compare_package_versions_section("1.20.0", {}) @@ -89,9 +94,11 @@ def test_release_notes_compare_package_versions_section_semver_is_recognized(): def test_release_notes_compare_package_versions_section_semver_patch_release_is_recognized( - monkeypatch, -): - def mock_compare_apt_repo_versions(previous_version, current_version): + monkeypatch: pytest.MonkeyPatch, +) -> None: + def mock_compare_apt_repo_versions( + previous_version: str, current_version: str + ) -> str: output = f"| Package | {previous_version} | {current_version} |\n" output += "|---------|--------------------|-------------------|\n" output += "|containerd|1.0|1.1|\n" @@ -107,26 +114,30 @@ def mock_compare_apt_repo_versions(previous_version, current_version): ), "Semver patch releases are supported" -def test_release_notes_compare_package_versions_section_unrecognizable_version(): +def test_release_notes_compare_package_versions_section_unrecognizable_version() -> ( + None +): with pytest.raises(UnsupportedDistroVersion): release_notes_compare_package_versions_section("garden.linux", {}) @pytest.mark.parametrize("flavor", TEST_FLAVORS) -def test_get_variant_from_flavor(flavor): +def test_get_variant_from_flavor(flavor: str) -> None: assert get_variant_from_flavor(flavor[0]) == flavor[1] -def test_default_get_file_extension_for_deployment_platform(): +def test_default_get_file_extension_for_deployment_platform() -> None: assert DeploymentPlatform().image_extension() == "raw" @mock_aws -def test_github_release_page(monkeypatch, downloads_dir, release_s3_bucket): - class SubmoduleAsRepo(Repo): +def test_github_release_page( + monkeypatch: pytest.MonkeyPatch, downloads_dir: None, release_s3_bucket: Bucket +) -> None: + class SubmoduleAsRepo(Repo): # type: ignore[misc] """This will fake a git submodule as a git repository object.""" - def __new__(cls, *args, **kwargs): + def __new__(cls, *args, **kwargs): # type: ignore[no-untyped-def] r = super().__new__(Repo) # pyright: ignore[reportArgumentType] r.__init__(*args, **kwargs) diff --git a/tests/github/test_download_metadata_files.py b/tests/github/test_download_metadata_files.py index 98cf62ba..8138929e 100644 --- a/tests/github/test_download_metadata_files.py +++ b/tests/github/test_download_metadata_files.py @@ -3,7 +3,7 @@ from gardenlinux.constants import S3_DOWNLOADS_DIR from gardenlinux.features import CName from gardenlinux.github.release_notes.helpers import download_metadata_file -from gardenlinux.s3 import S3Artifacts +from gardenlinux.s3 import Bucket, S3Artifacts from ..constants import ( RELEASE_NOTES_S3_ARTIFACTS_DIR, @@ -13,9 +13,11 @@ ) -def test_download_metadata_file(downloads_dir, release_s3_bucket): +def test_download_metadata_file(downloads_dir: None, release_s3_bucket: Bucket) -> None: release_s3_bucket.upload_file( - RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml", + str( + RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml" + ), f"meta/singles/test-aws-gardener_prod-amd64-{TEST_GARDENLINUX_RELEASE}-{TEST_GARDENLINUX_COMMIT_SHORT}", ) @@ -33,9 +35,13 @@ def test_download_metadata_file(downloads_dir, release_s3_bucket): assert (S3_DOWNLOADS_DIR / "test-aws-gardener_prod-amd64.s3_metadata.yaml").exists() -def test_download_metadata_file_no_such_release(downloads_dir, release_s3_bucket): +def test_download_metadata_file_no_such_release( + downloads_dir: None, release_s3_bucket: Bucket +) -> None: release_s3_bucket.upload_file( - RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml", + str( + RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml" + ), f"meta/singles/test-aws-gardener_prod-amd64-{TEST_GARDENLINUX_RELEASE}-{TEST_GARDENLINUX_COMMIT_SHORT}", ) s3_artifacts = S3Artifacts(TEST_GARDENLINUX_RELEASE_BUCKET_NAME) @@ -58,9 +64,13 @@ def test_download_metadata_file_no_such_release(downloads_dir, release_s3_bucket ).exists() -def test_download_metadata_file_no_such_commit(downloads_dir, release_s3_bucket): +def test_download_metadata_file_no_such_commit( + downloads_dir: None, release_s3_bucket: Bucket +) -> None: release_s3_bucket.upload_file( - RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml", + str( + RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml" + ), f"meta/singles/test-aws-gardener_prod-amd64-{TEST_GARDENLINUX_RELEASE}-{TEST_GARDENLINUX_COMMIT_SHORT}", ) @@ -85,10 +95,12 @@ def test_download_metadata_file_no_such_commit(downloads_dir, release_s3_bucket) def test_download_metadata_file_no_such_release_and_commit( - downloads_dir, release_s3_bucket -): + downloads_dir: None, release_s3_bucket: Bucket +) -> None: release_s3_bucket.upload_file( - RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml", + str( + RELEASE_NOTES_S3_ARTIFACTS_DIR / "aws-gardener_prod-amd64.s3_metadata.yaml" + ), f"meta/singles/test-aws-gardener_prod-amd64-{TEST_GARDENLINUX_RELEASE}-{TEST_GARDENLINUX_COMMIT_SHORT}", ) diff --git a/tests/github/test_github_script.py b/tests/github/test_github_script.py index 907e0da5..cc10b83e 100644 --- a/tests/github/test_github_script.py +++ b/tests/github/test_github_script.py @@ -8,7 +8,9 @@ from ..constants import TEST_GARDENLINUX_COMMIT, TEST_GARDENLINUX_RELEASE -def test_script_parse_args_wrong_command(monkeypatch, capfd): +def test_script_parse_args_wrong_command( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr(sys, "argv", ["gh", "rejoice"]) with pytest.raises(SystemExit): @@ -20,7 +22,9 @@ def test_script_parse_args_wrong_command(monkeypatch, capfd): ) -def test_script_parse_args_create_command_required_args(monkeypatch, capfd): +def test_script_parse_args_create_command_required_args( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr( sys, "argv", ["gh", "create", "--owner", "gardenlinux", "--repo", "gardenlinux"] ) @@ -34,7 +38,9 @@ def test_script_parse_args_create_command_required_args(monkeypatch, capfd): ) -def test_script_parse_args_upload_command_required_args(monkeypatch, capfd): +def test_script_parse_args_upload_command_required_args( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr( sys, "argv", ["gh", "upload", "--owner", "gardenlinux", "--repo", "gardenlinux"] ) @@ -49,7 +55,9 @@ def test_script_parse_args_upload_command_required_args(monkeypatch, capfd): ), "Expected help message on missing arguments for 'upload' command" -def test_script_create_dry_run(monkeypatch, capfd): +def test_script_create_dry_run( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr( sys, "argv", @@ -81,7 +89,9 @@ def test_script_create_dry_run(monkeypatch, capfd): ), "Expected dry-run create to return generated release notes text" -def test_script_create(monkeypatch, caplog): +def test_script_create( + monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture +) -> None: monkeypatch.setattr( sys, "argv", @@ -115,7 +125,9 @@ def test_script_create(monkeypatch, caplog): ), "Expected a release creation confirmation log entry" -def test_script_upload_dry_run(monkeypatch, capfd): +def test_script_upload_dry_run( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr( sys, "argv", diff --git a/tests/github/test_upload_to_github_release_page.py b/tests/github/test_upload_to_github_release_page.py index a62342f9..11d3b976 100644 --- a/tests/github/test_upload_to_github_release_page.py +++ b/tests/github/test_upload_to_github_release_page.py @@ -1,4 +1,5 @@ import sys +from pathlib import Path import pytest import requests @@ -10,14 +11,16 @@ from ..constants import TEST_GARDENLINUX_RELEASE -def test_upload_to_github_release_page_dryrun(caplog, artifact_for_upload): +def test_upload_to_github_release_page_dryrun( + caplog: pytest.LogCaptureFixture, artifact_for_upload: Path +) -> None: with requests_mock.Mocker(): assert ( - upload_to_github_release_page( + upload_to_github_release_page( # type: ignore[func-returns-value] "gardenlinux", "gardenlinux", TEST_GARDENLINUX_RELEASE, - artifact_for_upload, + str(artifact_for_upload), dry_run=True, ) is None @@ -28,15 +31,15 @@ def test_upload_to_github_release_page_dryrun(caplog, artifact_for_upload): def test_upload_to_github_release_page_needs_github_token( - downloads_dir, artifact_for_upload -): + downloads_dir: None, artifact_for_upload: Path +) -> None: with requests_mock.Mocker(): with pytest.raises(ValueError) as exn: upload_to_github_release_page( "gardenlinux", "gardenlinux", TEST_GARDENLINUX_RELEASE, - artifact_for_upload, + str(artifact_for_upload), dry_run=False, ) assert str(exn.value) == "GITHUB_TOKEN environment variable not set", ( @@ -45,8 +48,11 @@ def test_upload_to_github_release_page_needs_github_token( def test_upload_to_github_release_page( - downloads_dir, caplog, github_token, artifact_for_upload -): + downloads_dir: None, + caplog: pytest.LogCaptureFixture, + github_token: None, + artifact_for_upload: Path, +) -> None: with requests_mock.Mocker(real_http=True) as m: m.post( f"https://uploads.github.com/repos/gardenlinux/gardenlinux/releases/{TEST_GARDENLINUX_RELEASE}/assets?name=artifact.log", @@ -58,7 +64,7 @@ def test_upload_to_github_release_page( "gardenlinux", "gardenlinux", TEST_GARDENLINUX_RELEASE, - artifact_for_upload, + str(artifact_for_upload), dry_run=False, ) assert any( @@ -67,15 +73,18 @@ def test_upload_to_github_release_page( def test_upload_to_github_release_page_unreadable_artifact( - downloads_dir, caplog, github_token, artifact_for_upload -): + downloads_dir: None, + caplog: pytest.LogCaptureFixture, + github_token: None, + artifact_for_upload: Path, +) -> None: artifact_for_upload.chmod(0) upload_to_github_release_page( "gardenlinux", "gardenlinux", TEST_GARDENLINUX_RELEASE, - artifact_for_upload, + str(artifact_for_upload), dry_run=False, ) assert any("Error reading file" in record.message for record in caplog.records), ( @@ -84,8 +93,11 @@ def test_upload_to_github_release_page_unreadable_artifact( def test_upload_to_github_release_page_failed( - downloads_dir, caplog, github_token, artifact_for_upload -): + downloads_dir: None, + caplog: pytest.LogCaptureFixture, + github_token: None, + artifact_for_upload: Path, +) -> None: with requests_mock.Mocker(real_http=True) as m: m.post( f"https://uploads.github.com/repos/gardenlinux/gardenlinux/releases/{TEST_GARDENLINUX_RELEASE}/assets?name=artifact.log", @@ -98,7 +110,7 @@ def test_upload_to_github_release_page_failed( "gardenlinux", "gardenlinux", TEST_GARDENLINUX_RELEASE, - artifact_for_upload, + str(artifact_for_upload), dry_run=False, ) assert any( @@ -107,7 +119,9 @@ def test_upload_to_github_release_page_failed( ), "Expected an error HTTP status code to be logged" -def test_script_parse_args_wrong_command(monkeypatch, capfd): +def test_script_parse_args_wrong_command( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr(sys, "argv", ["gh", "rejoice"]) with pytest.raises(SystemExit): @@ -119,7 +133,9 @@ def test_script_parse_args_wrong_command(monkeypatch, capfd): ) -def test_script_parse_args_upload_command_required_args(monkeypatch, capfd): +def test_script_parse_args_upload_command_required_args( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr( sys, "argv", ["gh", "upload", "--owner", "gardenlinux", "--repo", "gardenlinux"] ) @@ -134,7 +150,9 @@ def test_script_parse_args_upload_command_required_args(monkeypatch, capfd): ), "Expected help message on missing arguments for 'upload' command" -def test_script_upload_dry_run(monkeypatch, capfd): +def test_script_upload_dry_run( + monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr( sys, "argv", diff --git a/tests/oci/test_container.py b/tests/oci/test_container.py index 55c9b8bf..deecdc61 100644 --- a/tests/oci/test_container.py +++ b/tests/oci/test_container.py @@ -7,15 +7,10 @@ from gardenlinux.oci import Container -from ..constants import ( - CONTAINER_NAME_ZOT_EXAMPLE, - REGISTRY, - TEST_COMMIT, - TEST_VERSION, -) +from ..constants import CONTAINER_NAME_ZOT_EXAMPLE, REGISTRY, TEST_COMMIT, TEST_VERSION -@pytest.fixture(name="Container_login_403") # type: ignore[misc] +@pytest.fixture(name="Container_login_403") def patch__Container_login_403(monkeypatch: pytest.MonkeyPatch) -> None: """Patch `login()` to return HTTP 403. `docker.errors.APIError` extends from `requests.exceptions.HTTPError` as well.""" @@ -27,7 +22,7 @@ def login_403(*args: Any, **kwargs: Any) -> None: monkeypatch.setattr(Container, "login", login_403) -@pytest.fixture(name="Container_read_or_generate_403") # type: ignore[misc] +@pytest.fixture(name="Container_read_or_generate_403") def patch__Container_read_or_generate_403(monkeypatch: pytest.MonkeyPatch) -> None: """Patch `read_or_generate_manifest()` to return HTTP 403.""" @@ -39,7 +34,7 @@ def read_or_generate_403(*args: Any, **kwargs: Any) -> None: monkeypatch.setattr(Container, "read_or_generate_manifest", read_or_generate_403) -@pytest.mark.usefixtures("zot_session") # type: ignore[misc] +@pytest.mark.usefixtures("zot_session") def test_manifest() -> None: """Verify a newly created manifest returns correct commit value.""" # Arrange @@ -53,8 +48,8 @@ def test_manifest() -> None: assert manifest.commit == TEST_COMMIT -@pytest.mark.usefixtures("zot_session") # type: ignore[misc] -@pytest.mark.usefixtures("Container_read_or_generate_403") # type: ignore[misc] +@pytest.mark.usefixtures("zot_session") +@pytest.mark.usefixtures("Container_read_or_generate_403") def test_manifest_403() -> None: """Verify container calls raises exceptions for certain errors.""" # Arrange @@ -64,7 +59,7 @@ def test_manifest_403() -> None: container.read_or_generate_manifest(version=TEST_VERSION, commit=TEST_COMMIT) -@pytest.mark.usefixtures("zot_session") # type: ignore[misc] +@pytest.mark.usefixtures("zot_session") def test_manifest_auth_token( monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ) -> None: @@ -82,8 +77,8 @@ def test_manifest_auth_token( assert container.auth.token == b64encode(bytes(token, "utf-8")).decode("utf-8") -@pytest.mark.usefixtures("zot_session") # type: ignore[misc] -@pytest.mark.usefixtures("Container_login_403") # type: ignore[misc] +@pytest.mark.usefixtures("zot_session") +@pytest.mark.usefixtures("Container_login_403") def test_manifest_login_username_password( monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ) -> None: diff --git a/tests/oci/test_index.py b/tests/oci/test_index.py index dbdfe083..36a694e3 100644 --- a/tests/oci/test_index.py +++ b/tests/oci/test_index.py @@ -77,7 +77,7 @@ def test_append_manifest_cname_not_found() -> None: "not-a-dict", {"annotations": {}}, ], -) # type: ignore[misc] +) def test_append_invalid_input_raises(bad_manifest: Dict[str, Any]) -> None: """Test proper error handling for invalid append_manifest input.""" # Arrange diff --git a/tests/oci/test_layer.py b/tests/oci/test_layer.py index 717b02d7..7b1795d5 100644 --- a/tests/oci/test_layer.py +++ b/tests/oci/test_layer.py @@ -18,7 +18,7 @@ def to_dict(self) -> Dict[str, Any]: return {"dummy": True} -@pytest.fixture(autouse=True) # type: ignore[misc] +@pytest.fixture(autouse=True) def patch__Layer(monkeypatch: pytest.MonkeyPatch) -> Generator[None]: """Replace oras.oci.Layer with DummyLayer in Layer's module.""" monkeypatch.setattr(gl_layer, "_Layer", DummyLayer) diff --git a/tests/oci/test_oci.py b/tests/oci/test_oci.py index 00568a0f..52a58b54 100644 --- a/tests/oci/test_oci.py +++ b/tests/oci/test_oci.py @@ -171,7 +171,9 @@ def get_tags(client: OrasClient, repo: str) -> List[str]: return tags_json.get("tags", []) # type: ignore[no-any-return] -def get_manifest(client: OrasClient, repo: str, reference: str) -> Tuple[Any, str]: +def get_manifest( + client: OrasClient, repo: str, reference: str +) -> Tuple[Any, str | None]: """Get manifest and digest for a repository reference""" # Create a simple request for the manifest manifest_resp = client.do_request( @@ -305,8 +307,8 @@ def verify_additional_tags( return missing_tags -@pytest.mark.usefixtures("zot_session") # type: ignore[misc] -@pytest.mark.parametrize( # type: ignore[misc] +@pytest.mark.usefixtures("zot_session") +@pytest.mark.parametrize( "version, cname, arch, additional_tags_index, additional_tags_manifest", [ ( diff --git a/tests/s3/conftest.py b/tests/s3/conftest.py index 0dbaac63..50fa01b4 100644 --- a/tests/s3/conftest.py +++ b/tests/s3/conftest.py @@ -9,6 +9,7 @@ import boto3 import pytest from moto import mock_aws +from mypy_boto3_s3.service_resource import S3ServiceResource BUCKET_NAME = "test-bucket" REGION = "us-east-1" @@ -16,7 +17,7 @@ @dataclass(frozen=True) class S3Env: - s3: boto3.client + s3: S3ServiceResource bucket_name: str tmp_path: Path cname: str @@ -50,8 +51,10 @@ def dummy_digest(data: BytesIO, algo: str) -> Any: raise ValueError(f"Unsupported algo: {algo}") -@pytest.fixture(autouse=True) # type: ignore[misc] -def s3_setup(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Generator[Any]: +@pytest.fixture(autouse=True) +def s3_setup( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> Generator[S3Env, None, None]: """ Provides a clean S3 setup for each test. """ diff --git a/tests/s3/test_main.py b/tests/s3/test_main.py index d0c109fd..e1b44c69 100644 --- a/tests/s3/test_main.py +++ b/tests/s3/test_main.py @@ -7,7 +7,7 @@ import gardenlinux.s3.__main__ as s3m -@pytest.mark.parametrize( # type: ignore[misc] +@pytest.mark.parametrize( "argv, expected_method, expected_args, expected_kwargs", [ (