encoding.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. # Copyright 2013 Donald Stufft and individual contributors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import base64
  15. import binascii
  16. from abc import ABCMeta, abstractmethod
  17. from typing import SupportsBytes, Type
  18. # TODO: when the minimum supported version of Python is 3.8, we can import
  19. # Protocol from typing, and replace Encoder with a Protocol instead.
  20. class _Encoder(metaclass=ABCMeta):
  21. @staticmethod
  22. @abstractmethod
  23. def encode(data: bytes) -> bytes:
  24. """Transform raw data to encoded data."""
  25. @staticmethod
  26. @abstractmethod
  27. def decode(data: bytes) -> bytes:
  28. """Transform encoded data back to raw data.
  29. Decoding after encoding should be a no-op, i.e. `decode(encode(x)) == x`.
  30. """
  31. # Functions that use encoders are passed a subclass of _Encoder, not an instance
  32. # (because the methods are all static). Let's gloss over that detail by defining
  33. # an alias for Type[_Encoder].
  34. Encoder = Type[_Encoder]
  35. class RawEncoder(_Encoder):
  36. @staticmethod
  37. def encode(data: bytes) -> bytes:
  38. return data
  39. @staticmethod
  40. def decode(data: bytes) -> bytes:
  41. return data
  42. class HexEncoder(_Encoder):
  43. @staticmethod
  44. def encode(data: bytes) -> bytes:
  45. return binascii.hexlify(data)
  46. @staticmethod
  47. def decode(data: bytes) -> bytes:
  48. return binascii.unhexlify(data)
  49. class Base16Encoder(_Encoder):
  50. @staticmethod
  51. def encode(data: bytes) -> bytes:
  52. return base64.b16encode(data)
  53. @staticmethod
  54. def decode(data: bytes) -> bytes:
  55. return base64.b16decode(data)
  56. class Base32Encoder(_Encoder):
  57. @staticmethod
  58. def encode(data: bytes) -> bytes:
  59. return base64.b32encode(data)
  60. @staticmethod
  61. def decode(data: bytes) -> bytes:
  62. return base64.b32decode(data)
  63. class Base64Encoder(_Encoder):
  64. @staticmethod
  65. def encode(data: bytes) -> bytes:
  66. return base64.b64encode(data)
  67. @staticmethod
  68. def decode(data: bytes) -> bytes:
  69. return base64.b64decode(data)
  70. class URLSafeBase64Encoder(_Encoder):
  71. @staticmethod
  72. def encode(data: bytes) -> bytes:
  73. return base64.urlsafe_b64encode(data)
  74. @staticmethod
  75. def decode(data: bytes) -> bytes:
  76. return base64.urlsafe_b64decode(data)
  77. class Encodable:
  78. def encode(self: SupportsBytes, encoder: Encoder = RawEncoder) -> bytes:
  79. return encoder.encode(bytes(self))