0
# Path Operations
1
2
Path manipulation, normalization, and joining operations for URL path components, including filename and extension handling with support for complex path manipulations.
3
4
## Capabilities
5
6
### Path Construction and Joining
7
8
Methods for building and extending URL paths with proper encoding and normalization.
9
10
```python { .api }
11
def joinpath(self, *other: str, encoded: bool = False) -> "URL":
12
"""
13
Join URL path with additional path segments.
14
15
Args:
16
*other (str): Path segments to append
17
encoded (bool): Whether path segments are already encoded
18
19
Returns:
20
URL: New URL with joined path segments
21
22
Examples:
23
url.joinpath('api', 'v1', 'users')
24
url.joinpath('files', 'document.pdf')
25
url.joinpath('path with spaces', encoded=False)
26
"""
27
28
def join(self, url: "URL") -> "URL":
29
"""
30
Join this URL with another URL using RFC 3986 resolution rules.
31
32
Args:
33
url (URL): URL to resolve against this URL
34
35
Returns:
36
URL: New URL after RFC 3986 resolution
37
38
Examples:
39
base.join(URL('path/to/resource'))
40
base.join(URL('../parent/resource'))
41
base.join(URL('?query=value'))
42
"""
43
44
def __truediv__(self, name: str) -> "URL":
45
"""
46
Append path segment using / operator.
47
48
Args:
49
name (str): Path segment to append
50
51
Returns:
52
URL: New URL with appended path segment
53
54
Examples:
55
url / 'api' / 'v1' / 'users'
56
url / 'documents' / 'file.pdf'
57
"""
58
```
59
60
### Path Component Access
61
62
Properties for accessing path components and segments.
63
64
```python { .api }
65
@property
66
def raw_parts(self) -> tuple[str, ...]:
67
"""
68
Raw path segments as tuple.
69
70
Returns:
71
tuple[str, ...]: Tuple of raw (encoded) path segments
72
73
Examples:
74
URL('/path/to/file').raw_parts # ('/', 'path', 'to', 'file')
75
URL('/api/v1/').raw_parts # ('/', 'api', 'v1', '')
76
"""
77
78
@property
79
def parts(self) -> tuple[str, ...]:
80
"""
81
Decoded path segments as tuple.
82
83
Returns:
84
tuple[str, ...]: Tuple of decoded path segments
85
"""
86
87
@property
88
def parent(self) -> "URL":
89
"""
90
Parent URL (removes last path segment).
91
92
Returns:
93
URL: New URL with last path segment removed
94
95
Examples:
96
URL('/path/to/file').parent # URL('/path/to')
97
URL('/api/v1/users').parent # URL('/api/v1')
98
URL('/').parent # URL('/')
99
"""
100
101
@property
102
def raw_path(self) -> str:
103
"""Raw (encoded) path component"""
104
105
@property
106
def path(self) -> str:
107
"""Decoded path component"""
108
109
@property
110
def path_safe(self) -> str:
111
"""Path with safe decoding (preserves important characters like /)"""
112
```
113
114
### Filename and Extension Operations
115
116
Methods and properties for working with filename components of paths.
117
118
```python { .api }
119
@property
120
def raw_name(self) -> str:
121
"""
122
Raw filename component (last path segment).
123
124
Returns:
125
str: Raw filename or empty string if path ends with /
126
"""
127
128
@property
129
def name(self) -> str:
130
"""
131
Decoded filename component (last path segment).
132
133
Returns:
134
str: Decoded filename or empty string if path ends with /
135
136
Examples:
137
URL('/documents/report.pdf').name # 'report.pdf'
138
URL('/api/users/').name # ''
139
URL('/path/to/file').name # 'file'
140
"""
141
142
@property
143
def raw_suffix(self) -> str:
144
"""
145
Raw file extension including leading dot.
146
147
Returns:
148
str: Raw file extension or empty string if no extension
149
"""
150
151
@property
152
def suffix(self) -> str:
153
"""
154
Decoded file extension including leading dot.
155
156
Returns:
157
str: Decoded file extension or empty string if no extension
158
159
Examples:
160
URL('/file.txt').suffix # '.txt'
161
URL('/archive.tar.gz').suffix # '.gz'
162
URL('/README').suffix # ''
163
"""
164
165
@property
166
def raw_suffixes(self) -> tuple[str, ...]:
167
"""
168
All raw file extensions as tuple.
169
170
Returns:
171
tuple[str, ...]: Tuple of all raw extensions
172
"""
173
174
@property
175
def suffixes(self) -> tuple[str, ...]:
176
"""
177
All decoded file extensions as tuple.
178
179
Returns:
180
tuple[str, ...]: Tuple of all decoded extensions
181
182
Examples:
183
URL('/file.txt').suffixes # ('.txt',)
184
URL('/archive.tar.gz').suffixes # ('.tar', '.gz')
185
URL('/document.backup.old').suffixes # ('.backup', '.old')
186
"""
187
188
def with_name(self, name: str, *, encoded: bool = False) -> "URL":
189
"""
190
Return URL with new filename component.
191
192
Args:
193
name (str): New filename to replace current filename
194
encoded (bool): Whether name is already encoded
195
196
Returns:
197
URL: New URL with updated filename
198
199
Raises:
200
ValueError: If name contains path separators
201
202
Examples:
203
url.with_name('newfile.txt')
204
url.with_name('document with spaces.pdf')
205
"""
206
207
def with_suffix(self, suffix: str, *, encoded: bool = False) -> "URL":
208
"""
209
Return URL with new file extension.
210
211
Args:
212
suffix (str): New file extension (should include leading dot)
213
encoded (bool): Whether suffix is already encoded
214
215
Returns:
216
URL: New URL with updated file extension
217
218
Examples:
219
url.with_suffix('.json')
220
url.with_suffix('.backup.txt')
221
"""
222
```
223
224
### Path Normalization
225
226
Built-in path normalization handles relative path components and ensures clean paths.
227
228
Path normalization automatically:
229
- Resolves `.` (current directory) references
230
- Resolves `..` (parent directory) references
231
- Removes duplicate slashes
232
- Preserves trailing slashes when semantically significant
233
234
## Usage Examples
235
236
### Basic Path Operations
237
238
```python
239
from yarl import URL
240
241
base_url = URL('https://example.com/api/v1')
242
243
# Append path segments
244
users_url = base_url.joinpath('users')
245
print(users_url) # https://example.com/api/v1/users
246
247
user_url = base_url.joinpath('users', '123')
248
print(user_url) # https://example.com/api/v1/users/123
249
250
# Multiple segments at once
251
deep_url = base_url.joinpath('resources', 'documents', 'files')
252
print(deep_url) # https://example.com/api/v1/resources/documents/files
253
```
254
255
### Using the / Operator
256
257
```python
258
from yarl import URL
259
260
api_url = URL('https://api.example.com')
261
262
# Chain path segments
263
endpoint = api_url / 'v2' / 'users' / '456' / 'profile'
264
print(endpoint) # https://api.example.com/v2/users/456/profile
265
266
# Mix with other operations
267
full_url = (api_url
268
/ 'search'
269
/ 'documents'
270
% {'q': 'python', 'limit': 10})
271
print(full_url) # https://api.example.com/search/documents?q=python&limit=10
272
```
273
274
### Working with Filenames
275
276
```python
277
from yarl import URL
278
279
file_url = URL('https://cdn.example.com/documents/report.pdf')
280
281
# Access filename components
282
print(file_url.name) # 'report.pdf'
283
print(file_url.suffix) # '.pdf'
284
print(file_url.suffixes) # ('.pdf',)
285
286
# Modify filename
287
new_file = file_url.with_name('summary.pdf')
288
print(new_file) # https://cdn.example.com/documents/summary.pdf
289
290
# Change extension
291
txt_version = file_url.with_suffix('.txt')
292
print(txt_version) # https://cdn.example.com/documents/report.txt
293
294
# Work with parent directories
295
parent = file_url.parent
296
print(parent) # https://cdn.example.com/documents
297
298
grandparent = file_url.parent.parent
299
print(grandparent) # https://cdn.example.com
300
```
301
302
### Complex File Extensions
303
304
```python
305
from yarl import URL
306
307
archive_url = URL('https://example.com/files/backup.tar.gz')
308
309
print(archive_url.suffix) # '.gz' (last extension)
310
print(archive_url.suffixes) # ('.tar', '.gz') (all extensions)
311
312
# Change just the compression
313
uncompressed = archive_url.with_suffix('.tar')
314
print(uncompressed) # https://example.com/files/backup.tar
315
316
# Multiple extensions
317
config_url = URL('https://example.com/config.local.dev.json')
318
print(config_url.suffixes) # ('.local', '.dev', '.json')
319
320
# Remove all extensions by changing name
321
base_name = config_url.name.split('.')[0] # 'config'
322
clean_url = config_url.with_name(base_name)
323
print(clean_url) # https://example.com/config
324
```
325
326
### Path Segments and Navigation
327
328
```python
329
from yarl import URL
330
331
deep_url = URL('https://example.com/api/v1/users/123/documents/456')
332
333
# Access path segments
334
print(deep_url.parts) # ('/', 'api', 'v1', 'users', '123', 'documents', '456')
335
336
# Navigate up the hierarchy
337
print(deep_url.parent) # https://example.com/api/v1/users/123/documents
338
print(deep_url.parent.parent) # https://example.com/api/v1/users/123
339
print(deep_url.parent.parent.parent) # https://example.com/api/v1/users
340
341
# Build new paths from segments
342
segments = deep_url.parts[1:4] # ('api', 'v1', 'users')
343
new_base = URL('https://example.com').joinpath(*segments)
344
print(new_base) # https://example.com/api/v1/users
345
```
346
347
### Path Normalization Examples
348
349
```python
350
from yarl import URL
351
352
# Relative path resolution
353
messy_url = URL('https://example.com/api/../docs/./guide/../tutorial/basics.html')
354
print(messy_url) # Automatically normalized to: https://example.com/docs/tutorial/basics.html
355
356
# Join with relative paths
357
base = URL('https://example.com/api/v1/users')
358
relative_join = base.join(URL('../docs/guide.html'))
359
print(relative_join) # https://example.com/api/v1/docs/guide.html
360
361
# Complex relative navigation
362
complex_base = URL('https://example.com/app/module/submodule/')
363
back_and_forward = complex_base.join(URL('../../other/module/file.js'))
364
print(back_and_forward) # https://example.com/app/other/module/file.js
365
```
366
367
### URL Resolution (RFC 3986)
368
369
```python
370
from yarl import URL
371
372
base_url = URL('https://example.com/app/current/page')
373
374
# Absolute URL resolution
375
absolute_result = base_url.join(URL('https://other.com/resource'))
376
print(absolute_result) # https://other.com/resource
377
378
# Relative path resolution
379
relative_result = base_url.join(URL('other/resource'))
380
print(relative_result) # https://example.com/app/current/other/resource
381
382
# Parent directory resolution
383
parent_result = base_url.join(URL('../sibling/resource'))
384
print(parent_result) # https://example.com/app/sibling/resource
385
386
# Query-only resolution
387
query_result = base_url.join(URL('?query=value'))
388
print(query_result) # https://example.com/app/current/page?query=value
389
390
# Fragment-only resolution
391
fragment_result = base_url.join(URL('#section'))
392
print(fragment_result) # https://example.com/app/current/page#section
393
```
394
395
### Special Path Cases
396
397
```python
398
from yarl import URL
399
400
# Root path operations
401
root_url = URL('https://example.com/')
402
api_from_root = root_url / 'api' / 'v1'
403
print(api_from_root) # https://example.com/api/v1
404
405
# Empty path components
406
empty_url = URL('https://example.com')
407
with_empty = empty_url.joinpath('', 'api', '', 'users')
408
print(with_empty) # https://example.com/api/users (empty segments ignored)
409
410
# Trailing slash preservation
411
trailing_url = URL('https://example.com/api/')
412
print(trailing_url.joinpath('users')) # https://example.com/api/users
413
print(trailing_url / 'users') # https://example.com/api/users
414
415
# Special characters in paths
416
special_url = URL('https://example.com').joinpath('files', 'my document.pdf')
417
print(special_url) # https://example.com/files/my%20document.pdf (automatically encoded)
418
```