2016-06-11 12:49:08 +02:00
|
|
|
"""
|
|
|
|
String Utilities:
|
|
|
|
|
|
|
|
This module helps in converting strings from one type to another.
|
|
|
|
|
|
|
|
Currently we have strings of 3 semantic types:
|
|
|
|
|
|
|
|
1. text strings: These strings are used to represent all textual data,
|
|
|
|
like people's names, stream names, content of messages, etc.
|
|
|
|
These strings can contain non-ASCII characters, so its type should be
|
2016-12-23 17:12:07 +01:00
|
|
|
typing.Text (which is `str` in python 3 and `unicode` in python 2).
|
2016-06-11 12:49:08 +02:00
|
|
|
|
|
|
|
2. binary strings: These strings are used to represent binary data.
|
2017-09-27 10:06:17 +02:00
|
|
|
This should be of type `bytes`
|
2016-06-11 12:49:08 +02:00
|
|
|
|
|
|
|
3. native strings: These strings are for internal use only. Strings of
|
|
|
|
this type are not meant to be stored in database, displayed to end
|
|
|
|
users, etc. Things like exception names, parameter names, attribute
|
|
|
|
names, etc should be native strings. These strings should only
|
|
|
|
contain ASCII characters and they should have type `str`.
|
|
|
|
|
|
|
|
There are 3 utility functions provided for converting strings from one type
|
|
|
|
to another - force_text, force_bytes, force_str
|
|
|
|
|
|
|
|
Interconversion between text strings and binary strings can be done by
|
|
|
|
using encode and decode appropriately or by using the utility functions
|
|
|
|
force_text and force_bytes.
|
|
|
|
|
|
|
|
It is recommended to use the utility functions for other string conversions.
|
|
|
|
"""
|
|
|
|
|
2017-03-03 19:01:52 +01:00
|
|
|
from typing import Any, Dict, Mapping, Union, TypeVar, Text
|
2016-06-09 08:37:53 +02:00
|
|
|
|
2016-12-21 13:17:53 +01:00
|
|
|
NonBinaryStr = TypeVar('NonBinaryStr', str, Text)
|
2016-06-09 08:37:53 +02:00
|
|
|
# This is used to represent text or native strings
|
|
|
|
|
2016-07-05 03:26:40 +02:00
|
|
|
def force_text(s, encoding='utf-8'):
|
2017-09-27 10:06:17 +02:00
|
|
|
# type: (Union[Text, bytes], str) -> Text
|
2016-06-11 12:49:08 +02:00
|
|
|
"""converts a string to a text string"""
|
2016-12-23 17:12:07 +01:00
|
|
|
if isinstance(s, Text):
|
2016-06-09 08:37:53 +02:00
|
|
|
return s
|
2017-09-27 10:06:17 +02:00
|
|
|
elif isinstance(s, bytes):
|
2016-07-05 03:26:40 +02:00
|
|
|
return s.decode(encoding)
|
2016-06-09 08:37:53 +02:00
|
|
|
else:
|
2016-07-21 19:04:24 +02:00
|
|
|
raise TypeError("force_text expects a string type")
|
2016-06-09 08:37:53 +02:00
|
|
|
|
2016-07-05 03:26:40 +02:00
|
|
|
def force_bytes(s, encoding='utf-8'):
|
2017-09-27 10:06:17 +02:00
|
|
|
# type: (Union[Text, bytes], str) -> bytes
|
2016-06-11 12:49:08 +02:00
|
|
|
"""converts a string to binary string"""
|
2017-09-27 10:06:17 +02:00
|
|
|
if isinstance(s, bytes):
|
2016-06-09 08:37:53 +02:00
|
|
|
return s
|
2016-12-23 17:12:07 +01:00
|
|
|
elif isinstance(s, Text):
|
2016-07-05 03:26:40 +02:00
|
|
|
return s.encode(encoding)
|
2016-06-09 08:37:53 +02:00
|
|
|
else:
|
2016-07-21 19:04:24 +02:00
|
|
|
raise TypeError("force_bytes expects a string type")
|
2016-06-09 08:37:53 +02:00
|
|
|
|
2016-07-05 03:26:40 +02:00
|
|
|
def force_str(s, encoding='utf-8'):
|
2017-09-27 10:06:17 +02:00
|
|
|
# type: (Union[Text, bytes], str) -> str
|
2016-06-11 12:49:08 +02:00
|
|
|
"""converts a string to a native string"""
|
2016-06-09 08:37:53 +02:00
|
|
|
if isinstance(s, str):
|
|
|
|
return s
|
2016-12-23 17:12:07 +01:00
|
|
|
elif isinstance(s, Text):
|
2016-07-05 03:26:40 +02:00
|
|
|
return s.encode(encoding)
|
2017-09-27 10:06:17 +02:00
|
|
|
elif isinstance(s, bytes):
|
2016-07-05 03:26:40 +02:00
|
|
|
return s.decode(encoding)
|
2016-06-09 08:37:53 +02:00
|
|
|
else:
|
2016-07-21 19:04:24 +02:00
|
|
|
raise TypeError("force_str expects a string type")
|
2016-06-09 08:37:53 +02:00
|
|
|
|
2016-07-05 03:26:40 +02:00
|
|
|
def dict_with_str_keys(dct, encoding='utf-8'):
|
|
|
|
# type: (Mapping[NonBinaryStr, Any], str) -> Dict[str, Any]
|
2016-06-09 08:37:53 +02:00
|
|
|
"""applies force_str on the keys of a dict (non-recursively)"""
|
2017-09-27 10:06:17 +02:00
|
|
|
return {force_str(key, encoding): value for key, value in dct.items()}
|
2016-06-12 09:36:05 +02:00
|
|
|
|
|
|
|
class ModelReprMixin(object):
|
|
|
|
"""
|
|
|
|
This mixin provides a python 2 and 3 compatible way of handling string representation of a model.
|
|
|
|
When declaring a model, inherit this mixin before django.db.models.Model.
|
2016-12-23 17:12:07 +01:00
|
|
|
Define __unicode__ on your model which returns a typing.Text object.
|
2016-06-12 09:36:05 +02:00
|
|
|
This mixin will automatically define __str__ and __repr__.
|
|
|
|
"""
|
2016-11-29 07:22:02 +01:00
|
|
|
|
2016-06-12 09:36:05 +02:00
|
|
|
def __unicode__(self):
|
2016-11-23 04:53:54 +01:00
|
|
|
# type: () -> Text
|
2016-08-02 00:57:06 +02:00
|
|
|
# Originally raised an exception, but Django (e.g. the ./manage.py shell)
|
|
|
|
# was catching the exception and not displaying any sort of error
|
|
|
|
return u"Implement __unicode__ in your subclass of ModelReprMixin!"
|
2016-06-12 09:36:05 +02:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
# type: () -> str
|
|
|
|
return force_str(self.__unicode__())
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
# type: () -> str
|
|
|
|
return force_str(self.__unicode__())
|