format.py 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. #!/bin/env python
  2. import re
  3. old_pattern = re.compile(r'%\w')
  4. new_pattern = re.compile(r'\{(\w+(\.\w+|\[\w+\])?)\}')
  5. __formaters = {}
  6. def format(text, *a, **kw):
  7. f = __formaters.get(text)
  8. if f is None:
  9. f = formater(text)
  10. __formaters[text] = f
  11. return f(*a, **kw)
  12. #return formater(text)(*a, **kw)
  13. def formater(text):
  14. """
  15. >>> format('%s %s', 3, 2, 7, a=7, id=8)
  16. '3 2'
  17. >>> format('%(a)d %(id)s', 3, 2, 7, a=7, id=8)
  18. '7 8'
  19. >>> format('{1} {id}', 3, 2, a=7, id=8)
  20. '2 8'
  21. >>> class Obj: id = 3
  22. >>> format('{obj.id} {0.id}', Obj(), obj=Obj())
  23. '3 3'
  24. """
  25. # def arg(k,a,kw):
  26. # if k.isdigit():
  27. # return a[int(k)]
  28. # return kw[k]
  29. def translator(k):
  30. if '.' in k:
  31. name,attr = k.split('.')
  32. if name.isdigit():
  33. k = int(name)
  34. return lambda *a, **kw: getattr(a[k], attr)
  35. return lambda *a, **kw: getattr(kw[name], attr)
  36. # elif '[' in k and k.endswith(']'):
  37. # name,index = k[:k.index('[')],k[k.index('[')+1:-1]
  38. # def _(*a, **kw):
  39. # if index.isdigit():
  40. # return arg(name,a,kw)[int(index)]
  41. # return arg(name,a,kw)[index]
  42. # return _
  43. else:
  44. if k.isdigit():
  45. return lambda *a, **kw: a[int(k)]
  46. return lambda *a, **kw: kw[k]
  47. args = [translator(k) for k,_1 in new_pattern.findall(text)]
  48. if args:
  49. if old_pattern.findall(text):
  50. raise Exception('mixed format is not allowed')
  51. f = new_pattern.sub('%s', text)
  52. def _(*a, **kw):
  53. return f % tuple([k(*a,**kw) for k in args])
  54. return _
  55. elif '%(' in text:
  56. return lambda *a, **kw: text % kw
  57. else:
  58. n = len(old_pattern.findall(text))
  59. return lambda *a, **kw: text % tuple(a[:n])
  60. if __name__ == '__main__':
  61. import doctest
  62. doctest.testmod()