m2m模型翻译
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
5.9 KiB

6 months ago
  1. # Human friendly input/output in Python.
  2. #
  3. # Author: Peter Odding <peter@peterodding.com>
  4. # Last Change: April 19, 2020
  5. # URL: https://humanfriendly.readthedocs.io
  6. """
  7. Simple case insensitive dictionaries.
  8. The :class:`CaseInsensitiveDict` class is a dictionary whose string keys
  9. are case insensitive. It works by automatically coercing string keys to
  10. :class:`CaseInsensitiveKey` objects. Keys that are not strings are
  11. supported as well, just without case insensitivity.
  12. At its core this module works by normalizing strings to lowercase before
  13. comparing or hashing them. It doesn't support proper case folding nor
  14. does it support Unicode normalization, hence the word "simple".
  15. """
  16. # Standard library modules.
  17. import collections
  18. try:
  19. # Python >= 3.3.
  20. from collections.abc import Iterable, Mapping
  21. except ImportError:
  22. # Python 2.7.
  23. from collections import Iterable, Mapping
  24. # Modules included in our package.
  25. from humanfriendly.compat import basestring, unicode
  26. # Public identifiers that require documentation.
  27. __all__ = ("CaseInsensitiveDict", "CaseInsensitiveKey")
  28. class CaseInsensitiveDict(collections.OrderedDict):
  29. """
  30. Simple case insensitive dictionary implementation (that remembers insertion order).
  31. This class works by overriding methods that deal with dictionary keys to
  32. coerce string keys to :class:`CaseInsensitiveKey` objects before calling
  33. down to the regular dictionary handling methods. While intended to be
  34. complete this class has not been extensively tested yet.
  35. """
  36. def __init__(self, other=None, **kw):
  37. """Initialize a :class:`CaseInsensitiveDict` object."""
  38. # Initialize our superclass.
  39. super(CaseInsensitiveDict, self).__init__()
  40. # Handle the initializer arguments.
  41. self.update(other, **kw)
  42. def coerce_key(self, key):
  43. """
  44. Coerce string keys to :class:`CaseInsensitiveKey` objects.
  45. :param key: The value to coerce (any type).
  46. :returns: If `key` is a string then a :class:`CaseInsensitiveKey`
  47. object is returned, otherwise the value of `key` is
  48. returned unmodified.
  49. """
  50. if isinstance(key, basestring):
  51. key = CaseInsensitiveKey(key)
  52. return key
  53. @classmethod
  54. def fromkeys(cls, iterable, value=None):
  55. """Create a case insensitive dictionary with keys from `iterable` and values set to `value`."""
  56. return cls((k, value) for k in iterable)
  57. def get(self, key, default=None):
  58. """Get the value of an existing item."""
  59. return super(CaseInsensitiveDict, self).get(self.coerce_key(key), default)
  60. def pop(self, key, default=None):
  61. """Remove an item from a case insensitive dictionary."""
  62. return super(CaseInsensitiveDict, self).pop(self.coerce_key(key), default)
  63. def setdefault(self, key, default=None):
  64. """Get the value of an existing item or add a new item."""
  65. return super(CaseInsensitiveDict, self).setdefault(self.coerce_key(key), default)
  66. def update(self, other=None, **kw):
  67. """Update a case insensitive dictionary with new items."""
  68. if isinstance(other, Mapping):
  69. # Copy the items from the given mapping.
  70. for key, value in other.items():
  71. self[key] = value
  72. elif isinstance(other, Iterable):
  73. # Copy the items from the given iterable.
  74. for key, value in other:
  75. self[key] = value
  76. elif other is not None:
  77. # Complain about unsupported values.
  78. msg = "'%s' object is not iterable"
  79. type_name = type(value).__name__
  80. raise TypeError(msg % type_name)
  81. # Copy the keyword arguments (if any).
  82. for key, value in kw.items():
  83. self[key] = value
  84. def __contains__(self, key):
  85. """Check if a case insensitive dictionary contains the given key."""
  86. return super(CaseInsensitiveDict, self).__contains__(self.coerce_key(key))
  87. def __delitem__(self, key):
  88. """Delete an item in a case insensitive dictionary."""
  89. return super(CaseInsensitiveDict, self).__delitem__(self.coerce_key(key))
  90. def __getitem__(self, key):
  91. """Get the value of an item in a case insensitive dictionary."""
  92. return super(CaseInsensitiveDict, self).__getitem__(self.coerce_key(key))
  93. def __setitem__(self, key, value):
  94. """Set the value of an item in a case insensitive dictionary."""
  95. return super(CaseInsensitiveDict, self).__setitem__(self.coerce_key(key), value)
  96. class CaseInsensitiveKey(unicode):
  97. """
  98. Simple case insensitive dictionary key implementation.
  99. The :class:`CaseInsensitiveKey` class provides an intentionally simple
  100. implementation of case insensitive strings to be used as dictionary keys.
  101. If you need features like Unicode normalization or proper case folding
  102. please consider using a more advanced implementation like the :pypi:`istr`
  103. package instead.
  104. """
  105. def __new__(cls, value):
  106. """Create a :class:`CaseInsensitiveKey` object."""
  107. # Delegate string object creation to our superclass.
  108. obj = unicode.__new__(cls, value)
  109. # Store the lowercased string and its hash value.
  110. normalized = obj.lower()
  111. obj._normalized = normalized
  112. obj._hash_value = hash(normalized)
  113. return obj
  114. def __hash__(self):
  115. """Get the hash value of the lowercased string."""
  116. return self._hash_value
  117. def __eq__(self, other):
  118. """Compare two strings as lowercase."""
  119. if isinstance(other, CaseInsensitiveKey):
  120. # Fast path (and the most common case): Comparison with same type.
  121. return self._normalized == other._normalized
  122. elif isinstance(other, unicode):
  123. # Slow path: Comparison with strings that need lowercasing.
  124. return self._normalized == other.lower()
  125. else:
  126. return NotImplemented