0
# uritemplate
1
2
A Python implementation of RFC 6570 URI Templates that enables creation and expansion of URI templates according to the official specification. Provides both class-based and functional interfaces for creating dynamic URIs from templates and variable substitutions.
3
4
## Package Information
5
6
- **Package Name**: uritemplate
7
- **Language**: Python
8
- **Installation**: `pip install uritemplate`
9
10
## Core Imports
11
12
```python
13
from uritemplate import URITemplate, expand, partial, variables
14
```
15
16
Alternative imports for specific functionality:
17
18
```python
19
from uritemplate import URITemplate # Class-based approach
20
from uritemplate import expand # Direct expansion function
21
from uritemplate import partial # Partial expansion function
22
from uritemplate import variables # Variable extraction function
23
```
24
25
Internal types and classes (for advanced usage):
26
27
```python
28
from uritemplate.orderedset import OrderedSet
29
from uritemplate.variable import URIVariable, VariableValue, VariableValueDict, ScalarVariableValue
30
```
31
32
## Basic Usage
33
34
```python
35
from uritemplate import URITemplate, expand
36
37
# Basic template expansion using the URITemplate class
38
template = URITemplate('https://api.github.com/users/{username}/repos')
39
uri = template.expand(username='octocat')
40
print(uri) # https://api.github.com/users/octocat/repos
41
42
# Direct expansion using the expand function
43
uri = expand('https://api.github.com/users/{username}/repos', username='octocat')
44
print(uri) # https://api.github.com/users/octocat/repos
45
46
# Multiple variables with different expansion operators
47
api_template = URITemplate('https://api.github.com{/endpoint}{?page,per_page}')
48
uri = api_template.expand(endpoint='repos', page=2, per_page=50)
49
print(uri) # https://api.github.com/repos?page=2&per_page=50
50
51
# Using dictionary for variable values
52
variables_dict = {'username': 'octocat', 'repo': 'Hello-World'}
53
repo_template = URITemplate('https://api.github.com/repos/{username}/{repo}')
54
uri = repo_template.expand(variables_dict)
55
print(uri) # https://api.github.com/repos/octocat/Hello-World
56
```
57
58
## Architecture
59
60
The uritemplate package follows a simple, focused design with two main interfaces:
61
62
- **Functional Interface**: Direct functions (`expand`, `partial`, `variables`) for immediate operations
63
- **Class-based Interface**: `URITemplate` class for reusable template objects with parsing optimization
64
65
Core components:
66
- **URITemplate**: Main template class that parses and expands URI templates
67
- **URIVariable**: Internal variable representation supporting all RFC 6570 operators
68
- **OrderedSet**: Custom ordered set implementation for maintaining variable name order
69
- **Operator**: Enum defining URI template expansion operators (simple, reserved, fragment, etc.)
70
71
## Capabilities
72
73
### Template Expansion
74
75
Expand URI templates with variable substitutions using either the functional or class-based interface.
76
77
```python { .api }
78
def expand(
79
uri: str,
80
var_dict: Optional[VariableValueDict] = None,
81
**kwargs: VariableValue
82
) -> str:
83
"""
84
Expand the URI template with the given parameters.
85
86
Parameters:
87
- uri: The templated URI to expand
88
- var_dict: Optional dictionary with variables and values
89
- **kwargs: Alternative way to pass arguments
90
91
Returns:
92
Expanded URI string
93
"""
94
```
95
96
```python { .api }
97
class URITemplate:
98
def __init__(self, uri: str):
99
"""
100
Initialize a URI template.
101
102
Parameters:
103
- uri: The URI template string
104
"""
105
106
def expand(
107
self,
108
var_dict: Optional[VariableValueDict] = None,
109
**kwargs: VariableValue
110
) -> str:
111
"""
112
Expand the template with the given parameters.
113
114
Parameters:
115
- var_dict: Optional dictionary with variables and values
116
- **kwargs: Alternative way to pass arguments
117
118
Returns:
119
Expanded URI string
120
"""
121
```
122
123
### Partial Template Expansion
124
125
Partially expand templates, leaving unresolved variables as template expressions for later expansion.
126
127
```python { .api }
128
def partial(
129
uri: str,
130
var_dict: Optional[VariableValueDict] = None,
131
**kwargs: VariableValue
132
) -> URITemplate:
133
"""
134
Partially expand the template with the given parameters.
135
136
If all parameters for the template are not given, return a
137
partially expanded template.
138
139
Parameters:
140
- uri: The templated URI to expand
141
- var_dict: Optional dictionary with variables and values
142
- **kwargs: Alternative way to pass arguments
143
144
Returns:
145
URITemplate instance with partially expanded template
146
"""
147
```
148
149
```python { .api }
150
class URITemplate:
151
def partial(
152
self,
153
var_dict: Optional[VariableValueDict] = None,
154
**kwargs: VariableValue
155
) -> URITemplate:
156
"""
157
Partially expand the template with the given parameters.
158
159
Parameters:
160
- var_dict: Optional dictionary with variables and values
161
- **kwargs: Alternative way to pass arguments
162
163
Returns:
164
URITemplate instance with partially expanded template
165
"""
166
```
167
168
#### Usage Example
169
170
```python
171
from uritemplate import partial, URITemplate
172
173
# Partial expansion with function
174
template_str = 'https://api.github.com/repos/{owner}/{repo}{/path}{?page,per_page}'
175
partial_template = partial(template_str, owner='octocat', repo='Hello-World')
176
print(str(partial_template)) # https://api.github.com/repos/octocat/Hello-World{/path}{?page,per_page}
177
178
# Complete the expansion later
179
final_uri = partial_template.expand(path='issues', page=1)
180
print(final_uri) # https://api.github.com/repos/octocat/Hello-World/issues?page=1
181
182
# Partial expansion with class
183
template = URITemplate('https://api.example.com{/version}/users{/user_id}{?fields}')
184
partial_result = template.partial(version='v1')
185
final_uri = partial_result.expand(user_id=123, fields='name,email')
186
print(final_uri) # https://api.example.com/v1/users/123?fields=name,email
187
```
188
189
### Variable Extraction
190
191
Extract all variable names from a URI template for introspection and validation.
192
193
```python { .api }
194
def variables(uri: str) -> OrderedSet:
195
"""
196
Parse the variables of the template.
197
198
This returns all of the variable names in the URI Template.
199
200
Parameters:
201
- uri: The URI template string
202
203
Returns:
204
OrderedSet of variable names found in the template
205
"""
206
```
207
208
#### Usage Example
209
210
```python
211
from uritemplate import variables
212
213
# Extract variables from template
214
template_str = 'https://api.github.com/repos/{owner}/{repo}/issues{/number}{?state,labels}'
215
vars_found = variables(template_str)
216
print(list(vars_found)) # ['owner', 'repo', 'number', 'state', 'labels']
217
218
# Check if specific variables are present
219
if 'owner' in vars_found and 'repo' in vars_found:
220
print("Template requires owner and repo parameters")
221
222
# Get variable count
223
print(f"Template contains {len(vars_found)} variables")
224
```
225
226
### URI Template Class Properties
227
228
Access template properties and metadata through the URITemplate class.
229
230
```python { .api }
231
class URITemplate:
232
@property
233
def uri(self) -> str:
234
"""The original URI template string."""
235
236
@property
237
def variables(self) -> List[URIVariable]:
238
"""List of URIVariable objects representing parsed variables."""
239
240
@property
241
def variable_names(self) -> OrderedSet:
242
"""Set of variable names in the URI template."""
243
```
244
245
#### Usage Example
246
247
```python
248
from uritemplate import URITemplate
249
250
template = URITemplate('https://api.github.com/{endpoint}{?page,per_page}')
251
252
# Access original template string
253
print(template.uri) # https://api.github.com/{endpoint}{?page,per_page}
254
255
# Get variable names
256
print(list(template.variable_names)) # ['endpoint', 'page', 'per_page']
257
258
# Template comparison and hashing
259
template1 = URITemplate('https://api.example.com/{id}')
260
template2 = URITemplate('https://api.example.com/{id}')
261
print(template1 == template2) # True
262
263
# Templates can be used as dictionary keys
264
template_cache = {template1: "cached_result"}
265
```
266
267
### Internal Components (Advanced Usage)
268
269
Access to lower-level components for advanced template manipulation and introspection.
270
271
```python { .api }
272
class URIVariable:
273
def __init__(self, var: str):
274
"""
275
Initialize a URI variable from a template expression.
276
277
Parameters:
278
- var: The variable expression string (e.g., "var", "+var", "?var,x,y")
279
"""
280
281
def expand(self, var_dict: Optional[VariableValueDict] = None) -> Mapping[str, str]:
282
"""
283
Expand this variable using the provided variable dictionary.
284
285
Parameters:
286
- var_dict: Dictionary of variable names to values
287
288
Returns:
289
Dictionary mapping the original variable expression to its expanded form
290
"""
291
292
# Properties
293
original: str # The original variable expression
294
operator: Operator # The expansion operator used
295
variables: List[Tuple[str, Dict[str, Any]]] # Variable names and their options
296
variable_names: List[str] # List of variable names in this expression
297
defaults: Dict[str, str] # Default values for variables
298
```
299
300
#### Usage Example
301
302
```python
303
from uritemplate.variable import URIVariable
304
305
# Parse a complex variable expression
306
var = URIVariable("?var,hello,x,y")
307
print(var.variable_names) # ['var', 'hello', 'x', 'y']
308
print(var.operator) # Operator.form_style_query
309
310
# Expand the variable
311
expansion = var.expand({
312
'var': 'value',
313
'hello': 'Hello World!',
314
'x': '1024',
315
'y': '768'
316
})
317
print(expansion) # {'?var,hello,x,y': '?var=value&hello=Hello%20World%21&x=1024&y=768'}
318
```
319
320
## Types
321
322
### Main API Types
323
324
```python { .api }
325
class URITemplate:
326
def __init__(self, uri: str): ...
327
def expand(
328
self,
329
var_dict: Optional[VariableValueDict] = None,
330
**kwargs: VariableValue
331
) -> str: ...
332
def partial(
333
self,
334
var_dict: Optional[VariableValueDict] = None,
335
**kwargs: VariableValue
336
) -> URITemplate: ...
337
338
# Properties
339
uri: str # The original URI template string
340
variables: List[URIVariable] # List of parsed variable objects
341
variable_names: OrderedSet # Set of variable names in the template
342
343
# Standard methods
344
def __str__(self) -> str: ...
345
def __repr__(self) -> str: ...
346
def __eq__(self, other: object) -> bool: ...
347
def __hash__(self) -> int: ...
348
349
def expand(
350
uri: str,
351
var_dict: Optional[VariableValueDict] = None,
352
**kwargs: VariableValue
353
) -> str: ...
354
355
def partial(
356
uri: str,
357
var_dict: Optional[VariableValueDict] = None,
358
**kwargs: VariableValue
359
) -> URITemplate: ...
360
361
def variables(uri: str) -> OrderedSet: ...
362
```
363
364
### Supporting Types
365
366
```python { .api }
367
class OrderedSet:
368
def __init__(self, iterable: Optional[Iterable[str]] = None): ...
369
def add(self, key: str) -> None: ...
370
def discard(self, key: str) -> None: ...
371
def pop(self, last: bool = True) -> str: ...
372
def __len__(self) -> int: ...
373
def __contains__(self, key: object) -> bool: ...
374
def __iter__(self) -> Generator[str, None, None]: ...
375
def __reversed__(self) -> Generator[str, None, None]: ...
376
377
class URIVariable:
378
def __init__(self, var: str): ...
379
def expand(self, var_dict: Optional[VariableValueDict] = None) -> Mapping[str, str]: ...
380
381
# Properties
382
original: str # The original variable expression
383
operator: Operator # The expansion operator
384
variables: List[Tuple[str, Dict[str, Any]]] # Variable names and options
385
variable_names: List[str] # List of variable names
386
defaults: Dict[str, str] # Default values for variables
387
388
class Operator(Enum):
389
"""RFC 6570 URI Template expansion operators."""
390
default = "" # Simple string expansion: {var}
391
reserved = "+" # Reserved expansion: {+var}
392
fragment = "#" # Fragment expansion: {#var}
393
label_with_dot_prefix = "." # Label expansion: {.var}
394
path_segment = "/" # Path segment expansion: {/var}
395
path_style_parameter = ";" # Path-style parameter expansion: {;var}
396
form_style_query = "?" # Form-style query expansion: {?var}
397
form_style_query_continuation = "&" # Form-style query continuation: {&var}
398
399
# Type aliases for variable values
400
ScalarVariableValue = Union[int, float, complex, str, None]
401
VariableValue = Union[
402
Sequence[ScalarVariableValue],
403
List[ScalarVariableValue],
404
Mapping[str, ScalarVariableValue],
405
Tuple[str, ScalarVariableValue],
406
ScalarVariableValue
407
]
408
VariableValueDict = Dict[str, VariableValue]
409
```
410
411
## Advanced Usage Patterns
412
413
### Complex Variable Types
414
415
```python
416
from uritemplate import URITemplate
417
418
# List variables for path segments
419
template = URITemplate('https://api.example.com/{path*}')
420
uri = template.expand(path=['users', 'profile', 'settings'])
421
print(uri) # https://api.example.com/users/profile/settings
422
423
# Dictionary variables for query parameters
424
template = URITemplate('https://api.example.com/search{?params*}')
425
uri = template.expand(params={'q': 'python', 'sort': 'stars', 'order': 'desc'})
426
print(uri) # https://api.example.com/search?q=python&sort=stars&order=desc
427
428
# Mixed variable types
429
template = URITemplate('https://api.example.com/{+base}/search{?q,filters*}')
430
uri = template.expand(
431
base='https://search.example.com/api/v1',
432
q='machine learning',
433
filters={'category': 'tech', 'year': '2023'}
434
)
435
```
436
437
### Template Reuse and Performance
438
439
```python
440
from uritemplate import URITemplate
441
442
class GitHubAPI:
443
# Class-level template for reuse (parsed once)
444
repo_template = URITemplate('https://api.github.com/repos/{owner}/{repo}')
445
issues_template = URITemplate('https://api.github.com/repos/{owner}/{repo}/issues{/number}{?state,labels}')
446
447
def __init__(self, owner: str, repo: str):
448
self.owner = owner
449
self.repo = repo
450
451
def get_repo_url(self) -> str:
452
return self.repo_template.expand(owner=self.owner, repo=self.repo)
453
454
def get_issues_url(self, number: int = None, state: str = None) -> str:
455
params = {'owner': self.owner, 'repo': self.repo}
456
if number:
457
params['number'] = number
458
if state:
459
params['state'] = state
460
return self.issues_template.expand(params)
461
462
# Usage
463
api = GitHubAPI('octocat', 'Hello-World')
464
print(api.get_repo_url()) # https://api.github.com/repos/octocat/Hello-World
465
print(api.get_issues_url(state='open')) # https://api.github.com/repos/octocat/Hello-World/issues?state=open
466
```
467
468
## Error Handling
469
470
The uritemplate package handles various edge cases gracefully:
471
472
- **Missing variables**: Unexpanded variables remain in template form during partial expansion
473
- **Invalid variable types**: Complex objects are converted to strings when possible
474
- **Empty values**: Empty strings and None values are handled according to RFC 6570 specification
475
- **Special characters**: Proper URL encoding is applied based on the expansion operator used
476
477
```python
478
from uritemplate import URITemplate, expand
479
480
# Missing variables in partial expansion
481
template = URITemplate('https://api.example.com/{service}/{version}{/endpoint}')
482
partial_result = template.partial(service='users')
483
print(str(partial_result)) # https://api.example.com/users/{version}{/endpoint}
484
485
# Empty and None values
486
uri = expand('https://api.example.com{/path}{?query}', path='', query=None)
487
print(uri) # https://api.example.com/
488
489
# Special character encoding
490
uri = expand('https://api.example.com/search{?q}', q='hello world & special chars!')
491
print(uri) # https://api.example.com/search?q=hello%20world%20%26%20special%20chars%21
492
```