Skip to content

Commit 8b2d4c0

Browse files
committed
Improved error handling when building URLs. More documentation.
1 parent 0db3e72 commit 8b2d4c0

2 files changed

Lines changed: 44 additions & 24 deletions

File tree

flask_hashids.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,30 @@
44
from werkzeug.routing import BaseConverter, ValidationError
55

66

7-
DecodedHashid = Union[Tuple[int, ...], Tuple[()]]
8-
SimplifiedDecodedHashid = Union[int, Tuple[int, ...], Tuple[()]]
9-
10-
117
class HashidMixin:
12-
'''
13-
The HashidMixin class adds a hashid property to a class instance. This
14-
property will compute a hashid based on the attribute specified by a
15-
special class variable called __id_attribute__ (defaults to "id").
8+
''' Hashid Mixin class.
9+
10+
The HashidMixin class adds a hashid property to a class. This property
11+
will compute a hashid based on the attribute specified by the class
12+
variable `__id_attribute__: str = 'id'`.
1613
1714
The class can be used with SQLAlchemy models.
1815
This won't add a column to the table, the hashid is implemented as a
1916
property and computed on runtime.
2017
2118
NOTE: The extended class must have an attribute named after the value of
22-
__id_attribute__ (str) and must be of type int!
19+
`__id_attribute__: str` and must be of type `int`!
2320
'''
2421

2522
__id_attribute__: str = 'id'
2623

2724
@property
2825
def hashid(self) -> str:
26+
''' Hashid property.
27+
28+
Runtime computed hashid based on the attribute specified by the class
29+
variable `__id_attribute__: str`.
30+
'''
2931
id_attribute: int = getattr(self, self.__class__.__id_attribute__)
3032
hashids_extension: Hashids = current_app.extensions['hashids']
3133
return hashids_extension.encode(id_attribute)
@@ -34,8 +36,9 @@ def hashid(self) -> str:
3436
class HashidConverter(BaseConverter):
3537
''' Hashid Converter.
3638
37-
Converts and decodes a hashid from routes.
39+
Converts and decodes a hashid from routes.\n
3840
Examples:
41+
```python
3942
@app.route('/resources/<hashid:resource_id')
4043
def get_resource(resource_id: int):
4144
print(isinstance(resource_id, int)) # True
@@ -44,43 +47,60 @@ def get_resource(resource_id: int):
4447
def get_resources(resource_ids: Tuple[int, ...]):
4548
print(isinstance(resource_ids, tuple)) # True
4649
print(all(isinstance(i, int) for i in resource_ids)) # True
50+
```
4751
48-
Converts and encodes values when generating urls.
52+
Converts and encodes values when generating urls.\n
4953
Examples:
54+
```python
5055
url_for('get_resource', resource_id=123) # /resources/Mj3
5156
5257
url_for('get_resource', resource_id=(123, 456)) # /resources/Nk4
58+
```
5359
'''
5460

55-
def to_python(self, hashid: str) -> DecodedHashid:
61+
def to_python(self, hashid: str) -> Union[int, Tuple[int, ...]]:
62+
''' Decodes matched hashid in a URL to an int or tuple of ints '''
5663
hashids_extension: Hashids = current_app.extensions['hashids']
57-
decoded_hashid: SimplifiedDecodedHashid = hashids_extension.decode(hashid)
64+
decoded_hashid: Union[int, Tuple[int, ...], Tuple[()]] = hashids_extension.decode(hashid)
5865
if isinstance(decoded_hashid, tuple) and len(decoded_hashid) == 0:
5966
raise ValidationError()
6067
return decoded_hashid
6168

62-
def to_url(self, value: DecodedHashid) -> str:
69+
def to_url(self, value: Union[int, Tuple[int, ...]]) -> str:
70+
''' Encodes an int or tuple of ints to a hashid when building a URL '''
6371
hashids_extension: Hashids = current_app.extensions['hashids']
64-
normalized_value: Tuple[int, ...] = (value,) if isinstance(value, int) else value
65-
return hashids_extension.encode(*normalized_value)
72+
if isinstance(value, int):
73+
return hashids_extension.encode(value)
74+
elif isinstance(value, tuple):
75+
if len(value) == 0:
76+
raise ValueError('Tuple must not be empty')
77+
if not all(isinstance(v, int) for v in value):
78+
raise TypeError('Tuple must only contain integers')
79+
return hashids_extension.encode(*value)
80+
else:
81+
raise TypeError('Value must be int or tuple of ints')
6682

6783

6884
class Hashids:
69-
''' Wrapper class to easily integrate hashids in Flask '''
85+
''' Wrapper class to easily integrate Hashids in Flask '''
7086

7187
def __init__(self, app: Optional[Flask] = None) -> None:
72-
self.app: Union[Flask, None] = app
88+
self.app: Optional[Flask] = app
7389
if app is not None:
7490
self.init_app(app)
7591

7692
def init_app(self, app: Flask) -> None:
77-
''' Setup Hashids, includes the integration of the HashidConverter. '''
93+
''' Initialize Hashids extension
94+
95+
This method will configure the Hashids extension according to the
96+
`config` attribute of the passed `app` object. It will also register
97+
the HashidConverter.
98+
'''
7899
hashids_config: Dict = {}
79100
if 'HASHIDS_ALPHABET' in app.config:
80101
hashids_config['alphabet']: str = app.config['HASHIDS_ALPHABET']
81102
if 'HASHIDS_MIN_LENGTH' in app.config:
82-
hashids_config['min_length']: int = \
83-
int(app.config['HASHIDS_MIN_LENGTH'])
103+
hashids_config['min_length']: int = int(app.config['HASHIDS_MIN_LENGTH'])
84104
if 'HASHIDS_SALT' in app.config:
85105
hashids_config['salt']: str = app.config['HASHIDS_SALT']
86106
self._hashids: _Hashids = _Hashids(**hashids_config)
@@ -89,15 +109,15 @@ def init_app(self, app: Flask) -> None:
89109
app.extensions['hashids']: Hashids = self
90110
app.url_map.converters['hashid']: HashidConverter = HashidConverter
91111

92-
def decode(self, hashid: str) -> SimplifiedDecodedHashid:
112+
def decode(self, hashid: str) -> Union[int, Tuple[int, ...], Tuple[()]]:
93113
''' Decode the passed `hashid`.
94114
95115
Possible return values:
96116
- `int` if the hashid contains only one value.
97117
- `Tuple[int, ...]` if the hashid contains multiple values.
98118
- `Tuple[()]` if the hashid is invalid.
99119
'''
100-
decoded_hashid: DecodedHashid = self._hashids.decode(hashid)
120+
decoded_hashid: Union[Tuple[int, ...], Tuple[()]] = self._hashids.decode(hashid)
101121
if len(decoded_hashid) == 1:
102122
return decoded_hashid[0]
103123
return decoded_hashid

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
setup(
99
name='Flask-Hashids',
10-
version='1.0.1',
10+
version='1.0.2',
1111
url='https://github.com/Pevtrick/Flask-Hashids',
1212
author='Patrick Jentsch',
1313
author_email='patrickjentsch@gmx.net',

0 commit comments

Comments
 (0)