Skip to content

Commit

Permalink
Fixed bug in string_format parsing when no justification char specifi…
Browse files Browse the repository at this point in the history
…ed; Tried to clean up confusing doc string
  • Loading branch information
James Smith authored and James Smith committed Nov 29, 2024
1 parent ae05b78 commit b3c7b8c
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 16 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,19 @@ print(s)

### Format String

A format string may be used to format an `AnsiString` or `AnsiStr` before printing. The format specification string must be in the format `"[string_format[:ansi_format]]"` where `string_format` is an extension of the standard string format specifier and `ansi_format` contains 0 or more ANSI directives separated by semicolons (;). The ANSI directives may be any of the same string values that can be passed to the `AnsiString` constructor. If no `string_format` is desired, then it can be set to an empty string.
A format string may be used to format an `AnsiString` or `AnsiStr` before printing. The format specification string must be in the format `"[string_format[:ansi_format]]"` where:
- `string_format` is an extension of the standard string format specifier: `.?[+-]?[<>^]?[0-9]*`
- The first character is optional and is the fill character used (default: space)
- An optional + or - char may be specified after the first fill character to enable or disable formatting of the fill character (enabled by default)
- A `^`, `<`, or `>` character specified center, left, or right justification (left by default)
- An integer specifies the total width (0 by default)
- `ansi_format` contains 0 or more ansi directives separated by semicolons (;). The ANSI directives may be any of the same string values that can be passed to the `AnsiString` constructor. If no `string_format` is desired, then it can be set to an empty string.

Examples:

```py
ansi_str = AnsiString("This is an ANSI string")

# Right justify with width of 100, formatted with underline and colored red.
# By default, all fill characters will take on the first character's formatting.
print("{:>100:underline;red}".format(ansi_str))
Expand Down
32 changes: 17 additions & 15 deletions src/ansi_string/ansi_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ def _apply_string_format(self, string_format:str, settings:Union[AnsiFormat, Ans
(start, end) values where accompanying formats should be applied
'''
extend_formatting = True
match = re.search(r'^(.?)([+-]?)<([0-9]*)$', string_format)
match = re.search(r'^(.?)([+-]?)<([0-9]*)$', string_format) or re.search(r'^()()([0-9]*)$', string_format)
if match:
# Left justify
num = match.group(3)
Expand Down Expand Up @@ -689,11 +689,11 @@ def _apply_string_format(self, string_format:str, settings:Union[AnsiFormat, Ans
self.apply_formatting(settings)
return

match = re.search(r'^[<>\^]?[+-]?[0-9]*$', string_format)
match = re.search(r'^[<>\^]?[+-][0-9]*$', string_format)
if match:
raise ValueError('Sign not allowed in string format specifier')

match = re.search(r'^[<>\^]?[ ]?[0-9]*$', string_format)
match = re.search(r'^[<>\^]?[ ][0-9]*$', string_format)
if match:
raise ValueError('Space not allowed in string format specifier')

Expand Down Expand Up @@ -731,12 +731,13 @@ def to_str(self, format_spec:str=None, optimize:bool=True, reset_start:bool=Fals
'''
Returns an ANSI format string with both internal and given formatting spec set.
Parameters:
format_spec - must be in the format "[string_format[:ansi_format]]" where string_format is an extension of
the standard string format specifier and ansi_format contains 0 or more ansi directives
separated by semicolons (;)
ex: ">10:bold;red" to make output right justify with width of 10, bold and red formatting
No formatting should be applied as part of the justification, add a '-' after the fillchar.
ex: " ->10:bold;red" to not not apply formatting to justification characters
format_spec - must be in the format "[string_format[:ansi_format]]" where:
- string_format is an extension of the standard string format specifier: .?[+-]?[<>^]?[0-9]*
An optional + or - char may be specified after the first fill character to enable or disable
formatting of the fill character (enabled by default)
- ansi_format contains 0 or more ansi directives separated by semicolons (;)
ex: ">10:underline;red" for right justify, width of 10, underline and red formatting
ex: " ->10:underline;red" to do the same but don't extend underline across fill characters
optimize - optimization selects the shortest setting string based on the situation.
If this is False, then the RESET directive (0) will always used when settings change mid-string.
reset_start - when True, the output string will always start with the RESET directive (0)
Expand Down Expand Up @@ -851,12 +852,13 @@ def __format__(self, __format_spec:str) -> str:
'''
Returns an ANSI format string with both internal and given formatting spec set.
Parameters:
__format_spec - must be in the format "[string_format[:ansi_format]]" where string_format is an extension of
the standard string format specifier and ansi_format contains 0 or more ansi directives
separated by semicolons (;)
ex: ">10:bold;red" to make output right justify with width of 10, bold and red formatting
No formatting should be applied as part of the justification, add a '-' after the fillchar.
ex: " ->10:bold;red" to not not apply formatting to justification characters
__format_spec - must be in the format "[string_format[:ansi_format]]" where:
- string_format is an extension of the standard string format specifier: .?[+-]?[<>^]?[0-9]*
An optional + or - char may be specified after the first fill character to enable or disable
formatting of the fill character (enabled by default)
- ansi_format contains 0 or more ansi directives separated by semicolons (;)
ex: ">10:underline;red" for right justify, width of 10, underline and red formatting
ex: " ->10:underline;red" to do the same but don't extend underline across fill characters
'''
return self.to_str(__format_spec)

Expand Down
7 changes: 7 additions & 0 deletions tests/test_ansi_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,13 @@ def test_format_left_justify_and_strings(self):
'\x1b[1;31mThis string will be formatted bold and red++++++++++++++++++++++++++++++++++++++++++++++++\x1b[m'
)

def test_format_default_left_justify_and_strings(self):
s = AnsiString('This string will be formatted bold and red', 'bold')
self.assertEqual(
'{:90:fg_red}'.format(s),
'\x1b[1;31mThis string will be formatted bold and red \x1b[m'
)

def test_format_left_justify_no_extend_and_strings(self):
s = AnsiString('This string will be formatted bold and red', 'bold')
self.assertEqual(
Expand Down

0 comments on commit b3c7b8c

Please sign in to comment.