Adding alignment and field widths capabilities. Since objects like datetime.datetime have their custom formatting syntax, they don't support alignment and field width capabilities.
For example, formatting a datetime with
{time:>20%H:%M:%S} won't cause the time to be right aligned in a field of length 20. Instead, the string >20 will be printed verbatim.
Using the __format__ method, it's easy to write a general wrapper class Align which gives other objects the ability to handle alignment and field width in formatting operations.
#!/usr/bin/env python
from functools import update_wrapper
import re
_align_re =re .compile (r"^([<^>]?[0-9]*)(?:[.][0-9]*)?")
def with_align (format_method ,maxwidth =True ):
"""Decorator for custom __format__ methods.
Gives a __format__ method the ability to handle
alignment character and field min width and max width
with the syntax [align][minwidth].[maxwidth]
similar to the standard formatting syntax
"""
def wrapper (self ,format_spec ):
match =_align_re .match (format_spec )
index =match .end (0 if maxwidth else 1 )
match ,format_spec =format_spec [:index ],format_spec [index :]
data_str =format_method (self ,format_spec )
if match :
data_str =("{0:"+match +"}").format (data_str )
return data_str
update_wrapper (wrapper ,format_method )
return wrapper
class Align (object ):
"""Wrapper class to allow format alignment.
Wrapping an object in Align adds alignment capabilities
to the object in formatting operations.
Example: "Time is {0:>10%H%M%S}".format(Align(datetime.now()))
"""
def __init__ (self ,wrapped_obj ):
self .wrapped_obj =wrapped_obj
@with_align
def __format__ (self ,format_spec ):
return ("{0:"+format_spec +"}").format (self .wrapped_obj )
def dtime ():
"Returns a datetime object for testing purposes"
from sys import executable as python
from datetime import datetime
from os .path import getmtime
return datetime .fromtimestamp (getmtime (python ))
def atest ():
"Test of formatting a datetime object with alignment"
print ("Test without Align:")
# The alignment and field width can't be used for datetime
print ("Time was: {time:>20%H:%M:%S}".format (time =dtime ()))
# Wrapping the datetime object in an Align object allows
# to handle the alignment specification
print ("Test with Align:")
print ("Time was: {time:>20%H:%M:%S}".format (time =Align (dtime ())))
atest ()
""" my output ---->
Test without Align:
Time was: >2018:38:31
Test with Align:
Time was: 18:38:31 # correctly aligned
"""
The Align wrapper can be used for other kind of objects, and the decorator can be used to give the desired properties to user defined classes without the need to wrap the object in an Align wrapper.