kvform.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. __all__ = ['seqToKV', 'kvToSeq', 'dictToKV', 'kvToDict']
  2. from openid import oidutil
  3. import types
  4. class KVFormError(ValueError):
  5. pass
  6. def seqToKV(seq, strict=False):
  7. """Represent a sequence of pairs of strings as newline-terminated
  8. key:value pairs. The pairs are generated in the order given.
  9. @param seq: The pairs
  10. @type seq: [(str, (unicode|str))]
  11. @return: A string representation of the sequence
  12. @rtype: str
  13. """
  14. def err(msg):
  15. formatted = 'seqToKV warning: %s: %r' % (msg, seq)
  16. if strict:
  17. raise KVFormError(formatted)
  18. else:
  19. oidutil.log(formatted)
  20. lines = []
  21. for k, v in seq:
  22. if isinstance(k, types.StringType):
  23. k = k.decode('UTF8')
  24. elif not isinstance(k, types.UnicodeType):
  25. err('Converting key to string: %r' % k)
  26. k = str(k)
  27. if '\n' in k:
  28. raise KVFormError(
  29. 'Invalid input for seqToKV: key contains newline: %r' % (k,))
  30. if ':' in k:
  31. raise KVFormError(
  32. 'Invalid input for seqToKV: key contains colon: %r' % (k,))
  33. if k.strip() != k:
  34. err('Key has whitespace at beginning or end: %r' % (k,))
  35. if isinstance(v, types.StringType):
  36. v = v.decode('UTF8')
  37. elif not isinstance(v, types.UnicodeType):
  38. err('Converting value to string: %r' % (v,))
  39. v = str(v)
  40. if '\n' in v:
  41. raise KVFormError(
  42. 'Invalid input for seqToKV: value contains newline: %r' % (v,))
  43. if v.strip() != v:
  44. err('Value has whitespace at beginning or end: %r' % (v,))
  45. lines.append(k + ':' + v + '\n')
  46. return ''.join(lines).encode('UTF8')
  47. def kvToSeq(data, strict=False):
  48. """
  49. After one parse, seqToKV and kvToSeq are inverses, with no warnings::
  50. seq = kvToSeq(s)
  51. seqToKV(kvToSeq(seq)) == seq
  52. """
  53. def err(msg):
  54. formatted = 'kvToSeq warning: %s: %r' % (msg, data)
  55. if strict:
  56. raise KVFormError(formatted)
  57. else:
  58. oidutil.log(formatted)
  59. lines = data.split('\n')
  60. if lines[-1]:
  61. err('Does not end in a newline')
  62. else:
  63. del lines[-1]
  64. pairs = []
  65. line_num = 0
  66. for line in lines:
  67. line_num += 1
  68. # Ignore blank lines
  69. if not line.strip():
  70. continue
  71. pair = line.split(':', 1)
  72. if len(pair) == 2:
  73. k, v = pair
  74. k_s = k.strip()
  75. if k_s != k:
  76. fmt = ('In line %d, ignoring leading or trailing '
  77. 'whitespace in key %r')
  78. err(fmt % (line_num, k))
  79. if not k_s:
  80. err('In line %d, got empty key' % (line_num,))
  81. v_s = v.strip()
  82. if v_s != v:
  83. fmt = ('In line %d, ignoring leading or trailing '
  84. 'whitespace in value %r')
  85. err(fmt % (line_num, v))
  86. pairs.append((k_s.decode('UTF8'), v_s.decode('UTF8')))
  87. else:
  88. err('Line %d does not contain a colon' % line_num)
  89. return pairs
  90. def dictToKV(d):
  91. seq = d.items()
  92. seq.sort()
  93. return seqToKV(seq)
  94. def kvToDict(s):
  95. return dict(kvToSeq(s))