s3path
Advanced tools
+1
-1
| Metadata-Version: 2.4 | ||
| Name: s3path | ||
| Version: 0.6.2 | ||
| Version: 0.6.3 | ||
| Home-page: https://github.com/liormizr/s3path | ||
@@ -5,0 +5,0 @@ Author: Lior Mizrahi |
| Metadata-Version: 2.4 | ||
| Name: s3path | ||
| Version: 0.6.2 | ||
| Version: 0.6.3 | ||
| Home-page: https://github.com/liormizr/s3path | ||
@@ -5,0 +5,0 @@ Author: Lior Mizrahi |
@@ -8,3 +8,3 @@ """ | ||
| __version__ = '0.6.2' | ||
| __version__ = '0.6.3' | ||
| __all__ = ( | ||
@@ -11,0 +11,0 @@ 'Path', |
@@ -158,4 +158,2 @@ import sys | ||
| return True | ||
| with suppress(KeyError): | ||
| return path._cache['is_dir'] | ||
@@ -168,5 +166,3 @@ resource, config = configuration_map.get_configuration(path) | ||
| config=config) | ||
| is_dir = any(query) | ||
| path._cache['is_dir'] = is_dir | ||
| return is_dir | ||
| return any(query) | ||
@@ -173,0 +169,0 @@ |
@@ -5,3 +5,2 @@ from __future__ import annotations | ||
| import sys | ||
| import typing | ||
| import fnmatch | ||
@@ -13,3 +12,3 @@ import posixpath | ||
| from pathlib import PurePath, Path | ||
| from typing import Union, Literal, Optional | ||
| from typing import TYPE_CHECKING, Literal, Self, Generator | ||
| from io import DEFAULT_BUFFER_SIZE, TextIOWrapper | ||
@@ -19,6 +18,7 @@ | ||
| if typing.TYPE_CHECKING: | ||
| if TYPE_CHECKING: | ||
| from os import PathLike | ||
| import smart_open | ||
| from boto3.resources.factory import ServiceResource | ||
| KeyFileObjectType = Union[TextIOWrapper, smart_open.s3.Reader, smart_open.s3.MultipartWriter] | ||
| from boto3.resources.base import ServiceResource | ||
| KeyFileObjectType = TextIOWrapper | smart_open.s3.Reader | smart_open.s3.MultipartWriter | ||
@@ -31,5 +31,5 @@ from . import accessor | ||
| *, | ||
| parameters: Optional[dict] = None, | ||
| resource: Optional[ServiceResource] = None, | ||
| glob_new_algorithm: Optional[bool] = None): | ||
| parameters: dict | None = None, | ||
| resource: ServiceResource | None = None, | ||
| glob_new_algorithm: bool | None = None): | ||
| if not isinstance(path, PureS3Path): | ||
@@ -81,3 +81,3 @@ raise TypeError(f'path argument have to be a {PurePath} type. got {type(path)}') | ||
| @classmethod | ||
| def from_uri(cls, uri: str): | ||
| def from_uri(cls, uri: str) -> PureS3Path: | ||
| """ | ||
@@ -96,3 +96,3 @@ from_uri class method create a class instance from url | ||
| @classmethod | ||
| def from_bucket_key(cls, bucket: str, key: str): | ||
| def from_bucket_key(cls, bucket: str | PathLike, key: str | PathLike) -> PureS3Path: | ||
| """ | ||
@@ -286,14 +286,5 @@ from_bucket_key class method create a class instance from bucket, key pair's | ||
| class _PathCacheMixin: | ||
| """ | ||
| This is a mixin class to cache the results and path state. | ||
| Note: this is experimental and will be more robust in the future. | ||
| """ | ||
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs) | ||
| self._cache = {} | ||
| class S3Path(_PathNotSupportedMixin, _PathCacheMixin, PureS3Path, Path): | ||
| def stat(self, *, follow_symlinks: bool = True) -> accessor.StatResult: | ||
| class S3Path(_PathNotSupportedMixin, PureS3Path, Path): | ||
| def stat(self, *, follow_symlinks: bool = True) -> accessor.StatResult | None: | ||
| """ | ||
@@ -313,3 +304,3 @@ Returns information about this path (similarly to boto3's ObjectSummary). | ||
| def absolute(self) -> S3Path: | ||
| def absolute(self) -> Self: | ||
| """ | ||
@@ -324,3 +315,3 @@ Handle absolute method only if the path is already an absolute one | ||
| def owner(self) -> str: | ||
| def owner(self, *, follow_symlinks: bool = False) -> str: | ||
| """ | ||
@@ -331,2 +322,4 @@ Returns the name of the user owning the Bucket or key. | ||
| self._absolute_path_validation() | ||
| if follow_symlinks: | ||
| raise NotImplementedError(f'Setting follow_symlinks to {follow_symlinks} is unsupported on S3 service.') | ||
| if not self.is_file(): | ||
@@ -336,3 +329,3 @@ raise KeyError('file not found') | ||
| def rename(self, target): | ||
| def rename(self, target) -> S3Path: | ||
| """ | ||
@@ -351,3 +344,3 @@ Renames this file or Bucket / key prefix / key to the given target. | ||
| def replace(self, target): | ||
| def replace(self, target) -> S3Path: | ||
| """ | ||
@@ -370,3 +363,3 @@ Renames this Bucket / key prefix / key to the given target. | ||
| def samefile(self, other_path: Union[str, S3Path]) -> bool: | ||
| def samefile(self, other_path: str | PathLike) -> bool: | ||
| """ | ||
@@ -377,3 +370,3 @@ Returns whether this path points to the same Bucket key as other_path, | ||
| self._absolute_path_validation() | ||
| if not isinstance(other_path, Path): | ||
| if not isinstance(other_path, S3Path): | ||
| other_path = type(self)(other_path) | ||
@@ -419,3 +412,3 @@ return self.bucket == other_path.bucket and self.key == other_path.key and self.is_file() | ||
| def is_dir(self) -> bool: | ||
| def is_dir(self, *, follow_symlinks: bool = False) -> bool: | ||
| """ | ||
@@ -427,2 +420,4 @@ Returns True if the path points to a Bucket or a key prefix, False if it points to a full key path. | ||
| self._absolute_path_validation() | ||
| if follow_symlinks: | ||
| raise NotImplementedError(f'Setting follow_symlinks to {follow_symlinks} is unsupported on S3 service.') | ||
| if self.bucket and not self.key: | ||
@@ -432,3 +427,3 @@ return True | ||
| def is_file(self) -> bool: | ||
| def is_file(self, *, follow_symlinks: bool = False) -> bool: | ||
| """ | ||
@@ -440,2 +435,4 @@ Returns True if the path points to a Bucket key, False if it points to Bucket or a key prefix. | ||
| self._absolute_path_validation() | ||
| if follow_symlinks: | ||
| raise NotImplementedError(f'Setting follow_symlinks to {follow_symlinks} is unsupported on S3 service.') | ||
| if not self.bucket or not self.key: | ||
@@ -448,3 +445,3 @@ return False | ||
| def exists(self) -> bool: | ||
| def exists(self, *, follow_symlinks: bool = False) -> bool: | ||
| """ | ||
@@ -454,2 +451,4 @@ Whether the path points to an existing Bucket, key or key prefix. | ||
| self._absolute_path_validation() | ||
| if follow_symlinks: | ||
| raise NotImplementedError(f'Setting follow_symlinks to {follow_symlinks} is unsupported on S3 service.') | ||
| if not self.bucket: | ||
@@ -459,3 +458,3 @@ return True | ||
| def iterdir(self): | ||
| def iterdir(self) -> Generator['S3Path']: | ||
| """ | ||
@@ -468,3 +467,2 @@ When the path points to a Bucket or a key prefix, yield path objects of the directory contents | ||
| path = self / entry.name | ||
| path._cache['is_dir'] = entry.is_dir() | ||
| yield path | ||
@@ -476,5 +474,5 @@ | ||
| buffering: int = DEFAULT_BUFFER_SIZE, | ||
| encoding: Optional[str] = None, | ||
| errors: Optional[str] = None, | ||
| newline: Optional[str] = None) -> KeyFileObjectType: | ||
| encoding: str | None = None, | ||
| errors: str | None = None, | ||
| newline: str | None = None) -> KeyFileObjectType: | ||
| """ | ||
@@ -494,3 +492,7 @@ Opens the Bucket key pointed to by the path, returns a Key file object that you can read/write with | ||
| def glob(self, pattern: str, *, case_sensitive=None, recurse_symlinks=False): | ||
| def glob( | ||
| self, | ||
| pattern: str, *, | ||
| case_sensitive: bool | None = None, | ||
| recurse_symlinks: bool = False) -> Generator['S3Path']: | ||
| """ | ||
@@ -518,3 +520,7 @@ Glob the given relative pattern in the Bucket / key prefix represented by this path, | ||
| def rglob(self, pattern: str, *, case_sensitive=None, recurse_symlinks=False): | ||
| def rglob( | ||
| self, | ||
| pattern: str, *, | ||
| case_sensitive: bool | None = None, | ||
| recurse_symlinks: bool = False) -> Generator['S3Path']: | ||
| """ | ||
@@ -540,3 +546,3 @@ This is like calling S3Path.glob with "**/" added in front of the given relative pattern | ||
| def get_presigned_url(self, expire_in: Union[timedelta, int] = 3600) -> str: | ||
| def get_presigned_url(self, expire_in: timedelta | int = 3600) -> str: | ||
| """ | ||
@@ -612,3 +618,7 @@ Returns a pre-signed url. Anyone with the url can make a GET request to get the file. | ||
| def walk(self, top_down: bool = True, on_error:bool = None, follow_symlinks: bool = False): | ||
| def walk( | ||
| self, | ||
| top_down: bool = True, | ||
| on_error:bool = None, | ||
| follow_symlinks: bool = False) -> Generator[tuple['S3Path', list[str], list[str]]]: | ||
| if follow_symlinks: | ||
@@ -652,3 +662,3 @@ raise NotImplementedError(f'Setting follow_symlinks to {follow_symlinks} is unsupported on S3 service.') | ||
| @classmethod | ||
| def from_uri(cls, uri: str, *, version_id: str): | ||
| def from_uri(cls, uri: str, *, version_id: str) -> PureVersionedS3Path: | ||
| """ | ||
@@ -666,3 +676,3 @@ from_uri class method creates a class instance from uri and version id | ||
| @classmethod | ||
| def from_bucket_key(cls, bucket: str, key: str, *, version_id: str): | ||
| def from_bucket_key(cls, bucket: str, key: str, *, version_id: str) -> PureVersionedS3Path: | ||
| """ | ||
@@ -679,3 +689,3 @@ from_bucket_key class method creates a class instance from bucket, key and version id | ||
| def with_segments(self, *pathsegments): | ||
| def with_segments(self, *pathsegments) -> PureVersionedS3Path: | ||
| """Construct a new path object from any number of path-like objects. | ||
@@ -682,0 +692,0 @@ Subclasses may override this method to customize how new path objects |
+23
-86
@@ -258,16 +258,9 @@ """ | ||
| dummy_object = resource.Object('bucket', 'key') | ||
| if smart_open.__version__ >= '5.1.0': | ||
| self._smart_open_new_version_kwargs( | ||
| dummy_object, | ||
| resource, | ||
| config, | ||
| transport_params, | ||
| smart_open_kwargs) | ||
| else: | ||
| self._smart_open_old_version_kwargs( | ||
| dummy_object, | ||
| resource, | ||
| config, | ||
| transport_params, | ||
| smart_open_kwargs) | ||
| self._update_smart_open_kwargs( | ||
| dummy_object, | ||
| resource, | ||
| config, | ||
| transport_params, | ||
| smart_open_kwargs, | ||
| ) | ||
@@ -445,9 +438,9 @@ file_object = smart_open.open(**smart_open_kwargs) | ||
| def _boto3_method_with_extraargs( | ||
| self, | ||
| boto3_method, | ||
| config=None, | ||
| args=(), | ||
| kwargs=None, | ||
| extra_args=None, | ||
| allowed_extra_args=()): | ||
| self, | ||
| boto3_method, | ||
| config=None, | ||
| args=(), | ||
| kwargs=None, | ||
| extra_args=None, | ||
| allowed_extra_args=()): | ||
| kwargs = kwargs or {} | ||
@@ -464,3 +457,3 @@ extra_args = extra_args or {} | ||
| def _smart_open_new_version_kwargs( | ||
| def _update_smart_open_kwargs( | ||
| self, | ||
@@ -473,3 +466,3 @@ dummy_object, | ||
| """ | ||
| New Smart-Open api | ||
| New Smart-Open (>=5.1.0) api | ||
| Doc: https://github.com/RaRe-Technologies/smart_open/blob/develop/MIGRATING_FROM_OLDER_VERSIONS.rst | ||
@@ -493,50 +486,3 @@ """ | ||
| def _smart_open_old_version_kwargs( | ||
| self, | ||
| dummy_object, | ||
| resource, | ||
| config, | ||
| transport_params, | ||
| smart_open_kwargs): | ||
| """ | ||
| Old Smart-Open api | ||
| <5.0.0 | ||
| """ | ||
| def get_resource_kwargs(): | ||
| # This is a good example of the complicity of boto3 and botocore | ||
| # resource arguments from the resource object :-/ | ||
| # very annoying... | ||
| try: | ||
| access_key = resource.meta.client._request_signer._credentials.access_key | ||
| secret_key = resource.meta.client._request_signer._credentials.secret_key | ||
| token = resource.meta.client._request_signer._credentials.token | ||
| except AttributeError: | ||
| access_key = secret_key = token = None | ||
| return { | ||
| 'endpoint_url': resource.meta.client.meta._endpoint_url, | ||
| 'config': resource.meta.client._client_config, | ||
| 'region_name': resource.meta.client._client_config.region_name, | ||
| 'use_ssl': resource.meta.client._endpoint.host.startswith('https'), | ||
| 'verify': resource.meta.client._endpoint.http_session._verify, | ||
| 'aws_access_key_id': access_key, | ||
| 'aws_secret_access_key': secret_key, | ||
| 'aws_session_token': token, | ||
| } | ||
| initiate_multipart_upload_kwargs = self._update_kwargs_with_config( | ||
| dummy_object.initiate_multipart_upload, config=config) | ||
| object_kwargs = self._update_kwargs_with_config(dummy_object.get, config=config) | ||
| transport_params.update( | ||
| multipart_upload_kwargs=initiate_multipart_upload_kwargs, | ||
| object_kwargs=object_kwargs, | ||
| resource_kwargs=get_resource_kwargs(), | ||
| session=boto3.DEFAULT_SESSION, | ||
| ) | ||
| smart_open_kwargs.update( | ||
| ignore_ext=True, | ||
| transport_params=transport_params, | ||
| ) | ||
| class _VersionedS3Accessor(_S3Accessor): | ||
@@ -583,16 +529,9 @@ | ||
| dummy_object = resource.Object('bucket', 'key') | ||
| if smart_open.__version__ >= '5.1.0': | ||
| self._smart_open_new_version_kwargs( | ||
| dummy_object, | ||
| resource, | ||
| config, | ||
| transport_params, | ||
| smart_open_kwargs) | ||
| else: | ||
| self._smart_open_old_version_kwargs( | ||
| dummy_object, | ||
| resource, | ||
| config, | ||
| transport_params, | ||
| smart_open_kwargs) | ||
| self._update_smart_open_kwargs( | ||
| dummy_object, | ||
| resource, | ||
| config, | ||
| transport_params, | ||
| smart_open_kwargs, | ||
| ) | ||
@@ -1031,4 +970,2 @@ file_object = smart_open.open(**smart_open_kwargs) | ||
| self._absolute_path_validation() | ||
| if smart_open.__version__ < '4.0.0' and mode.startswith('b'): | ||
| mode = ''.join(reversed(mode)) | ||
| return self._accessor.open( | ||
@@ -1035,0 +972,0 @@ self, |
+1
-1
@@ -8,3 +8,3 @@ #!/usr/bin/env python | ||
| name='s3path', | ||
| version='0.6.2', | ||
| version='0.6.3', | ||
| url='https://github.com/liormizr/s3path', | ||
@@ -11,0 +11,0 @@ author='Lior Mizrahi', |
@@ -405,3 +405,3 @@ import shutil | ||
| with S3Path('/test-bucket/directory/Test.test').open() as fp: | ||
| assert len(fp.readlines(1)) == (1 if sys.version_info >= (3, 6) else 2) | ||
| assert len(fp.readlines(1)) == 1 | ||
@@ -497,18 +497,2 @@ with S3Path('/test-bucket/directory/Test.test').open('br') as fp: | ||
| @pytest.mark.skipif(sys.version_info < (3, 12), reason="requires python 3.12 or higher") | ||
| def test_issue_193(s3_mock): | ||
| s3 = boto3.resource('s3') | ||
| s3.create_bucket(Bucket='test-bucket') | ||
| object_summary = s3.ObjectSummary('test-bucket', 'docs/conf.py') | ||
| object_summary.put(Body=b'test data') | ||
| s3_path = S3Path('/test-bucket/docs') | ||
| assert sorted(s3_path.iterdir()) == sorted([ | ||
| S3Path('/test-bucket/docs/conf.py'), | ||
| ]) | ||
| path = list(s3_path.iterdir())[0] | ||
| assert 'is_dir' in path._cache | ||
| assert not path._cache['is_dir'] | ||
| def test_open_for_reading(s3_mock): | ||
@@ -515,0 +499,0 @@ s3 = boto3.resource('s3') |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
182718
-1.37%3456
-1.87%