mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	mypy: Add boto3-stubs.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							bfdb2f4628
						
					
				
				
					commit
					1bdb7b1141
				
			@@ -51,8 +51,6 @@ module = [
 | 
				
			|||||||
    "aioapns.*",
 | 
					    "aioapns.*",
 | 
				
			||||||
    "bitfield.*",
 | 
					    "bitfield.*",
 | 
				
			||||||
    "bmemcached.*",
 | 
					    "bmemcached.*",
 | 
				
			||||||
    "boto3.*",
 | 
					 | 
				
			||||||
    "botocore.*",
 | 
					 | 
				
			||||||
    "bs4.*",
 | 
					    "bs4.*",
 | 
				
			||||||
    "bson.*",
 | 
					    "bson.*",
 | 
				
			||||||
    "cairosvg.*",
 | 
					    "cairosvg.*",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,6 +36,7 @@ SQLAlchemy==1.3.*  # 1.4 has badly busted type annotations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Needed for S3 file uploads
 | 
					# Needed for S3 file uploads
 | 
				
			||||||
boto3
 | 
					boto3
 | 
				
			||||||
 | 
					mypy-boto3-s3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Needed for integrations
 | 
					# Needed for integrations
 | 
				
			||||||
defusedxml
 | 
					defusedxml
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,10 @@ boto3==1.17.105 \
 | 
				
			|||||||
    # via
 | 
					    # via
 | 
				
			||||||
    #   -r requirements/common.in
 | 
					    #   -r requirements/common.in
 | 
				
			||||||
    #   moto
 | 
					    #   moto
 | 
				
			||||||
 | 
					boto3-stubs[s3]==1.18.17 \
 | 
				
			||||||
 | 
					    --hash=sha256:97aef3a2173bedd95b75aafaa1a6a85e321af107a11855f30afded7a1e2462bc \
 | 
				
			||||||
 | 
					    --hash=sha256:b5bd2f3f54f06eecb9f6a085643c4c02a057c5fbbad4256c4e4a02a0744758df
 | 
				
			||||||
 | 
					    # via -r requirements/mypy.in
 | 
				
			||||||
botocore==1.20.105 \
 | 
					botocore==1.20.105 \
 | 
				
			||||||
    --hash=sha256:b0fda4edf8eb105453890700d49011ada576d0cc7326a0699dfabe9e872f552c \
 | 
					    --hash=sha256:b0fda4edf8eb105453890700d49011ada576d0cc7326a0699dfabe9e872f552c \
 | 
				
			||||||
    --hash=sha256:b5ba72d22212b0355f339c2a98b3296b3b2202a48e6a2b1366e866bc65a64b67
 | 
					    --hash=sha256:b5ba72d22212b0355f339c2a98b3296b3b2202a48e6a2b1366e866bc65a64b67
 | 
				
			||||||
@@ -108,6 +112,10 @@ botocore==1.20.105 \
 | 
				
			|||||||
    #   boto3
 | 
					    #   boto3
 | 
				
			||||||
    #   moto
 | 
					    #   moto
 | 
				
			||||||
    #   s3transfer
 | 
					    #   s3transfer
 | 
				
			||||||
 | 
					botocore-stubs==1.21.17 \
 | 
				
			||||||
 | 
					    --hash=sha256:6fca2ff326532e8ad8b74c1e5ef6e0457f409ebe38cb8b4aa6cb50b534ee3ee3 \
 | 
				
			||||||
 | 
					    --hash=sha256:b754cb23471948b8cbe50d24d4a74c617672905e4ae170b01bcfcae6496d7cb6
 | 
				
			||||||
 | 
					    # via boto3-stubs
 | 
				
			||||||
cachetools==4.2.2 \
 | 
					cachetools==4.2.2 \
 | 
				
			||||||
    --hash=sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001 \
 | 
					    --hash=sha256:2cc0b89715337ab6dbba85b5b50effe2b0c74e035d83ee8ed637cf52f12ae001 \
 | 
				
			||||||
    --hash=sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff
 | 
					    --hash=sha256:61b5ed1e22a0924aed1d23b478f37e8d52549ff8a961de2909c69bf950020cff
 | 
				
			||||||
@@ -788,6 +796,12 @@ mypy==0.910 \
 | 
				
			|||||||
    # via
 | 
					    # via
 | 
				
			||||||
    #   -r requirements/mypy.in
 | 
					    #   -r requirements/mypy.in
 | 
				
			||||||
    #   sqlalchemy-stubs
 | 
					    #   sqlalchemy-stubs
 | 
				
			||||||
 | 
					mypy-boto3-s3==1.18.17 \
 | 
				
			||||||
 | 
					    --hash=sha256:63a76e94df730984196fd46be3f541dacc8d162f6f70c210a4cd9a80d6775e3b \
 | 
				
			||||||
 | 
					    --hash=sha256:af3699fb37614ff8044b7b6d3d7dd2211e5307bf018ac4f0a3591ec2011123c1
 | 
				
			||||||
 | 
					    # via
 | 
				
			||||||
 | 
					    #   -r requirements/common.in
 | 
				
			||||||
 | 
					    #   boto3-stubs
 | 
				
			||||||
mypy-extensions==0.4.3 \
 | 
					mypy-extensions==0.4.3 \
 | 
				
			||||||
    --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
 | 
					    --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
 | 
				
			||||||
    --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
 | 
					    --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
 | 
				
			||||||
@@ -1742,9 +1756,12 @@ typing-extensions==3.10.0.0 \
 | 
				
			|||||||
    #   arrow
 | 
					    #   arrow
 | 
				
			||||||
    #   asgiref
 | 
					    #   asgiref
 | 
				
			||||||
    #   black
 | 
					    #   black
 | 
				
			||||||
 | 
					    #   boto3-stubs
 | 
				
			||||||
 | 
					    #   botocore-stubs
 | 
				
			||||||
    #   importlib-metadata
 | 
					    #   importlib-metadata
 | 
				
			||||||
    #   libcst
 | 
					    #   libcst
 | 
				
			||||||
    #   mypy
 | 
					    #   mypy
 | 
				
			||||||
 | 
					    #   mypy-boto3-s3
 | 
				
			||||||
    #   pyre-check
 | 
					    #   pyre-check
 | 
				
			||||||
    #   pyre-extensions
 | 
					    #   pyre-extensions
 | 
				
			||||||
    #   sqlalchemy-stubs
 | 
					    #   sqlalchemy-stubs
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
mypy
 | 
					mypy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
backoff-stubs
 | 
					backoff-stubs
 | 
				
			||||||
 | 
					boto3-stubs[s3]
 | 
				
			||||||
lxml-stubs
 | 
					lxml-stubs
 | 
				
			||||||
https://github.com/andersk/pika-stubs/archive/87c5795741449e37bdbd2ceceee853fd56462440.zip#egg=pika-stubs==0.1.3+git  # https://github.com/hahow/pika-stubs/issues/1, https://github.com/hahow/pika-stubs/pull/4
 | 
					https://github.com/andersk/pika-stubs/archive/87c5795741449e37bdbd2ceceee853fd56462440.zip#egg=pika-stubs==0.1.3+git  # https://github.com/hahow/pika-stubs/issues/1, https://github.com/hahow/pika-stubs/pull/4
 | 
				
			||||||
sqlalchemy-stubs
 | 
					sqlalchemy-stubs
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,14 @@
 | 
				
			|||||||
backoff-stubs==1.10.0 \
 | 
					backoff-stubs==1.10.0 \
 | 
				
			||||||
    --hash=sha256:03e995de0a70016c6fe758498e1ca811f1db517c00cbd06e3039c9e4f6ea2566
 | 
					    --hash=sha256:03e995de0a70016c6fe758498e1ca811f1db517c00cbd06e3039c9e4f6ea2566
 | 
				
			||||||
    # via -r requirements/mypy.in
 | 
					    # via -r requirements/mypy.in
 | 
				
			||||||
 | 
					boto3-stubs[s3]==1.18.17 \
 | 
				
			||||||
 | 
					    --hash=sha256:97aef3a2173bedd95b75aafaa1a6a85e321af107a11855f30afded7a1e2462bc \
 | 
				
			||||||
 | 
					    --hash=sha256:b5bd2f3f54f06eecb9f6a085643c4c02a057c5fbbad4256c4e4a02a0744758df
 | 
				
			||||||
 | 
					    # via -r requirements/mypy.in
 | 
				
			||||||
 | 
					botocore-stubs==1.21.17 \
 | 
				
			||||||
 | 
					    --hash=sha256:6fca2ff326532e8ad8b74c1e5ef6e0457f409ebe38cb8b4aa6cb50b534ee3ee3 \
 | 
				
			||||||
 | 
					    --hash=sha256:b754cb23471948b8cbe50d24d4a74c617672905e4ae170b01bcfcae6496d7cb6
 | 
				
			||||||
 | 
					    # via boto3-stubs
 | 
				
			||||||
lxml-stubs==0.2.0 \
 | 
					lxml-stubs==0.2.0 \
 | 
				
			||||||
    --hash=sha256:78f1bfb31b1f2af9a5c9e9a602ab1b589a64a5a3cc444931a39cdfd02d6864b0 \
 | 
					    --hash=sha256:78f1bfb31b1f2af9a5c9e9a602ab1b589a64a5a3cc444931a39cdfd02d6864b0 \
 | 
				
			||||||
    --hash=sha256:f0b3621ec2a23bea4145f484490c8b27383ecb407b3f8b079199ad4a0af4180b
 | 
					    --hash=sha256:f0b3621ec2a23bea4145f484490c8b27383ecb407b3f8b079199ad4a0af4180b
 | 
				
			||||||
@@ -41,6 +49,10 @@ mypy==0.910 \
 | 
				
			|||||||
    # via
 | 
					    # via
 | 
				
			||||||
    #   -r requirements/mypy.in
 | 
					    #   -r requirements/mypy.in
 | 
				
			||||||
    #   sqlalchemy-stubs
 | 
					    #   sqlalchemy-stubs
 | 
				
			||||||
 | 
					mypy-boto3-s3==1.18.17 \
 | 
				
			||||||
 | 
					    --hash=sha256:63a76e94df730984196fd46be3f541dacc8d162f6f70c210a4cd9a80d6775e3b \
 | 
				
			||||||
 | 
					    --hash=sha256:af3699fb37614ff8044b7b6d3d7dd2211e5307bf018ac4f0a3591ec2011123c1
 | 
				
			||||||
 | 
					    # via boto3-stubs
 | 
				
			||||||
mypy-extensions==0.4.3 \
 | 
					mypy-extensions==0.4.3 \
 | 
				
			||||||
    --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
 | 
					    --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
 | 
				
			||||||
    --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
 | 
					    --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
 | 
				
			||||||
@@ -179,5 +191,6 @@ typing-extensions==3.10.0.0 \
 | 
				
			|||||||
    --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \
 | 
					    --hash=sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342 \
 | 
				
			||||||
    --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84
 | 
					    --hash=sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84
 | 
				
			||||||
    # via
 | 
					    # via
 | 
				
			||||||
 | 
					    #   boto3-stubs
 | 
				
			||||||
    #   mypy
 | 
					    #   mypy
 | 
				
			||||||
    #   sqlalchemy-stubs
 | 
					    #   sqlalchemy-stubs
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -494,6 +494,10 @@ more-itertools==8.8.0 \
 | 
				
			|||||||
    --hash=sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d \
 | 
					    --hash=sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d \
 | 
				
			||||||
    --hash=sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a
 | 
					    --hash=sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a
 | 
				
			||||||
    # via openapi-core
 | 
					    # via openapi-core
 | 
				
			||||||
 | 
					mypy-boto3-s3==1.18.17 \
 | 
				
			||||||
 | 
					    --hash=sha256:63a76e94df730984196fd46be3f541dacc8d162f6f70c210a4cd9a80d6775e3b \
 | 
				
			||||||
 | 
					    --hash=sha256:af3699fb37614ff8044b7b6d3d7dd2211e5307bf018ac4f0a3591ec2011123c1
 | 
				
			||||||
 | 
					    # via -r requirements/common.in
 | 
				
			||||||
oauthlib==3.1.1 \
 | 
					oauthlib==3.1.1 \
 | 
				
			||||||
    --hash=sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc \
 | 
					    --hash=sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc \
 | 
				
			||||||
    --hash=sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3
 | 
					    --hash=sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3
 | 
				
			||||||
@@ -1046,6 +1050,7 @@ typing-extensions==3.10.0.0 \
 | 
				
			|||||||
    #   -r requirements/common.in
 | 
					    #   -r requirements/common.in
 | 
				
			||||||
    #   asgiref
 | 
					    #   asgiref
 | 
				
			||||||
    #   importlib-metadata
 | 
					    #   importlib-metadata
 | 
				
			||||||
 | 
					    #   mypy-boto3-s3
 | 
				
			||||||
    #   zulip-bots
 | 
					    #   zulip-bots
 | 
				
			||||||
uhashring==2.1 \
 | 
					uhashring==2.1 \
 | 
				
			||||||
    --hash=sha256:b21340d0d32497a67f34f5177a64908115fdc23264ed87fa7d1eca79ef9641fa
 | 
					    --hash=sha256:b21340d0d32497a67f34f5177a64908115fdc23264ed87fa7d1eca79ef9641fa
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,4 +48,4 @@ API_FEATURE_LEVEL = 92
 | 
				
			|||||||
#   historical commits sharing the same major version, in which case a
 | 
					#   historical commits sharing the same major version, in which case a
 | 
				
			||||||
#   minor version bump suffices.
 | 
					#   minor version bump suffices.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PROVISION_VERSION = "153.13"
 | 
					PROVISION_VERSION = "153.14"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,12 +16,12 @@ import tempfile
 | 
				
			|||||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
 | 
					from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import orjson
 | 
					import orjson
 | 
				
			||||||
from boto3.resources.base import ServiceResource
 | 
					 | 
				
			||||||
from django.apps import apps
 | 
					from django.apps import apps
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.forms.models import model_to_dict
 | 
					from django.forms.models import model_to_dict
 | 
				
			||||||
from django.utils.timezone import is_naive as timezone_is_naive
 | 
					from django.utils.timezone import is_naive as timezone_is_naive
 | 
				
			||||||
from django.utils.timezone import make_aware as timezone_make_aware
 | 
					from django.utils.timezone import make_aware as timezone_make_aware
 | 
				
			||||||
 | 
					from mypy_boto3_s3.service_resource import Object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import zerver.lib.upload
 | 
					import zerver.lib.upload
 | 
				
			||||||
from analytics.models import RealmCount, StreamCount, UserCount
 | 
					from analytics.models import RealmCount, StreamCount, UserCount
 | 
				
			||||||
@@ -1275,7 +1275,7 @@ def export_uploads_and_avatars(realm: Realm, output_dir: Path) -> None:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def _check_key_metadata(
 | 
					def _check_key_metadata(
 | 
				
			||||||
    email_gateway_bot: Optional[UserProfile],
 | 
					    email_gateway_bot: Optional[UserProfile],
 | 
				
			||||||
    key: ServiceResource,
 | 
					    key: Object,
 | 
				
			||||||
    processing_avatars: bool,
 | 
					    processing_avatars: bool,
 | 
				
			||||||
    realm: Realm,
 | 
					    realm: Realm,
 | 
				
			||||||
    user_ids: Set[int],
 | 
					    user_ids: Set[int],
 | 
				
			||||||
@@ -1298,10 +1298,10 @@ def _check_key_metadata(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _get_exported_s3_record(
 | 
					def _get_exported_s3_record(
 | 
				
			||||||
    bucket_name: str, key: ServiceResource, processing_emoji: bool
 | 
					    bucket_name: str, key: Object, processing_emoji: bool
 | 
				
			||||||
) -> Dict[str, Union[str, int]]:
 | 
					) -> Dict[str, Any]:
 | 
				
			||||||
    # Helper function for export_files_from_s3
 | 
					    # Helper function for export_files_from_s3
 | 
				
			||||||
    record = dict(
 | 
					    record: Dict[str, Any] = dict(
 | 
				
			||||||
        s3_path=key.key,
 | 
					        s3_path=key.key,
 | 
				
			||||||
        bucket=bucket_name,
 | 
					        bucket=bucket_name,
 | 
				
			||||||
        size=key.content_length,
 | 
					        size=key.content_length,
 | 
				
			||||||
@@ -1315,7 +1315,7 @@ def _get_exported_s3_record(
 | 
				
			|||||||
        record["file_name"] = os.path.basename(key.key)
 | 
					        record["file_name"] = os.path.basename(key.key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if "user_profile_id" in record:
 | 
					    if "user_profile_id" in record:
 | 
				
			||||||
        user_profile = get_user_profile_by_id(record["user_profile_id"])
 | 
					        user_profile = get_user_profile_by_id(int(record["user_profile_id"]))
 | 
				
			||||||
        record["user_profile_email"] = user_profile.email
 | 
					        record["user_profile_email"] = user_profile.email
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Fix the record ids
 | 
					        # Fix the record ids
 | 
				
			||||||
@@ -1340,7 +1340,7 @@ def _get_exported_s3_record(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _save_s3_object_to_file(
 | 
					def _save_s3_object_to_file(
 | 
				
			||||||
    key: ServiceResource,
 | 
					    key: Object,
 | 
				
			||||||
    output_dir: str,
 | 
					    output_dir: str,
 | 
				
			||||||
    processing_avatars: bool,
 | 
					    processing_avatars: bool,
 | 
				
			||||||
    processing_emoji: bool,
 | 
					    processing_emoji: bool,
 | 
				
			||||||
@@ -1365,7 +1365,7 @@ def _save_s3_object_to_file(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if not os.path.exists(dirname):
 | 
					    if not os.path.exists(dirname):
 | 
				
			||||||
        os.makedirs(dirname)
 | 
					        os.makedirs(dirname)
 | 
				
			||||||
    key.download_file(filename)
 | 
					    key.download_file(Filename=filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def export_files_from_s3(
 | 
					def export_files_from_s3(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -823,7 +823,7 @@ def import_uploads(
 | 
				
			|||||||
                    content_type = "application/octet-stream"
 | 
					                    content_type = "application/octet-stream"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            key.upload_file(
 | 
					            key.upload_file(
 | 
				
			||||||
                os.path.join(import_dir, record["path"]),
 | 
					                Filename=os.path.join(import_dir, record["path"]),
 | 
				
			||||||
                ExtraArgs={"ContentType": content_type, "Metadata": metadata},
 | 
					                ExtraArgs={"ContentType": content_type, "Metadata": metadata},
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,6 @@ import boto3
 | 
				
			|||||||
import fakeldap
 | 
					import fakeldap
 | 
				
			||||||
import ldap
 | 
					import ldap
 | 
				
			||||||
import orjson
 | 
					import orjson
 | 
				
			||||||
from boto3.resources.base import ServiceResource
 | 
					 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.contrib.auth.models import AnonymousUser
 | 
					from django.contrib.auth.models import AnonymousUser
 | 
				
			||||||
from django.db.migrations.state import StateApps
 | 
					from django.db.migrations.state import StateApps
 | 
				
			||||||
@@ -37,6 +36,7 @@ from django.http.request import QueryDict
 | 
				
			|||||||
from django.test import override_settings
 | 
					from django.test import override_settings
 | 
				
			||||||
from django.urls import URLResolver
 | 
					from django.urls import URLResolver
 | 
				
			||||||
from moto import mock_s3
 | 
					from moto import mock_s3
 | 
				
			||||||
 | 
					from mypy_boto3_s3.service_resource import Bucket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import zerver.lib.upload
 | 
					import zerver.lib.upload
 | 
				
			||||||
from zerver.lib import cache
 | 
					from zerver.lib import cache
 | 
				
			||||||
@@ -521,7 +521,7 @@ def use_s3_backend(method: FuncT) -> FuncT:
 | 
				
			|||||||
    return new_method
 | 
					    return new_method
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def create_s3_buckets(*bucket_names: str) -> List[ServiceResource]:
 | 
					def create_s3_buckets(*bucket_names: str) -> List[Bucket]:
 | 
				
			||||||
    session = boto3.Session(settings.S3_KEY, settings.S3_SECRET_KEY)
 | 
					    session = boto3.Session(settings.S3_KEY, settings.S3_SECRET_KEY)
 | 
				
			||||||
    s3 = session.resource("s3")
 | 
					    s3 = session.resource("s3")
 | 
				
			||||||
    buckets = [s3.create_bucket(Bucket=name) for name in bucket_names]
 | 
					    buckets = [s3.create_bucket(Bucket=name) for name in bucket_names]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,6 @@ from typing import Any, Callable, Optional, Tuple
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import boto3
 | 
					import boto3
 | 
				
			||||||
import botocore
 | 
					import botocore
 | 
				
			||||||
from boto3.resources.base import ServiceResource
 | 
					 | 
				
			||||||
from boto3.session import Session
 | 
					from boto3.session import Session
 | 
				
			||||||
from botocore.client import Config
 | 
					from botocore.client import Config
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
@@ -25,6 +24,8 @@ from django.http import HttpRequest
 | 
				
			|||||||
from django.urls import reverse
 | 
					from django.urls import reverse
 | 
				
			||||||
from django.utils.translation import gettext as _
 | 
					from django.utils.translation import gettext as _
 | 
				
			||||||
from jinja2.utils import Markup as mark_safe
 | 
					from jinja2.utils import Markup as mark_safe
 | 
				
			||||||
 | 
					from mypy_boto3_s3.client import S3Client
 | 
				
			||||||
 | 
					from mypy_boto3_s3.service_resource import Bucket, Object
 | 
				
			||||||
from PIL import Image, ImageOps
 | 
					from PIL import Image, ImageOps
 | 
				
			||||||
from PIL.GifImagePlugin import GifImageFile
 | 
					from PIL.GifImagePlugin import GifImageFile
 | 
				
			||||||
from PIL.Image import DecompressionBombError
 | 
					from PIL.Image import DecompressionBombError
 | 
				
			||||||
@@ -278,9 +279,7 @@ class ZulipUploadBackend:
 | 
				
			|||||||
### S3
 | 
					### S3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_bucket(bucket_name: str, session: Optional[Session] = None) -> ServiceResource:
 | 
					def get_bucket(bucket_name: str, session: Optional[Session] = None) -> Bucket:
 | 
				
			||||||
    # See https://github.com/python/typeshed/issues/2706
 | 
					 | 
				
			||||||
    # for why this return type is a `ServiceResource`.
 | 
					 | 
				
			||||||
    if session is None:
 | 
					    if session is None:
 | 
				
			||||||
        session = boto3.Session(settings.S3_KEY, settings.S3_SECRET_KEY)
 | 
					        session = boto3.Session(settings.S3_KEY, settings.S3_SECRET_KEY)
 | 
				
			||||||
    bucket = session.resource(
 | 
					    bucket = session.resource(
 | 
				
			||||||
@@ -290,8 +289,7 @@ def get_bucket(bucket_name: str, session: Optional[Session] = None) -> ServiceRe
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def upload_image_to_s3(
 | 
					def upload_image_to_s3(
 | 
				
			||||||
    # See https://github.com/python/typeshed/issues/2706
 | 
					    bucket: Bucket,
 | 
				
			||||||
    bucket: ServiceResource,
 | 
					 | 
				
			||||||
    file_name: str,
 | 
					    file_name: str,
 | 
				
			||||||
    content_type: Optional[str],
 | 
					    content_type: Optional[str],
 | 
				
			||||||
    user_profile: UserProfile,
 | 
					    user_profile: UserProfile,
 | 
				
			||||||
@@ -367,7 +365,7 @@ class S3UploadBackend(ZulipUploadBackend):
 | 
				
			|||||||
        self.avatar_bucket = get_bucket(settings.S3_AVATAR_BUCKET, self.session)
 | 
					        self.avatar_bucket = get_bucket(settings.S3_AVATAR_BUCKET, self.session)
 | 
				
			||||||
        self.uploads_bucket = get_bucket(settings.S3_AUTH_UPLOADS_BUCKET, self.session)
 | 
					        self.uploads_bucket = get_bucket(settings.S3_AUTH_UPLOADS_BUCKET, self.session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._boto_client = None
 | 
					        self._boto_client: Optional[S3Client] = None
 | 
				
			||||||
        self.public_upload_url_base = self.construct_public_upload_url_base()
 | 
					        self.public_upload_url_base = self.construct_public_upload_url_base()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def construct_public_upload_url_base(self) -> str:
 | 
					    def construct_public_upload_url_base(self) -> str:
 | 
				
			||||||
@@ -410,7 +408,7 @@ class S3UploadBackend(ZulipUploadBackend):
 | 
				
			|||||||
        assert not key.startswith("/")
 | 
					        assert not key.startswith("/")
 | 
				
			||||||
        return urllib.parse.urljoin(self.public_upload_url_base, key)
 | 
					        return urllib.parse.urljoin(self.public_upload_url_base, key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_boto_client(self) -> botocore.client.BaseClient:
 | 
					    def get_boto_client(self) -> S3Client:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Creating the client takes a long time so we need to cache it.
 | 
					        Creating the client takes a long time so we need to cache it.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -424,7 +422,7 @@ class S3UploadBackend(ZulipUploadBackend):
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
        return self._boto_client
 | 
					        return self._boto_client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def delete_file_from_s3(self, path_id: str, bucket: ServiceResource) -> bool:
 | 
					    def delete_file_from_s3(self, path_id: str, bucket: Bucket) -> bool:
 | 
				
			||||||
        key = bucket.Object(path_id)
 | 
					        key = bucket.Object(path_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -532,9 +530,7 @@ class S3UploadBackend(ZulipUploadBackend):
 | 
				
			|||||||
        self.delete_file_from_s3(path_id + "-medium.png", self.avatar_bucket)
 | 
					        self.delete_file_from_s3(path_id + "-medium.png", self.avatar_bucket)
 | 
				
			||||||
        self.delete_file_from_s3(path_id, self.avatar_bucket)
 | 
					        self.delete_file_from_s3(path_id, self.avatar_bucket)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_avatar_key(self, file_name: str) -> ServiceResource:
 | 
					    def get_avatar_key(self, file_name: str) -> Object:
 | 
				
			||||||
        # See https://github.com/python/typeshed/issues/2706
 | 
					 | 
				
			||||||
        # for why this return type is a `ServiceResource`.
 | 
					 | 
				
			||||||
        key = self.avatar_bucket.Object(file_name)
 | 
					        key = self.avatar_bucket.Object(file_name)
 | 
				
			||||||
        return key
 | 
					        return key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -693,7 +689,10 @@ class S3UploadBackend(ZulipUploadBackend):
 | 
				
			|||||||
            os.path.join("exports", secrets.token_hex(16), os.path.basename(tarball_path))
 | 
					            os.path.join("exports", secrets.token_hex(16), os.path.basename(tarball_path))
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        key.upload_file(tarball_path, Callback=percent_callback)
 | 
					        if percent_callback is None:
 | 
				
			||||||
 | 
					            key.upload_file(Filename=tarball_path)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            key.upload_file(Filename=tarball_path, Callback=percent_callback)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public_url = self.get_public_upload_url(key.key)
 | 
					        public_url = self.get_public_upload_url(key.key)
 | 
				
			||||||
        return public_url
 | 
					        return public_url
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ from django.conf import settings
 | 
				
			|||||||
from django.db import migrations, models
 | 
					from django.db import migrations, models
 | 
				
			||||||
from django.db.backends.postgresql.schema import DatabaseSchemaEditor
 | 
					from django.db.backends.postgresql.schema import DatabaseSchemaEditor
 | 
				
			||||||
from django.db.migrations.state import StateApps
 | 
					from django.db.migrations.state import StateApps
 | 
				
			||||||
 | 
					from mypy_boto3_s3.type_defs import CopySourceTypeDef
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Uploader:
 | 
					class Uploader:
 | 
				
			||||||
@@ -66,8 +67,8 @@ class S3Uploader(Uploader):
 | 
				
			|||||||
        ).Bucket(self.bucket_name)
 | 
					        ).Bucket(self.bucket_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def copy_files(self, src_key: str, dst_key: str) -> None:
 | 
					    def copy_files(self, src_key: str, dst_key: str) -> None:
 | 
				
			||||||
        source = dict(Bucket=self.bucket_name, Key=src_key)
 | 
					        source = CopySourceTypeDef(Bucket=self.bucket_name, Key=src_key)
 | 
				
			||||||
        self.bucket.copy(source, dst_key)
 | 
					        self.bucket.copy(CopySource=source, Key=dst_key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_uploader() -> Uploader:
 | 
					def get_uploader() -> Uploader:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user