0
# Path Manipulation
1
2
The Path class provides comprehensive URL path manipulation with segment-based access, automatic percent-encoding/decoding, and support for both absolute and relative paths. Path objects handle the complexities of URL path formatting while providing an intuitive interface.
3
4
## Capabilities
5
6
### Path Construction and Loading
7
8
Create and load path objects from strings with automatic segment parsing and encoding handling.
9
10
```python { .api }
11
class Path:
12
def __init__(self, path='', force_absolute=lambda _: False, strict=False):
13
"""
14
Initialize a Path object.
15
16
Args:
17
path (str): Path string to parse
18
force_absolute (callable): Function to determine if path should be absolute
19
strict (bool): Enable strict parsing mode
20
"""
21
22
def load(self, path):
23
"""
24
Load a path string into the Path object.
25
26
Args:
27
path (str): Path string to parse and load
28
29
Returns:
30
Path: Self for method chaining
31
"""
32
```
33
34
**Usage:**
35
36
```python
37
from furl import Path
38
39
# Create from string
40
p = Path('/api/v1/users')
41
print(p.segments) # ['api', 'v1', 'users']
42
43
# Create empty path
44
p = Path()
45
p.load('/new/path/here')
46
print(p.segments) # ['new', 'path', 'here']
47
48
# Relative path
49
rel_path = Path('relative/path')
50
print(rel_path.isabsolute) # False
51
```
52
53
### Segment Manipulation
54
55
Direct manipulation of path segments with automatic encoding and path reconstruction.
56
57
```python { .api }
58
class Path:
59
@property
60
def segments(self) -> list[str]:
61
"""
62
List of path segments (percent-decoded).
63
Segments are automatically decoded and can be manipulated directly.
64
"""
65
66
@segments.setter
67
def segments(self, segments: list[str]):
68
"""Set path segments list"""
69
70
def add(self, path):
71
"""
72
Add path segments to the existing path.
73
74
Args:
75
path (str): Path string with segments to add
76
77
Returns:
78
Path: Self for method chaining
79
"""
80
81
def set(self, path):
82
"""
83
Set the path, replacing all existing segments.
84
85
Args:
86
path (str): New path string
87
88
Returns:
89
Path: Self for method chaining
90
"""
91
92
def remove(self, path):
93
"""
94
Remove path segments.
95
96
Args:
97
path (str|bool): Path segments to remove, or True to remove all
98
99
Returns:
100
Path: Self for method chaining
101
"""
102
```
103
104
**Usage:**
105
106
```python
107
p = Path('/api/v1')
108
109
# Direct segment manipulation
110
p.segments.append('users')
111
p.segments.append('123')
112
print(str(p)) # '/api/v1/users/123'
113
114
# Add path segments
115
p.add('profile')
116
print(str(p)) # '/api/v1/users/123/profile'
117
118
# Set new path (replaces existing)
119
p.set('/different/path')
120
print(p.segments) # ['different', 'path']
121
122
# Remove segments
123
p.remove('path')
124
print(p.segments) # ['different']
125
126
# Remove all segments
127
p.remove(True)
128
print(str(p)) # '/'
129
```
130
131
### Path Properties
132
133
Access path characteristics and metadata.
134
135
```python { .api }
136
class Path:
137
@property
138
def isabsolute(self) -> bool:
139
"""
140
Boolean indicating if path is absolute (starts with /).
141
For URL paths with netloc, this becomes read-only True.
142
"""
143
144
@isabsolute.setter
145
def isabsolute(self, isabsolute: bool):
146
"""Set absolute path flag (may be read-only in URL context)"""
147
148
@property
149
def isdir(self) -> bool:
150
"""
151
Boolean indicating if path represents a directory.
152
True if path ends with empty segment (trailing slash).
153
"""
154
155
@property
156
def isfile(self) -> bool:
157
"""
158
Boolean indicating if path represents a file.
159
True if path doesn't end with empty segment (no trailing slash).
160
"""
161
```
162
163
**Usage:**
164
165
```python
166
# Directory path (ends with /)
167
dir_path = Path('/api/v1/')
168
print(dir_path.isdir) # True
169
print(dir_path.isfile) # False
170
print(dir_path.segments) # ['api', 'v1', '']
171
172
# File path (no trailing /)
173
file_path = Path('/api/v1/users')
174
print(file_path.isdir) # False
175
print(file_path.isfile) # True
176
177
# Absolute vs relative
178
abs_path = Path('/absolute/path')
179
print(abs_path.isabsolute) # True
180
181
rel_path = Path('relative/path')
182
print(rel_path.isabsolute) # False
183
184
# Make relative path absolute
185
rel_path.isabsolute = True
186
print(str(rel_path)) # '/relative/path'
187
```
188
189
### Path Normalization
190
191
Normalize paths by resolving relative references and cleaning segments.
192
193
```python { .api }
194
class Path:
195
def normalize(self):
196
"""
197
Normalize the path by resolving . and .. segments.
198
199
Returns:
200
Path: Self for method chaining
201
"""
202
```
203
204
**Usage:**
205
206
```python
207
# Path with relative references
208
messy_path = Path('/api/../api/v1/./users/../users')
209
print(messy_path.segments) # ['api', '..', 'api', 'v1', '.', 'users', '..', 'users']
210
211
messy_path.normalize()
212
print(messy_path.segments) # ['api', 'v1', 'users']
213
print(str(messy_path)) # '/api/v1/users'
214
```
215
216
### Path String Operations
217
218
Convert paths to strings and perform path operations.
219
220
```python { .api }
221
class Path:
222
def __str__(self) -> str:
223
"""
224
Convert path to string with proper encoding.
225
226
Returns:
227
str: Percent-encoded path string
228
"""
229
230
def __truediv__(self, path) -> 'Path':
231
"""
232
Path division operator (/) for joining paths.
233
234
Args:
235
path (str): Path segment to join
236
237
Returns:
238
Path: New Path object with joined path
239
"""
240
241
def __eq__(self, other) -> bool:
242
"""Check path equality"""
243
244
def __bool__(self) -> bool:
245
"""Boolean evaluation (True if path has segments)"""
246
247
def asdict(self) -> dict:
248
"""
249
Convert path to dictionary representation.
250
251
Returns:
252
dict: Dictionary with path metadata
253
"""
254
```
255
256
**Usage:**
257
258
```python
259
base_path = Path('/api/v1')
260
261
# String conversion with encoding
262
path_with_spaces = Path('/path with spaces/and special chars!')
263
print(str(path_with_spaces)) # '/path%20with%20spaces/and%20special%20chars!'
264
265
# Path joining with / operator
266
full_path = base_path / 'users' / '123'
267
print(str(full_path)) # '/api/v1/users/123'
268
269
# Path comparison
270
path1 = Path('/api/v1')
271
path2 = Path('/api/v1')
272
print(path1 == path2) # True
273
274
# Boolean evaluation
275
empty_path = Path()
276
print(bool(empty_path)) # False
277
278
non_empty = Path('/something')
279
print(bool(non_empty)) # True
280
281
# Dictionary representation
282
path_dict = base_path.asdict()
283
print(path_dict) # Contains path metadata
284
```
285
286
## Encoding and Unicode Support
287
288
Paths automatically handle percent-encoding and Unicode characters:
289
290
```python
291
# Unicode characters in paths
292
unicode_path = Path('/пользователи/123')
293
print(str(unicode_path)) # Properly encoded
294
295
# Percent-encoded input
296
encoded_path = Path('/users%20with%20spaces')
297
print(encoded_path.segments) # ['users with spaces'] - automatically decoded
298
299
# Special characters
300
special_path = Path('/special/chars/^`<>[]"#/?')
301
print(str(special_path)) # '/special/chars/%5E%60%3C%3E%5B%5D%22%23%2F%3F'
302
```
303
304
## URL Context Behavior
305
306
When a Path is part of a furl object with a netloc (host), certain behaviors change:
307
308
```python
309
from furl import furl
310
311
# Path in URL context
312
f = furl('http://example.com/path')
313
print(f.path.isabsolute) # True (required for URLs with netloc)
314
315
# Attempting to make URL path relative raises error
316
try:
317
f.path.isabsolute = False
318
except AttributeError as e:
319
print("Cannot make URL path relative when netloc is present")
320
321
# Fragment paths don't have this restriction
322
f.fragment.path.isabsolute = False # This works fine
323
```
324
325
## Error Handling
326
327
Path objects handle various error conditions:
328
329
- **Invalid characters**: Special characters are automatically encoded
330
- **Relative references**: `..` and `.` segments can be normalized
331
- **Empty segments**: Handled correctly for directory vs file distinction
332
- **Unicode support**: Proper encoding/decoding of international characters
333
- **Strict mode**: Additional validation when `strict=True`