slash3

codecov

Introduction

Slash3 is a Python package for building and navigating Amazon Web Services S3 URIs.

What's an S3 URI?

An S3 URI (Uniform Resource Identifier) is a string that identifies a bucket, and optionally a key, in Amazon Web Services S3.

The pattern for an S3 URI is s3://BUCKET/KEY.

For example:

  • The bucket named "circus" can be described by the URI s3://circus/
  • The key prefix for all circus images can be described by the URI s3://circus/images/
  • The path to Steve's staff photograph can be described by the URI s3://circus/images/steve.jpg

Installation

Slash3 requires Python 3.9 or later and can be installed from PyPI.

pip install slash3

Usage

Construct a URI from a URI

If you've already got a string URI then pass it directly to S3Uri:

from slash3 import S3Uri

uri = S3Uri("s3://circus/")

uri.bucket  # circus
uri.key     #

uri = S3Uri("s3://circus/images/clowns.jpg")

uri.bucket  # circus
uri.key     # images/clowns.jpg

Construct a URI from a bucket and key

To construct an S3 URI from a bucket name and an optional key, call S3Uri.to_uri:

from slash3 import S3Uri

uri = S3Uri.to_uri("circus")
# s3://circus/

uri = S3Uri.to_uri("circus", "images/clowns.jpg")
# s3://circus/images/clowns.jpg

Join a key suffix with a "/" delimiter

To join a key suffix with a "/" delimiter -- for example, to join an object's name to a key prefix -- call S3Uri.join() or use the / operator:

from slash3 import S3Uri

uri = S3Uri("s3://circus/")

images = uri / "images"
# s3://circus/images

clowns = images / "clowns.jpg"
# s3://circus/images/clowns.jpg

Slash3 will automatically normalise away any consecutive "/" delimiters.

Append a key suffix without a delimiter

To append a key suffix without a delimiter, call S3Uri.append() or use the + operator:

from slash3 import S3Uri

staff = S3Uri("s3://circus/staff-")

steve = staff + "steve.jpg"
# s3://circus/staff-steve.jpg

penny = staff + "penny.jpg"
# s3://circus/staff-penny.jpg

Get the parent key prefix

To get a URI's parent key prefix, call S3Uri.parent:

from slash3 import S3Uri

steve = S3Uri("s3://circus/images/steve.jpg")

steve.parent
# s3://circus/images/

Get the key's leaf / file name

from slash3 import S3Uri

steve = S3Uri("s3://circus/images/steve.jpg")

steve.leaf
# steve.jpg

Get a relative key path

To discover the relative path between a specific URI and a parent URI, call S3Uri.relative_to:

from slash3 import S3Uri

avatar = S3Uri("s3://circus/images/staff/steve.jpg")
images = "s3://circus/images/"

avatar.relative_to(images)
# staff/steve.jpg

Support

Please submit all your questions, feature requests and bug reports at github.com/cariad/slash3/issues. Thank you!

Licence

Slash3 is open-source and released under the MIT License.

You don't have to give attribution in your project, but -- as a freelance developer with rent to pay -- I appreciate it!

Author

Hello! 👋 I'm Cariad Eccleston, and I'm a freelance Amazon Web Services architect, DevOps evangelist, CI/CD pipeline engineer and backend developer.

You can find me at cariad.earth, github/cariad, linkedin/cariad and on Mastodon at @cariad@tech.lgbt.

  1"""
  2[![codecov](https://codecov.io/gh/cariad/slash3/branch/main/graph/badge.svg?token=Vq0w74e8YY)](https://codecov.io/gh/cariad/slash3)
  3
  4# Introduction
  5
  6**Slash3** is a Python package for building and navigating Amazon Web Services
  7S3 URIs.
  8
  9# What's an S3 URI?
 10
 11An S3 URI (Uniform Resource Identifier) is a string that identifies a bucket,
 12and optionally a key, in Amazon Web Services S3.
 13
 14The pattern for an S3 URI is `s3://BUCKET/KEY`.
 15
 16For example:
 17
 18- The bucket named "circus" can be described by the URI `s3://circus/`
 19- The key prefix for all circus images can be described by the URI
 20`s3://circus/images/`
 21- The path to Steve's staff photograph can be described by the URI
 22`s3://circus/images/steve.jpg`
 23
 24# Installation
 25
 26Slash3 requires Python 3.9 or later and can be installed from
 27[PyPI](https://pypi.org/project/slash3/).
 28
 29```shell
 30pip install slash3
 31```
 32
 33# Usage
 34
 35## Construct a URI from a URI
 36
 37If you've already got a string URI then pass it directly to `S3Uri`:
 38
 39```python
 40from slash3 import S3Uri
 41
 42uri = S3Uri("s3://circus/")
 43
 44uri.bucket  # circus
 45uri.key     #
 46
 47uri = S3Uri("s3://circus/images/clowns.jpg")
 48
 49uri.bucket  # circus
 50uri.key     # images/clowns.jpg
 51```
 52
 53## Construct a URI from a bucket and key
 54
 55To construct an S3 URI from a bucket name and an optional key, call
 56`S3Uri.to_uri`:
 57
 58```python
 59from slash3 import S3Uri
 60
 61uri = S3Uri.to_uri("circus")
 62# s3://circus/
 63
 64uri = S3Uri.to_uri("circus", "images/clowns.jpg")
 65# s3://circus/images/clowns.jpg
 66```
 67
 68## Join a key suffix with a "/" delimiter
 69
 70To join a key suffix with a "/" delimiter -- for example, to join an object's
 71name to a key prefix -- call `S3Uri.join()` or use the `/` operator:
 72
 73```python
 74from slash3 import S3Uri
 75
 76uri = S3Uri("s3://circus/")
 77
 78images = uri / "images"
 79# s3://circus/images
 80
 81clowns = images / "clowns.jpg"
 82# s3://circus/images/clowns.jpg
 83```
 84
 85Slash3 will automatically normalise away any consecutive "/" delimiters.
 86
 87## Append a key suffix without a delimiter
 88
 89To append a key suffix without a delimiter, call `S3Uri.append()` or use the `+`
 90operator:
 91
 92```python
 93from slash3 import S3Uri
 94
 95staff = S3Uri("s3://circus/staff-")
 96
 97steve = staff + "steve.jpg"
 98# s3://circus/staff-steve.jpg
 99
100penny = staff + "penny.jpg"
101# s3://circus/staff-penny.jpg
102```
103
104## Get the parent key prefix
105
106To get a URI's parent key prefix, call `S3Uri.parent`:
107
108```python
109from slash3 import S3Uri
110
111steve = S3Uri("s3://circus/images/steve.jpg")
112
113steve.parent
114# s3://circus/images/
115```
116
117## Get the key's leaf / file name
118
119```python
120from slash3 import S3Uri
121
122steve = S3Uri("s3://circus/images/steve.jpg")
123
124steve.leaf
125# steve.jpg
126```
127
128## Get a relative key path
129
130To discover the relative path between a specific URI and a parent URI, call
131`S3Uri.relative_to`:
132
133```python
134from slash3 import S3Uri
135
136avatar = S3Uri("s3://circus/images/staff/steve.jpg")
137images = "s3://circus/images/"
138
139avatar.relative_to(images)
140# staff/steve.jpg
141```
142
143# Support
144
145Please submit all your questions, feature requests and bug reports at
146[github.com/cariad/slash3/issues](https://github.com/cariad/slash3/issues). Thank you!
147
148# Licence
149
150Slash3 is [open-source](https://github.com/cariad/slash3) and released under the
151[MIT License](https://github.com/cariad/slash3/blob/main/LICENSE).
152
153You don't have to give attribution in your project, but -- as a freelance
154developer with rent to pay -- I appreciate it!
155
156# Author
157
158Hello! 👋 I'm **Cariad Eccleston**, and I'm a freelance Amazon Web Services
159architect, DevOps evangelist, CI/CD pipeline engineer and backend developer.
160
161You can find me at [cariad.earth](https://www.cariad.earth),
162[github/cariad](https://github.com/cariad),
163[linkedin/cariad](https://linkedin.com/in/cariad) and on Mastodon at
164[@cariad@tech.lgbt](https://tech.lgbt/@cariad).
165"""
166
167from importlib.resources import open_text
168
169from slash3.key import S3Key
170from slash3.uri import S3Uri
171
172with open_text(__package__, "VERSION") as t:
173    __version__ = t.readline().strip()
174
175__all__ = [
176    "S3Key",
177    "S3Uri",
178]
class S3Key:
  9class S3Key:
 10    """
 11    An Amazon Web Services S3 key.
 12    """
 13
 14    def __init__(self, key: Optional[str] = None) -> None:
 15        logger.debug('Creating new S3Key from "%s"', key)
 16        key = key or ""
 17
 18        if key.startswith("/"):
 19            raise ValueError(
 20                f'S3 keys cannot start with the delimiter "/" ("{key}")',
 21            )
 22
 23        if "//" in key:
 24            raise ValueError(
 25                f'S3 keys cannot contain consecutive "/" delimiters ("{key}")',
 26            )
 27
 28        if len(key) > MAX_LENGTH:
 29            raise ValueError(
 30                f"S3 keys cannot be longer than {MAX_LENGTH} characters "
 31                f'("{key}" has {len(key)} characters)',
 32            )
 33
 34        self._key = key or ""
 35
 36    def __add__(self, other: str) -> "S3Key":
 37        return self.append(other)
 38
 39    def __eq__(self, other: Any) -> bool:
 40        return self._key == str(other)
 41
 42    def __len__(self) -> int:
 43        return len(self._key)
 44
 45    def __repr__(self) -> str:
 46        return self._key
 47
 48    def __truediv__(self, other: str) -> "S3Key":
 49        return self.join(other)
 50
 51    def append(self, suffix: str) -> "S3Key":
 52        """
 53        Appends a string to the key.
 54
 55        ```python
 56        images = S3Key("images/staff-")
 57
 58        steve = images.append("steve.jpg")
 59        # "images/staff-steve.jpg"
 60
 61        # Or use "+":
 62        penny = images + "penny.jpg"
 63        # "images/staff-penny.jpg"
 64        ```
 65
 66        To add a suffix with a "/" delimiter, use `join()` instead.
 67        """
 68
 69        logger.debug('Appending base "%s" and suffix "%s"', self._key, suffix)
 70
 71        if self._key.endswith("/") and suffix.startswith("/"):
 72            base = self.normal_right(self._key, slash=True)
 73            logger.debug('Base normalised to "%s"', base)
 74            suffix = self.normal_left(suffix)
 75        else:
 76            base = self._key
 77
 78        return S3Key(base + suffix)
 79
 80    def join(self, suffix: str) -> "S3Key":
 81        """
 82        Joins a string to the key with a "/" delimiter.
 83
 84        ```python
 85        images = S3Key("images/staff")
 86
 87        steve = images.join("steve.jpg")
 88        # "images/staff/steve.jpg"
 89
 90        # Or use "/":
 91        penny = images / "penny.jpg"
 92        # "images/staff/penny.jpg"
 93        ```
 94
 95        To append a string without a "/" delimiter, use `append()` instead.
 96        """
 97
 98        logger.debug('Joining base "%s" and suffix "%s"', self._key, suffix)
 99
100        base = self.normal_right(self._key, slash=True) if self._key else self._key
101        logger.debug('Base normalised to "%s"', base)
102
103        suffix = self.normal_left(suffix)
104        logger.debug('Suffix normalised to "%s"', suffix)
105
106        return S3Key(base + suffix)
107
108    @property
109    def key(self) -> str:
110        """
111        Key.
112
113        For example, "private/clowns.jpg".
114        """
115
116        return self._key
117
118    @property
119    def leaf(self) -> str:
120        """
121        Key leaf.
122
123        In a file system metaphor, the leaf would be the file's name.
124
125        For example, the leaf of "private/clowns.jpg" is "clowns.jpg".
126        """
127
128        return self.relative_to(self.parent)
129
130    @staticmethod
131    def normal_left(key: str, slash: bool = False) -> str:
132        """
133        Normalises the left of the key.
134
135        If `slash` is true then the key is returned with exactly one leading
136        slash, otherwise the key is returned with no leading slashes.
137        """
138
139        while key.startswith("/"):
140            key = key[1:]
141
142        return "/" + key if slash else key
143
144    @staticmethod
145    def normal_right(key: str, slash: bool = False) -> str:
146        """
147        Normalises the right of the key.
148
149        If `slash` is true then the key is returned with exactly one trailing
150        slash, otherwise the key is returned with no trailing slashes.
151        """
152
153        while key.endswith("/"):
154            key = key[:-1]
155
156        return key + "/" if slash else key
157
158    @property
159    def parent(self) -> "S3Key":
160        """
161        Parent key.
162
163        For example, the parent of "private/clowns.jpg" is "private/".
164        """
165
166        logger.debug('Determining the parent of key "%s"', repr(self))
167
168        if self._key == "":
169            logger.debug("Empty key has no parent")
170            return self
171
172        if self._key.endswith("/"):
173            key = self._key[0:-1]
174        else:
175            key = self._key
176
177        if "/" not in key:
178            logger.debug("Root key parent is the root")
179            return S3Key()
180
181        index = key.rindex("/") + 1
182
183        logger.debug('Final "/" in key "%s" is at index %s', self._key, index)
184
185        parent_key = self._key[0:index]
186
187        logger.debug('Key "%s" parent is "%s"', self._key, parent_key)
188
189        return S3Key(parent_key)
190
191    def relative_to(self, parent: Union["S3Key", str]) -> str:
192        """
193        Gets the relative key path from this key to a `parent` key.
194
195        For example, the relative path to "private/clowns.jpg" from parent
196        "private" is "clowns.jpg".
197        """
198
199        logger.debug(
200            'Calculating the relative key from "%s" to "%s"',
201            self,
202            parent,
203        )
204
205        parent = str(parent)
206        normal = self.normal_right(parent, slash=True) if parent else parent
207
208        logger.debug('Normalised the parent key to "%s"', normal)
209
210        if not self._key.startswith(normal):
211            raise ValueError(f'"{parent}" is not a parent of "{self}"')
212
213        relative_key = self._key[len(normal) :]  # noqa: E203
214
215        logger.debug(
216            'The relative path from "%s" to "%s" is "%s"',
217            parent,
218            self,
219            relative_key,
220        )
221
222        return relative_key

An Amazon Web Services S3 key.

S3Key(key: Optional[str] = None)
14    def __init__(self, key: Optional[str] = None) -> None:
15        logger.debug('Creating new S3Key from "%s"', key)
16        key = key or ""
17
18        if key.startswith("/"):
19            raise ValueError(
20                f'S3 keys cannot start with the delimiter "/" ("{key}")',
21            )
22
23        if "//" in key:
24            raise ValueError(
25                f'S3 keys cannot contain consecutive "/" delimiters ("{key}")',
26            )
27
28        if len(key) > MAX_LENGTH:
29            raise ValueError(
30                f"S3 keys cannot be longer than {MAX_LENGTH} characters "
31                f'("{key}" has {len(key)} characters)',
32            )
33
34        self._key = key or ""
def append(self, suffix: str) -> slash3.S3Key:
51    def append(self, suffix: str) -> "S3Key":
52        """
53        Appends a string to the key.
54
55        ```python
56        images = S3Key("images/staff-")
57
58        steve = images.append("steve.jpg")
59        # "images/staff-steve.jpg"
60
61        # Or use "+":
62        penny = images + "penny.jpg"
63        # "images/staff-penny.jpg"
64        ```
65
66        To add a suffix with a "/" delimiter, use `join()` instead.
67        """
68
69        logger.debug('Appending base "%s" and suffix "%s"', self._key, suffix)
70
71        if self._key.endswith("/") and suffix.startswith("/"):
72            base = self.normal_right(self._key, slash=True)
73            logger.debug('Base normalised to "%s"', base)
74            suffix = self.normal_left(suffix)
75        else:
76            base = self._key
77
78        return S3Key(base + suffix)

Appends a string to the key.

images = S3Key("images/staff-")

steve = images.append("steve.jpg")
# "images/staff-steve.jpg"

# Or use "+":
penny = images + "penny.jpg"
# "images/staff-penny.jpg"

To add a suffix with a "/" delimiter, use join() instead.

def join(self, suffix: str) -> slash3.S3Key:
 80    def join(self, suffix: str) -> "S3Key":
 81        """
 82        Joins a string to the key with a "/" delimiter.
 83
 84        ```python
 85        images = S3Key("images/staff")
 86
 87        steve = images.join("steve.jpg")
 88        # "images/staff/steve.jpg"
 89
 90        # Or use "/":
 91        penny = images / "penny.jpg"
 92        # "images/staff/penny.jpg"
 93        ```
 94
 95        To append a string without a "/" delimiter, use `append()` instead.
 96        """
 97
 98        logger.debug('Joining base "%s" and suffix "%s"', self._key, suffix)
 99
100        base = self.normal_right(self._key, slash=True) if self._key else self._key
101        logger.debug('Base normalised to "%s"', base)
102
103        suffix = self.normal_left(suffix)
104        logger.debug('Suffix normalised to "%s"', suffix)
105
106        return S3Key(base + suffix)

Joins a string to the key with a "/" delimiter.

images = S3Key("images/staff")

steve = images.join("steve.jpg")
# "images/staff/steve.jpg"

# Or use "/":
penny = images / "penny.jpg"
# "images/staff/penny.jpg"

To append a string without a "/" delimiter, use append() instead.

key: str

Key.

For example, "private/clowns.jpg".

leaf: str

Key leaf.

In a file system metaphor, the leaf would be the file's name.

For example, the leaf of "private/clowns.jpg" is "clowns.jpg".

@staticmethod
def normal_left(key: str, slash: bool = False) -> str:
130    @staticmethod
131    def normal_left(key: str, slash: bool = False) -> str:
132        """
133        Normalises the left of the key.
134
135        If `slash` is true then the key is returned with exactly one leading
136        slash, otherwise the key is returned with no leading slashes.
137        """
138
139        while key.startswith("/"):
140            key = key[1:]
141
142        return "/" + key if slash else key

Normalises the left of the key.

If slash is true then the key is returned with exactly one leading slash, otherwise the key is returned with no leading slashes.

@staticmethod
def normal_right(key: str, slash: bool = False) -> str:
144    @staticmethod
145    def normal_right(key: str, slash: bool = False) -> str:
146        """
147        Normalises the right of the key.
148
149        If `slash` is true then the key is returned with exactly one trailing
150        slash, otherwise the key is returned with no trailing slashes.
151        """
152
153        while key.endswith("/"):
154            key = key[:-1]
155
156        return key + "/" if slash else key

Normalises the right of the key.

If slash is true then the key is returned with exactly one trailing slash, otherwise the key is returned with no trailing slashes.

parent: slash3.S3Key

Parent key.

For example, the parent of "private/clowns.jpg" is "private/".

def relative_to(self, parent: Union[slash3.S3Key, str]) -> str:
191    def relative_to(self, parent: Union["S3Key", str]) -> str:
192        """
193        Gets the relative key path from this key to a `parent` key.
194
195        For example, the relative path to "private/clowns.jpg" from parent
196        "private" is "clowns.jpg".
197        """
198
199        logger.debug(
200            'Calculating the relative key from "%s" to "%s"',
201            self,
202            parent,
203        )
204
205        parent = str(parent)
206        normal = self.normal_right(parent, slash=True) if parent else parent
207
208        logger.debug('Normalised the parent key to "%s"', normal)
209
210        if not self._key.startswith(normal):
211            raise ValueError(f'"{parent}" is not a parent of "{self}"')
212
213        relative_key = self._key[len(normal) :]  # noqa: E203
214
215        logger.debug(
216            'The relative path from "%s" to "%s" is "%s"',
217            parent,
218            self,
219            relative_key,
220        )
221
222        return relative_key

Gets the relative key path from this key to a parent key.

For example, the relative path to "private/clowns.jpg" from parent "private" is "clowns.jpg".

class S3Uri:
  8class S3Uri:
  9    """
 10    An Amazon Web Services S3 URI.
 11
 12    To construct a URI from a bucket's name and optional key, use the
 13    `to_uri()` class method instead.
 14    """
 15
 16    def __init__(self, uri: str) -> None:
 17        m = match(r"[sS]3:\/\/([^/]*)(\/(.*))?", uri)
 18
 19        if not m:
 20            raise ValueError(f'"{uri}" is not an S3 URI')
 21
 22        self._bucket = str(m.group(1))
 23        self._key = S3Key(m.group(3))
 24
 25    def __add__(self, suffix: str) -> "S3Uri":
 26        return self.append(suffix)
 27
 28    def __eq__(self, other: Any) -> bool:
 29        return self.uri == str(other)
 30
 31    def __repr__(self) -> str:
 32        return self.uri
 33
 34    def __truediv__(self, suffix: str) -> "S3Uri":
 35        return self.join(suffix)
 36
 37    def append(self, other: str) -> "S3Uri":
 38        """
 39        Appends a string to the URI.
 40
 41        ```python
 42        images = S3Uri("s3://circus/staff-")
 43
 44        steve = images.append("steve.jpg")
 45        # "s3://circus/staff-steve.jpg"
 46
 47        # Or use "+":
 48        penny = images + "penny.jpg"
 49        # "s3://circus/staff-penny.jpg"
 50        ```
 51
 52        To add a suffix with a "/" delimiter, use `join()` instead.
 53        """
 54
 55        return S3Uri.to_uri(self._bucket, self._key.append(other))
 56
 57    @property
 58    def bucket(self) -> str:
 59        """
 60        Bucket.
 61        """
 62
 63        return self._bucket
 64
 65    def join(self, suffix: str) -> "S3Uri":
 66        """
 67        Joins a string to the URI with a "/" delimiter.
 68
 69        ```python
 70        images = S3Uri("s3://circus/staff")
 71
 72        steve = images.join("steve.jpg")
 73        # "s3://circus/staff/steve.jpg"
 74
 75        # Or use "/":
 76        penny = images / "penny.jpg"
 77        # "s3://circus/staff/penny.jpg"
 78        ```
 79
 80        To append a string without a "/" delimiter, use `append()` instead.
 81        """
 82
 83        return S3Uri.to_uri(self._bucket, self._key.join(suffix))
 84
 85    @property
 86    def key(self) -> S3Key:
 87        """
 88        Key.
 89        """
 90
 91        return self._key
 92
 93    @property
 94    def leaf(self) -> str:
 95        """
 96        URI leaf.
 97
 98        In a file system metaphor, the leaf would be the file's name.
 99
100        For example, the leaf of "s3://circus/private/clowns.jpg" is
101        "clowns.jpg".
102        """
103
104        return self.key.leaf
105
106    @property
107    def parent(self) -> "S3Uri":
108        """
109        Parent URI.
110
111        For example, the parent of "s3://circus/private/clowns.jpg" is
112        "s3://circus/private/".
113        """
114
115        return S3Uri.to_uri(self._bucket, self.key.parent)
116
117    def relative_to(self, parent: Union["S3Uri", str]) -> str:
118        """
119        Gets the relative key path from this URI to a `parent` URI.
120
121        For example, the relative path to "s3://circus/private/clowns.jpg" from
122        "s3://circus/" is "private/clowns.jpg".
123        """
124
125        parent = S3Uri(parent) if isinstance(parent, str) else parent
126
127        if parent._bucket != self._bucket:
128            raise ValueError(
129                f'There is no relative path from "{parent}" to "{self}" '
130                "because these URIs describe different buckets"
131            )
132
133        return self._key.relative_to(parent.key)
134
135    @staticmethod
136    def to_string(bucket: str, key: Optional[Union[S3Key, str]]) -> str:
137        """
138        Constructs a string S3 URI from a bucket and optional key.
139
140        To construct an `S3Uri` instance, use `to_uri()` instead.
141        """
142
143        return f"s3://{bucket}/{key or ''}"
144
145    @classmethod
146    def to_uri(
147        cls,
148        bucket: str,
149        key: Optional[Union[S3Key, str]] = None,
150    ) -> "S3Uri":
151        """
152        Constructs an `S3Uri` from a bucket and optional key.
153        """
154
155        return cls(cls.to_string(bucket, key=key))
156
157    @property
158    def uri(self) -> str:
159        """
160        URI.
161        """
162
163        return self.to_string(self._bucket, self._key)

An Amazon Web Services S3 URI.

To construct a URI from a bucket's name and optional key, use the to_uri() class method instead.

S3Uri(uri: str)
16    def __init__(self, uri: str) -> None:
17        m = match(r"[sS]3:\/\/([^/]*)(\/(.*))?", uri)
18
19        if not m:
20            raise ValueError(f'"{uri}" is not an S3 URI')
21
22        self._bucket = str(m.group(1))
23        self._key = S3Key(m.group(3))
def append(self, other: str) -> slash3.S3Uri:
37    def append(self, other: str) -> "S3Uri":
38        """
39        Appends a string to the URI.
40
41        ```python
42        images = S3Uri("s3://circus/staff-")
43
44        steve = images.append("steve.jpg")
45        # "s3://circus/staff-steve.jpg"
46
47        # Or use "+":
48        penny = images + "penny.jpg"
49        # "s3://circus/staff-penny.jpg"
50        ```
51
52        To add a suffix with a "/" delimiter, use `join()` instead.
53        """
54
55        return S3Uri.to_uri(self._bucket, self._key.append(other))

Appends a string to the URI.

images = S3Uri("s3://circus/staff-")

steve = images.append("steve.jpg")
# "s3://circus/staff-steve.jpg"

# Or use "+":
penny = images + "penny.jpg"
# "s3://circus/staff-penny.jpg"

To add a suffix with a "/" delimiter, use join() instead.

bucket: str

Bucket.

def join(self, suffix: str) -> slash3.S3Uri:
65    def join(self, suffix: str) -> "S3Uri":
66        """
67        Joins a string to the URI with a "/" delimiter.
68
69        ```python
70        images = S3Uri("s3://circus/staff")
71
72        steve = images.join("steve.jpg")
73        # "s3://circus/staff/steve.jpg"
74
75        # Or use "/":
76        penny = images / "penny.jpg"
77        # "s3://circus/staff/penny.jpg"
78        ```
79
80        To append a string without a "/" delimiter, use `append()` instead.
81        """
82
83        return S3Uri.to_uri(self._bucket, self._key.join(suffix))

Joins a string to the URI with a "/" delimiter.

images = S3Uri("s3://circus/staff")

steve = images.join("steve.jpg")
# "s3://circus/staff/steve.jpg"

# Or use "/":
penny = images / "penny.jpg"
# "s3://circus/staff/penny.jpg"

To append a string without a "/" delimiter, use append() instead.

Key.

leaf: str

URI leaf.

In a file system metaphor, the leaf would be the file's name.

For example, the leaf of "s3://circus/private/clowns.jpg" is "clowns.jpg".

parent: slash3.S3Uri

Parent URI.

For example, the parent of "s3://circus/private/clowns.jpg" is "s3://circus/private/".

def relative_to(self, parent: Union[slash3.S3Uri, str]) -> str:
117    def relative_to(self, parent: Union["S3Uri", str]) -> str:
118        """
119        Gets the relative key path from this URI to a `parent` URI.
120
121        For example, the relative path to "s3://circus/private/clowns.jpg" from
122        "s3://circus/" is "private/clowns.jpg".
123        """
124
125        parent = S3Uri(parent) if isinstance(parent, str) else parent
126
127        if parent._bucket != self._bucket:
128            raise ValueError(
129                f'There is no relative path from "{parent}" to "{self}" '
130                "because these URIs describe different buckets"
131            )
132
133        return self._key.relative_to(parent.key)

Gets the relative key path from this URI to a parent URI.

For example, the relative path to "s3://circus/private/clowns.jpg" from "s3://circus/" is "private/clowns.jpg".

@staticmethod
def to_string(bucket: str, key: Union[slash3.S3Key, str, NoneType]) -> str:
135    @staticmethod
136    def to_string(bucket: str, key: Optional[Union[S3Key, str]]) -> str:
137        """
138        Constructs a string S3 URI from a bucket and optional key.
139
140        To construct an `S3Uri` instance, use `to_uri()` instead.
141        """
142
143        return f"s3://{bucket}/{key or ''}"

Constructs a string S3 URI from a bucket and optional key.

To construct an S3Uri instance, use to_uri() instead.

@classmethod
def to_uri( cls, bucket: str, key: Union[slash3.S3Key, str, NoneType] = None) -> slash3.S3Uri:
145    @classmethod
146    def to_uri(
147        cls,
148        bucket: str,
149        key: Optional[Union[S3Key, str]] = None,
150    ) -> "S3Uri":
151        """
152        Constructs an `S3Uri` from a bucket and optional key.
153        """
154
155        return cls(cls.to_string(bucket, key=key))

Constructs an S3Uri from a bucket and optional key.

uri: str

URI.