The little things give you away... A collection of various small helper stuff
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

93 lignes
1.9 KiB

  1. #!/usr/bin/env python3
  2. try:
  3. from contextlib import nullcontext
  4. except ImportError:
  5. # Python 3.6 and below
  6. # One-liner because I can.
  7. nullcontext = type("nc", (object,), {"__init__": (lambda self, v: setattr(self, "v", v)), "__enter__": (lambda self: self.v), "__exit__": (lambda self, *args, **kwargs: None)})
  8. import json
  9. import sys
  10. def parse_dict(f):
  11. d = {}
  12. while True:
  13. c = f.read(1)
  14. if c == b'e':
  15. return d
  16. elif b'0' <= c <= b'9':
  17. key = parse_str(f, c)
  18. value = parse_any(f)
  19. d[key] = value
  20. else:
  21. raise ValueError(f'invalid bencode value: {c} instead of expected e or digit')
  22. def parse_list(f):
  23. l = []
  24. while True:
  25. c = f.read(1)
  26. if c == b'e':
  27. return l
  28. l.append(parse_any(f, c))
  29. def _parse_int_bytes(f, stopChar):
  30. # Read until encountering a non-decimal character; if it isn't stopChar, raise a ValueError
  31. v = b''
  32. # Python 3.8+ (walrus)
  33. #while b'0' <= (c := f.read(1)) <= b'9':
  34. # v += c
  35. while True:
  36. c = f.read(1)
  37. if b'0' <= c <= b'9':
  38. v += c
  39. else:
  40. break
  41. if c == stopChar:
  42. return v
  43. raise ValueError(f'invalid bencode value: {c} instead of expected {stopChar}')
  44. def parse_int(f):
  45. return int(_parse_int_bytes(f, b'e'))
  46. def parse_str(f, c):
  47. v = _parse_int_bytes(f, b':')
  48. length = int(c + v)
  49. read = 0
  50. buf = []
  51. while read < length:
  52. d = f.read(length - read)
  53. buf.append(d)
  54. read += len(d)
  55. buf = b''.join(buf)
  56. try:
  57. return buf.decode('utf-8', 'strict')
  58. except UnicodeDecodeError:
  59. return buf.decode('iso-8859-1')
  60. def parse_any(f, c = None):
  61. if c is None:
  62. c = f.read(1)
  63. if c == b'd':
  64. return parse_dict(f)
  65. elif c == b'l':
  66. return parse_list(f)
  67. elif c == b'i':
  68. return parse_int(f)
  69. elif b'0' <= c <= b'9':
  70. return parse_str(f, c)
  71. else:
  72. raise ValueError(f'invalid bencode value: {c} instead of expected d, l, i, or digit')
  73. if sys.argv[1:]:
  74. c = open(sys.argv[1], 'rb')
  75. else:
  76. c = nullcontext(sys.stdin.buffer)
  77. with c as fp:
  78. print(json.dumps(parse_any(fp)))