Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/mog_commons/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.18'
__version__ = '0.1.19'
40 changes: 38 additions & 2 deletions src/mog_commons/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import six

from mog_commons.collection import distinct
from mog_commons.types import *

__all__ = [
'is_unicode',
Expand All @@ -29,6 +30,7 @@ def is_strlike(s):
return isinstance(s, (six.string_types, bytes))


@types(s=String)
def unicode_width(s):
if is_unicode(s):
return sum(__unicode_width_mapping[east_asian_width(c)] for c in s)
Expand All @@ -37,6 +39,7 @@ def unicode_width(s):
return len(s)


@types(encoding=Option(String), errors=String)
def to_unicode(s, encoding=None, errors='strict'):
"""
Make unicode string from any value
Expand All @@ -58,6 +61,7 @@ def to_unicode(s, encoding=None, errors='strict'):
return str(s)


@types(encoding=Option(String), errors=String)
def to_str(s, encoding=None, errors='strict'):
"""
Make str from any value
Expand All @@ -77,6 +81,7 @@ def to_str(s, encoding=None, errors='strict'):
return str(s)


@types(encoding=Option(String), errors=String)
def to_bytes(s, encoding=None, errors='strict'):
"""Convert string to bytes."""
encoding = encoding or 'utf-8'
Expand All @@ -92,11 +97,40 @@ def to_bytes(s, encoding=None, errors='strict'):
return str(s).encode(encoding, errors)


def edge_just(left, right, width, fillchar=' '):
padding = fillchar * max(1, width - unicode_width(left + right))
@types(left=String, right=String, width=int, fillchar=String, min_padding_length=int)
def edge_just(left, right, width, fillchar=' ', min_padding_length=1):
"""
:param left:
:param right:
:param width:
:param fillchar:
:param min_padding_length: minimum padding length
:return:
"""
assert unicode_width(fillchar) == 1, 'fillchar must be single-width char'
padding = fillchar * max(min_padding_length, width - unicode_width(left + right))
return left + padding + right


@types(s=String, width=int, fillchar=String)
def unicode_ljust(s, width, fillchar=' '):
"""
Return Unicode string left-justified in a print-length width.
Padding is done using the specified fill character (default is a space).
"""
return edge_just(s, '', width, fillchar, 0)


@types(s=String, width=int, fillchar=String)
def unicode_rjust(s, width, fillchar=' '):
"""
Return Unicode string right-justified in a print-length width.
Padding is done using the specified fill character (default is a space).
"""
return edge_just('', s, width, fillchar, 0)


@types(s=String, width=int)
def unicode_left(s, width):
"""Cut unicode string from left to fit a given width."""
i = 0
Expand All @@ -109,6 +143,7 @@ def unicode_left(s, width):
return s[:i]


@types(s=String, width=int)
def unicode_right(s, width):
"""Cut unicode string from right to fit a given width."""
i = len(s)
Expand All @@ -121,6 +156,7 @@ def unicode_right(s, width):
return s[i:]


@types(encoding_list=(String, ListOf(String)))
def unicode_decode(data, encoding_list):
"""
Decode string data with one or more encodings, trying sequentially
Expand Down
32 changes: 32 additions & 0 deletions tests/mog_commons/test_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,38 @@ def test_edge_just_unicode(self):
self.assertEqual(string.edge_just('あいu', 'えo', 10), 'あいu えo')
self.assertEqual(string.edge_just('あいう', 'えお', 10), 'あいう えお')

def test_edge_just_error(self):
msg = 'fillchar must be single-width char'
self.assertRaisesMessage(AssertionError, msg, string.edge_just, '', '', 10, '')
self.assertRaisesMessage(AssertionError, msg, string.edge_just, '', '', 10, 'ab')
self.assertRaisesMessage(AssertionError, msg, string.edge_just, '', '', 10, 'あ')

def test_unicode_ljust(self):
self.assertEqual(string.unicode_ljust('', 0), '')
self.assertEqual(string.unicode_ljust('', -1), '')
self.assertEqual(string.unicode_ljust('', 1), ' ')
self.assertEqual(string.unicode_ljust('', 10), ' ')
self.assertEqual(string.unicode_ljust('', 10, '.'), '..........')
self.assertEqual(string.unicode_ljust('abcde', 10), 'abcde ')
self.assertEqual(string.unicode_ljust('abcdefghij', 10), 'abcdefghij')
self.assertEqual(string.unicode_ljust('abcdefghijk', 10), 'abcdefghijk')
self.assertEqual(string.unicode_ljust('あいう', 10), 'あいう ')
self.assertEqual(string.unicode_ljust('あいうe', 10), 'あいうe ')
self.assertEqual(string.unicode_ljust('あいうeお', 10, '*'), 'あいうeお*')

def test_unicode_rjust(self):
self.assertEqual(string.unicode_rjust('', 0), '')
self.assertEqual(string.unicode_rjust('', -1), '')
self.assertEqual(string.unicode_rjust('', 1), ' ')
self.assertEqual(string.unicode_rjust('', 10), ' ')
self.assertEqual(string.unicode_rjust('', 10, '.'), '..........')
self.assertEqual(string.unicode_rjust('abcde', 10), ' abcde')
self.assertEqual(string.unicode_rjust('abcdefghij', 10), 'abcdefghij')
self.assertEqual(string.unicode_rjust('abcdefghijk', 10), 'abcdefghijk')
self.assertEqual(string.unicode_rjust('あいう', 10), ' あいう')
self.assertEqual(string.unicode_rjust('あいうe', 10), ' あいうe')
self.assertEqual(string.unicode_rjust('あいうeお', 10, '*'), '*あいうeお')

def test_unicode_left(self):
self.assertEqual(string.unicode_left('', 3), '')
self.assertEqual(string.unicode_left('abcde', 3), 'abc')
Expand Down