viur-core 3.8.25__tar.gz → 3.9.0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/PKG-INFO +1 -1
  2. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/base.py +24 -17
  3. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/record.py +10 -5
  4. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/relational.py +17 -9
  5. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/string.py +7 -11
  6. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/text.py +0 -19
  7. viur_core-3.9.0.dev2/src/viur/core/cache.py +585 -0
  8. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/config.py +5 -3
  9. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/query.py +23 -8
  10. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/transport.py +3 -3
  11. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/file.py +2 -2
  12. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/script.py +1 -1
  13. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/user.py +2 -2
  14. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/prototypes/list.py +4 -4
  15. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/prototypes/skelmodule.py +1 -1
  16. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/prototypes/tree.py +7 -7
  17. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/vi/__init__.py +91 -62
  18. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/skeleton.py +8 -2
  19. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/utils/__init__.py +13 -1
  20. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/version.py +1 -1
  21. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur_core.egg-info/PKG-INFO +1 -1
  22. viur_core-3.8.25/src/viur/core/cache.py +0 -266
  23. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/LICENSE +0 -0
  24. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/README.md +0 -0
  25. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/pyproject.toml +0 -0
  26. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/setup.cfg +0 -0
  27. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/__init__.py +0 -0
  28. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/__init__.py +0 -0
  29. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/boolean.py +0 -0
  30. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/captcha.py +0 -0
  31. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/color.py +0 -0
  32. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/credential.py +0 -0
  33. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/date.py +0 -0
  34. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/email.py +0 -0
  35. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/file.py +0 -0
  36. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/image.py +0 -0
  37. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/json.py +0 -0
  38. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/key.py +0 -0
  39. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/numeric.py +0 -0
  40. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/password.py +0 -0
  41. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/phone.py +0 -0
  42. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/randomslice.py +0 -0
  43. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/raw.py +0 -0
  44. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/select.py +0 -0
  45. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/selectcountry.py +0 -0
  46. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/sortindex.py +0 -0
  47. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/spam.py +0 -0
  48. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/spatial.py +0 -0
  49. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/treeleaf.py +0 -0
  50. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/treenode.py +0 -0
  51. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/uid.py +0 -0
  52. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/uri.py +0 -0
  53. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/bones/user.py +0 -0
  54. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/current.py +0 -0
  55. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/__init__.py +0 -0
  56. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/cache.py +0 -0
  57. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/config.py +0 -0
  58. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/overrides.py +0 -0
  59. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/types.py +0 -0
  60. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/db/utils.py +0 -0
  61. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/decorators.py +0 -0
  62. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/email.py +0 -0
  63. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/errors.py +0 -0
  64. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/i18n.py +0 -0
  65. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/languages/__init__.py +0 -0
  66. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/languages/de.py +0 -0
  67. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/languages/en.py +0 -0
  68. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/logging.py +0 -0
  69. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/module.py +0 -0
  70. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/__init__.py +0 -0
  71. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/formmailer.py +0 -0
  72. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/history.py +0 -0
  73. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/moduleconf.py +0 -0
  74. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/page.py +0 -0
  75. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/site.py +0 -0
  76. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/modules/translation.py +0 -0
  77. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/pagination.py +0 -0
  78. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/prototypes/__init__.py +0 -0
  79. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/prototypes/instanced_module.py +0 -0
  80. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/prototypes/singleton.py +0 -0
  81. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/ratelimit.py +0 -0
  82. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/__init__.py +0 -0
  83. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/abstract.py +0 -0
  84. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/__init__.py +0 -0
  85. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/default.py +0 -0
  86. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/__init__.py +0 -0
  87. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/date.py +0 -0
  88. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/debug.py +0 -0
  89. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/regex.py +0 -0
  90. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/session.py +0 -0
  91. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/strings.py +0 -0
  92. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/tests.py +0 -0
  93. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/env/viur.py +0 -0
  94. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/html/utils.py +0 -0
  95. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/json/__init__.py +0 -0
  96. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/render/json/default.py +0 -0
  97. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/request.py +0 -0
  98. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/scripts/viur_migrate.py +0 -0
  99. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/secret.py +0 -0
  100. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/securityheaders.py +0 -0
  101. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/securitykey.py +0 -0
  102. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/session.py +0 -0
  103. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/__init__.py +0 -0
  104. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/adapter.py +0 -0
  105. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/instance.py +0 -0
  106. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/meta.py +0 -0
  107. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/relskel.py +0 -0
  108. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/tasks.py +0 -0
  109. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/skeleton/utils.py +0 -0
  110. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/tasks.py +0 -0
  111. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/template/error.html +0 -0
  112. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/template/vi_user_google_login.html +0 -0
  113. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/utils/json.py +0 -0
  114. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/utils/parse.py +0 -0
  115. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur/core/utils/string.py +0 -0
  116. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur_core.egg-info/SOURCES.txt +0 -0
  117. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur_core.egg-info/dependency_links.txt +0 -0
  118. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur_core.egg-info/entry_points.txt +0 -0
  119. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur_core.egg-info/requires.txt +0 -0
  120. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/src/viur_core.egg-info/top_level.txt +0 -0
  121. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/tests/test_config.py +0 -0
  122. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/tests/test_db.py +0 -0
  123. {viur_core-3.8.25 → viur_core-3.9.0.dev2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: viur-core
3
- Version: 3.8.25
3
+ Version: 3.9.0.dev2
4
4
  Summary: The core component of ViUR, a development framework for Google App Engine
5
5
  Author-email: Mausbrand Informationssysteme GmbH <devs@viur.dev>
6
6
  Maintainer-email: Jan Max Meyer <jm@mausbrand.de>
@@ -1293,10 +1293,11 @@ class BaseBone(object):
1293
1293
  the list may contain more than one hashed value.
1294
1294
  """
1295
1295
 
1296
- def hashValue(value: str | int | float | db.Key) -> str:
1296
+ def hash_value(value: str | int | float | db.Key) -> str:
1297
1297
  h = hashlib.sha256()
1298
1298
  h.update(str(value).encode("UTF-8"))
1299
1299
  res = h.hexdigest()
1300
+
1300
1301
  if isinstance(value, int | float):
1301
1302
  return f"I-{res}"
1302
1303
  elif isinstance(value, str):
@@ -1307,27 +1308,32 @@ class BaseBone(object):
1307
1308
  def keyHash(key):
1308
1309
  if key is None:
1309
1310
  return "-"
1310
- return f"{hashValue(key.kind)}-{hashValue(key.id_or_name)}-<{keyHash(key.parent)}>"
1311
+ return f"{hash_value(key.kind)}-{hash_value(key.id_or_name)}-<{keyHash(key.parent)}>"
1311
1312
 
1312
1313
  return f"K-{keyHash(value)}"
1314
+
1313
1315
  raise NotImplementedError(f"Type {type(value)} can't be safely used in an uniquePropertyIndex")
1314
1316
 
1317
+ # zero/empty string and these should not be locked
1315
1318
  if not value and not self.unique.lockEmpty:
1316
- return [] # We are zero/empty string and these should not be locked
1317
- if not self.multiple and not isinstance(value, list):
1318
- return [hashValue(value)]
1319
- # We have a multiple bone or multiple values here
1319
+ return []
1320
+
1321
+ # Always work with list of values
1320
1322
  if not isinstance(value, list):
1321
1323
  value = [value]
1322
- tmpList = [hashValue(x) for x in value]
1324
+
1325
+ values = [hash_value(val) for val in value]
1326
+
1323
1327
  if self.unique.method == UniqueLockMethod.SameValue:
1324
- # We should lock each entry individually; lock each value
1325
- return tmpList
1328
+ # Lock each entry individually
1329
+ return values
1330
+
1326
1331
  elif self.unique.method == UniqueLockMethod.SameSet:
1327
- # We should ignore the sort-order; so simply sort that List
1328
- tmpList.sort()
1329
- # Lock the value for that specific list
1330
- return [hashValue(", ".join(tmpList))]
1332
+ # Ignore the sort-order; so simply sort that list
1333
+ values.sort()
1334
+
1335
+ # Lock the value for that specific list (equals to UniqueLockMethod.SameList)
1336
+ return [hash_value(", ".join(values))]
1331
1337
 
1332
1338
  def getUniquePropertyIndexValues(self, skel: 'viur.core.skeleton.SkeletonInstance', name: str) -> list[str]:
1333
1339
  """
@@ -1341,10 +1347,11 @@ class BaseBone(object):
1341
1347
  :return: A list of strings representing the hashed values for the current bone value(s) in the skeleton.
1342
1348
  If the bone has no value, an empty list is returned.
1343
1349
  """
1344
- val = skel[name]
1345
- if val is None:
1346
- return []
1347
- return self._hashValueForUniquePropertyIndex(val)
1350
+ if self.compute:
1351
+ self.serialize_compute(skel, name)
1352
+
1353
+ values = [value for _, _, value in self.iter_bone_value(skel, name) if value is not None]
1354
+ return self._hashValueForUniquePropertyIndex(values) if values else []
1348
1355
 
1349
1356
  def getReferencedBlobs(self, skel: 'viur.core.skeleton.SkeletonInstance', name: str) -> set[str]:
1350
1357
  """
@@ -215,13 +215,18 @@ class RecordBone(BaseBone):
215
215
 
216
216
  return result
217
217
 
218
- def getUniquePropertyIndexValues(self, valuesCache: dict, name: str) -> list[str]:
218
+ def getUniquePropertyIndexValues(self, skel: "SkeletonInstance", name: str) -> list[str]:
219
219
  """
220
- This method is intentionally not implemented as it's not possible to determine how to derive
221
- a key from the related skeleton being used (i.e., which fields to include and how).
222
-
220
+ Returns hashes over all fields of each record value, using the serialized form as canonical input.
223
221
  """
224
- raise NotImplementedError()
222
+ values = []
223
+
224
+ for _, _, using_skel in self.iter_bone_value(skel, name):
225
+ if using_skel is None:
226
+ continue
227
+ values.append(json.dumps(using_skel.dump(), sort_keys=True, default=str))
228
+
229
+ return self._hashValueForUniquePropertyIndex(values) if values else []
225
230
 
226
231
  def structure(self) -> dict:
227
232
  return super().structure() | {
@@ -1271,24 +1271,32 @@ class RelationalBone(BaseBone):
1271
1271
 
1272
1272
  return result
1273
1273
 
1274
- def getUniquePropertyIndexValues(self, valuesCache: dict, name: str) -> list[str]:
1274
+ def getUniquePropertyIndexValues(self, skel: "SkeletonInstance", name: str) -> list[str]:
1275
1275
  """
1276
1276
  Generates unique property index values for the RelationalBone based on the referenced keys.
1277
1277
  Can be overridden if different behavior is required (e.g., examining values from `prop:usingSkel`).
1278
1278
 
1279
- :param dict valuesCache: The cache containing the current values of the bone.
1279
+ :param skel: The skeleton instance.
1280
1280
  :param str name: The name of the bone for which to generate unique property index values.
1281
1281
 
1282
1282
  :return: A list containing the unique property index values for the specified bone.
1283
1283
  :rtype: List[str]
1284
1284
  """
1285
- value = valuesCache.get(name)
1286
- if not value: # We don't have a value to lock
1287
- return []
1288
- if isinstance(value, dict):
1289
- return self._hashValueForUniquePropertyIndex(value["dest"]["key"])
1290
- elif isinstance(value, list):
1291
- return self._hashValueForUniquePropertyIndex([entry["dest"]["key"] for entry in value if entry])
1285
+ values = []
1286
+
1287
+ for _, _, v in self.iter_bone_value(skel, name):
1288
+ if not v:
1289
+ continue
1290
+
1291
+ if self.using and (rel_skel := v.get("rel")):
1292
+ values.append(json.dumps(
1293
+ {"key": str(v["dest"]["key"]), "rel": rel_skel.dump()},
1294
+ sort_keys=True, default=str,
1295
+ ))
1296
+ else:
1297
+ values.append(v["dest"]["key"])
1298
+
1299
+ return self._hashValueForUniquePropertyIndex(values) if values else []
1292
1300
 
1293
1301
  def structure(self) -> dict:
1294
1302
  return super().structure() | {
@@ -292,19 +292,15 @@ class StringBone(RawBone):
292
292
  :param skel: The skeleton instance.
293
293
  :param name: The name of the property.
294
294
  :return: A list of unique index values for the property.
295
- :raises NotImplementedError: If the StringBone has languages and the implementation
296
- for this case is not yet defined.
297
295
  """
298
- if self.languages:
299
- # Not yet implemented as it's unclear if we should keep each language distinct or not
300
- raise NotImplementedError()
296
+ if not self.caseSensitive:
297
+ values = [
298
+ value.lower() if isinstance(value, str) else value
299
+ for _, _, value in self.iter_bone_value(skel, name)
300
+ if value is not None
301
+ ]
301
302
 
302
- if not self.caseSensitive and (value := skel[name]) is not None:
303
- if self.multiple:
304
- value = [v.lower() for v in value]
305
- else:
306
- value = value.lower()
307
- return self._hashValueForUniquePropertyIndex(value)
303
+ return self._hashValueForUniquePropertyIndex(values) if values else []
308
304
 
309
305
  return super().getUniquePropertyIndexValues(skel, name)
310
306
 
@@ -438,25 +438,6 @@ class TextBone(RawBone):
438
438
  elif not self.languages and isinstance(val, str):
439
439
  skel[boneName] = self.singleValueFromClient(val, skel, boneName, None)[0]
440
440
 
441
- def getUniquePropertyIndexValues(self, valuesCache: dict, name: str) -> list[str]:
442
- """
443
- Retrieves the unique property index values for the TextBone.
444
-
445
- If the TextBone supports multiple languages, this method raises a NotImplementedError, as it's unclear
446
- whether each language should be kept distinct or not. Otherwise, it calls the superclass's
447
- getUniquePropertyIndexValues method to retrieve the unique property index values.
448
-
449
- :param valuesCache: A dictionary containing the cached values for the TextBone.
450
- :param name: The name of the TextBone.
451
- :return: A list of unique property index values for the TextBone.
452
- :raises NotImplementedError: If the TextBone supports multiple languages.
453
- """
454
- if self.languages:
455
- # Not yet implemented as it's unclear if we should keep each language distinct or not
456
- raise NotImplementedError()
457
-
458
- return super().getUniquePropertyIndexValues(valuesCache, name)
459
-
460
441
  def structure(self) -> dict:
461
442
  return super().structure() | {
462
443
  "valid_html": self.validHtml,