simo 1.7.19__py3-none-any.whl → 2.0.0__py3-none-any.whl

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.

Potentially problematic release.


This version of simo might be problematic. Click here for more details.

Files changed (289) hide show
  1. simo/__pycache__/asgi.cpython-38.pyc +0 -0
  2. simo/__pycache__/on_http_start.cpython-38.pyc +0 -0
  3. simo/__pycache__/settings.cpython-38.pyc +0 -0
  4. simo/__pycache__/urls.cpython-38.pyc +0 -0
  5. simo/__pycache__/wsgi.cpython-38.pyc +0 -0
  6. simo/core/__pycache__/__init__.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  9. simo/core/__pycache__/api_meta.cpython-38.pyc +0 -0
  10. simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
  11. simo/core/__pycache__/autocomplete_views.cpython-38.pyc +0 -0
  12. simo/core/__pycache__/base_types.cpython-38.pyc +0 -0
  13. simo/core/__pycache__/context.cpython-38.pyc +0 -0
  14. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  15. simo/core/__pycache__/events.cpython-38.pyc +0 -0
  16. simo/core/__pycache__/filters.cpython-38.pyc +0 -0
  17. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  18. simo/core/__pycache__/gateways.cpython-38.pyc +0 -0
  19. simo/core/__pycache__/managers.cpython-38.pyc +0 -0
  20. simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
  21. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  22. simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
  23. simo/core/__pycache__/routing.cpython-38.pyc +0 -0
  24. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  25. simo/core/__pycache__/signal_receivers.cpython-38.pyc +0 -0
  26. simo/core/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  27. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  28. simo/core/__pycache__/todos.cpython-38.pyc +0 -0
  29. simo/core/__pycache__/views.cpython-38.pyc +0 -0
  30. simo/core/admin.py +28 -18
  31. simo/core/api.py +157 -16
  32. simo/core/api_meta.py +87 -0
  33. simo/core/auto_urls.py +4 -1
  34. simo/core/autocomplete_views.py +8 -4
  35. simo/core/base_types.py +1 -0
  36. simo/core/context.py +3 -1
  37. simo/core/controllers.py +112 -32
  38. simo/core/db_backend/base.py +7 -22
  39. simo/core/drf_braces/README +3 -0
  40. simo/core/drf_braces/__init__.py +7 -0
  41. simo/core/drf_braces/__pycache__/__init__.cpython-38.pyc +0 -0
  42. simo/core/drf_braces/__pycache__/utils.cpython-38.pyc +0 -0
  43. simo/core/drf_braces/fields/__init__.py +5 -0
  44. simo/core/drf_braces/fields/__pycache__/__init__.cpython-38.pyc +0 -0
  45. simo/core/drf_braces/fields/__pycache__/_fields.cpython-38.pyc +0 -0
  46. simo/core/drf_braces/fields/__pycache__/custom.cpython-38.pyc +0 -0
  47. simo/core/drf_braces/fields/__pycache__/mixins.cpython-38.pyc +0 -0
  48. simo/core/drf_braces/fields/__pycache__/modified.cpython-38.pyc +0 -0
  49. simo/core/drf_braces/fields/_fields.py +48 -0
  50. simo/core/drf_braces/fields/custom.py +107 -0
  51. simo/core/drf_braces/fields/mixins.py +58 -0
  52. simo/core/drf_braces/fields/modified.py +41 -0
  53. simo/core/drf_braces/forms/__init__.py +0 -0
  54. simo/core/drf_braces/forms/fields.py +20 -0
  55. simo/core/drf_braces/forms/serializer_form.py +156 -0
  56. simo/core/drf_braces/mixins.py +52 -0
  57. simo/core/drf_braces/models.py +0 -0
  58. simo/core/drf_braces/parsers.py +72 -0
  59. simo/core/drf_braces/renderers.py +37 -0
  60. simo/core/drf_braces/serializers/__init__.py +0 -0
  61. simo/core/drf_braces/serializers/__pycache__/__init__.cpython-38.pyc +0 -0
  62. simo/core/drf_braces/serializers/__pycache__/form_serializer.cpython-38.pyc +0 -0
  63. simo/core/drf_braces/serializers/enforce_validation_serializer.py +214 -0
  64. simo/core/drf_braces/serializers/form_serializer.py +391 -0
  65. simo/core/drf_braces/serializers/swapping.py +48 -0
  66. simo/core/drf_braces/tests/__init__.py +0 -0
  67. simo/core/drf_braces/tests/fields/__init__.py +0 -0
  68. simo/core/drf_braces/tests/fields/test_custom.py +94 -0
  69. simo/core/drf_braces/tests/fields/test_fields.py +13 -0
  70. simo/core/drf_braces/tests/fields/test_mixins.py +96 -0
  71. simo/core/drf_braces/tests/fields/test_modified.py +40 -0
  72. simo/core/drf_braces/tests/forms/__init__.py +0 -0
  73. simo/core/drf_braces/tests/forms/test_fields.py +46 -0
  74. simo/core/drf_braces/tests/forms/test_serializer_form.py +256 -0
  75. simo/core/drf_braces/tests/serializers/__init__.py +0 -0
  76. simo/core/drf_braces/tests/serializers/test_enforce_validation_serializer.py +169 -0
  77. simo/core/drf_braces/tests/serializers/test_form_serializer.py +387 -0
  78. simo/core/drf_braces/tests/serializers/test_swapping.py +40 -0
  79. simo/core/drf_braces/tests/test_mixins.py +111 -0
  80. simo/core/drf_braces/tests/test_parsers.py +73 -0
  81. simo/core/drf_braces/tests/test_renderers.py +23 -0
  82. simo/core/drf_braces/tests/test_utils.py +73 -0
  83. simo/core/drf_braces/utils.py +209 -0
  84. simo/core/events.py +3 -3
  85. simo/core/forms.py +79 -37
  86. simo/core/gateways.py +31 -14
  87. simo/core/management/__pycache__/__init__.cpython-38.pyc +0 -0
  88. simo/core/management/commands/__pycache__/__init__.cpython-38.pyc +0 -0
  89. simo/core/management/commands/gateways_manager.py +0 -1
  90. simo/core/managers.py +81 -0
  91. simo/core/middleware.py +25 -0
  92. simo/core/migrations/0026_category_instance.py +20 -0
  93. simo/core/migrations/0027_remove_component_tags.py +17 -0
  94. simo/core/migrations/0028_rename_subcomponents_component_slaves.py +18 -0
  95. simo/core/migrations/0029_auto_20240229_1331.py +33 -0
  96. simo/core/migrations/__pycache__/0026_category_instance.cpython-38.pyc +0 -0
  97. simo/core/migrations/__pycache__/0027_remove_component_tags.cpython-38.pyc +0 -0
  98. simo/core/migrations/__pycache__/0028_rename_subcomponents_component_slaves.cpython-38.pyc +0 -0
  99. simo/core/migrations/__pycache__/0029_auto_20240229_1331.cpython-38.pyc +0 -0
  100. simo/core/models.py +103 -66
  101. simo/core/permissions.py +28 -2
  102. simo/core/serializers.py +330 -26
  103. simo/core/socket_consumers.py +5 -14
  104. simo/core/tasks.py +11 -1
  105. simo/core/templates/admin/base.html +37 -10
  106. simo/core/templates/admin/wizard/discovery.html +188 -0
  107. simo/core/templates/admin/wizard/wizard_add.html +5 -5
  108. simo/core/utils/__pycache__/serialization.cpython-38.pyc +0 -0
  109. simo/core/utils/admin.py +9 -2
  110. simo/core/utils/formsets.py +17 -16
  111. simo/core/utils/helpers.py +1 -0
  112. simo/core/utils/serialization.py +56 -0
  113. simo/core/utils/type_constants.py +1 -1
  114. simo/core/utils/validators.py +14 -1
  115. simo/core/views.py +13 -0
  116. simo/fleet/__pycache__/admin.cpython-38.pyc +0 -0
  117. simo/fleet/__pycache__/api.cpython-38.pyc +0 -0
  118. simo/fleet/__pycache__/auto_urls.cpython-38.pyc +0 -0
  119. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  120. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  121. simo/fleet/__pycache__/gateways.cpython-38.pyc +0 -0
  122. simo/fleet/__pycache__/managers.cpython-38.pyc +0 -0
  123. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  124. simo/fleet/__pycache__/serializers.cpython-38.pyc +0 -0
  125. simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  126. simo/fleet/__pycache__/utils.cpython-38.pyc +0 -0
  127. simo/fleet/__pycache__/views.cpython-38.pyc +0 -0
  128. simo/fleet/admin.py +54 -25
  129. simo/fleet/api.py +59 -3
  130. simo/fleet/auto_urls.py +2 -3
  131. simo/fleet/controllers.py +199 -16
  132. simo/fleet/forms.py +325 -483
  133. simo/fleet/gateways.py +44 -2
  134. simo/fleet/managers.py +32 -0
  135. simo/fleet/migrations/0025_auto_20240130_1334.py +27 -0
  136. simo/fleet/migrations/0026_rename_i2cinterface_scl_pin_and_more.py +64 -0
  137. simo/fleet/migrations/0027_auto_20240306_0802.py +170 -0
  138. simo/fleet/migrations/0028_remove_i2cinterface_scl_pin_no_and_more.py +21 -0
  139. simo/fleet/migrations/0029_alter_i2cinterface_scl_pin_and_more.py +24 -0
  140. simo/fleet/migrations/0030_colonelpin_label_alter_colonel_type.py +24 -0
  141. simo/fleet/migrations/0031_alter_colonel_type.py +18 -0
  142. simo/fleet/migrations/__pycache__/0025_auto_20240130_1334.cpython-38.pyc +0 -0
  143. simo/fleet/migrations/__pycache__/0026_rename_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
  144. simo/fleet/migrations/__pycache__/0027_auto_20240306_0802.cpython-38.pyc +0 -0
  145. simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-38.pyc +0 -0
  146. simo/fleet/migrations/__pycache__/0029_alter_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
  147. simo/fleet/migrations/__pycache__/0030_colonelpin_label_alter_colonel_type.cpython-38.pyc +0 -0
  148. simo/fleet/migrations/__pycache__/0031_alter_colonel_type.cpython-38.pyc +0 -0
  149. simo/fleet/models.py +134 -82
  150. simo/fleet/serializers.py +35 -1
  151. simo/fleet/socket_consumers.py +239 -76
  152. simo/fleet/utils.py +15 -53
  153. simo/fleet/views.py +28 -14
  154. simo/generic/controllers.py +13 -89
  155. simo/generic/forms.py +30 -23
  156. simo/generic/gateways.py +98 -10
  157. simo/generic/models.py +3 -3
  158. simo/multimedia/controllers.py +9 -8
  159. simo/settings.py +7 -4
  160. simo/urls.py +4 -8
  161. simo/users/__pycache__/admin.cpython-38.pyc +0 -0
  162. simo/users/__pycache__/api.cpython-38.pyc +0 -0
  163. simo/users/__pycache__/auto_urls.cpython-38.pyc +0 -0
  164. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  165. simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
  166. simo/users/__pycache__/sso_urls.cpython-38.pyc +0 -0
  167. simo/users/admin.py +8 -1
  168. simo/users/api.py +38 -2
  169. simo/users/auto_urls.py +2 -2
  170. simo/users/migrations/0025_rename_name_fingerprint_type_and_more.py +22 -0
  171. simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-38.pyc +0 -0
  172. simo/users/models.py +2 -3
  173. simo/users/serializers.py +15 -1
  174. simo/users/sso_urls.py +3 -3
  175. simo/wsgi.py +7 -0
  176. {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/METADATA +8 -9
  177. {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/RECORD +180 -210
  178. {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/WHEEL +1 -1
  179. simo/core/__pycache__/__init__.cpython-37.pyc +0 -0
  180. simo/core/__pycache__/admin.cpython-37.pyc +0 -0
  181. simo/core/__pycache__/api.cpython-37.pyc +0 -0
  182. simo/core/__pycache__/apps.cpython-38.pyc +0 -0
  183. simo/core/__pycache__/controllers.cpython-37.pyc +0 -0
  184. simo/core/__pycache__/events.cpython-37.pyc +0 -0
  185. simo/core/__pycache__/forms.cpython-37.pyc +0 -0
  186. simo/core/__pycache__/gateways.cpython-37.pyc +0 -0
  187. simo/core/__pycache__/models.cpython-37.pyc +0 -0
  188. simo/core/__pycache__/scripts.cpython-37.pyc +0 -0
  189. simo/core/__pycache__/serializers.cpython-37.pyc +0 -0
  190. simo/core/__pycache__/widgets.cpython-37.pyc +0 -0
  191. simo/core/db_backend/__pycache__/__init__.cpython-38.pyc +0 -0
  192. simo/core/db_backend/__pycache__/base.cpython-38.pyc +0 -0
  193. simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc +0 -0
  194. simo/core/management/commands/__pycache__/run_gateway.cpython-38.pyc +0 -0
  195. simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  196. simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc +0 -0
  197. simo/core/migrations/__pycache__/0003_create_default_zones_and_categories.cpython-38.pyc +0 -0
  198. simo/core/migrations/__pycache__/0004_create_generic.cpython-38.pyc +0 -0
  199. simo/core/migrations/__pycache__/0005_component_subcomponents.cpython-38.pyc +0 -0
  200. simo/core/migrations/__pycache__/0006_alter_component_subcomponents.cpython-38.pyc +0 -0
  201. simo/core/migrations/__pycache__/0007_component_change_init_to.cpython-38.pyc +0 -0
  202. simo/core/migrations/__pycache__/0008_alter_component_change_init_to.cpython-38.pyc +0 -0
  203. simo/core/migrations/__pycache__/0009_auto_20220707_1404.cpython-38.pyc +0 -0
  204. simo/core/migrations/__pycache__/0010_historyaggregate.cpython-38.pyc +0 -0
  205. simo/core/migrations/__pycache__/0011_component_last_change.cpython-38.pyc +0 -0
  206. simo/core/migrations/__pycache__/0012_instance.cpython-38.pyc +0 -0
  207. simo/core/migrations/__pycache__/0013_auto_20231003_0754.cpython-38.pyc +0 -0
  208. simo/core/migrations/__pycache__/0014_zone_instance.cpython-38.pyc +0 -0
  209. simo/core/migrations/__pycache__/0015_auto_20231004_1113.cpython-38.pyc +0 -0
  210. simo/core/migrations/__pycache__/0016_auto_20231004_1113.cpython-38.pyc +0 -0
  211. simo/core/migrations/__pycache__/0017_auto_20231004_1313.cpython-38.pyc +0 -0
  212. simo/core/migrations/__pycache__/0018_auto_20231005_0622.cpython-38.pyc +0 -0
  213. simo/core/migrations/__pycache__/0019_alter_gateway_type.cpython-38.pyc +0 -0
  214. simo/core/migrations/__pycache__/0020_component_meta.cpython-38.pyc +0 -0
  215. simo/core/migrations/__pycache__/0021_auto_20231020_1041.cpython-38.pyc +0 -0
  216. simo/core/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  217. simo/core/templatetags/__pycache__/__init__.cpython-38.pyc +0 -0
  218. simo/core/templatetags/__pycache__/components_list.cpython-38.pyc +0 -0
  219. simo/core/utils/__pycache__/__init__.cpython-38.pyc +0 -0
  220. simo/core/utils/__pycache__/admin.cpython-38.pyc +0 -0
  221. simo/core/utils/__pycache__/config_values.cpython-38.pyc +0 -0
  222. simo/core/utils/__pycache__/easing.cpython-38.pyc +0 -0
  223. simo/core/utils/__pycache__/form_fields.cpython-38.pyc +0 -0
  224. simo/core/utils/__pycache__/form_widgets.cpython-38.pyc +0 -0
  225. simo/core/utils/__pycache__/formsets.cpython-38.pyc +0 -0
  226. simo/core/utils/__pycache__/helpers.cpython-38.pyc +0 -0
  227. simo/core/utils/__pycache__/logs.cpython-38.pyc +0 -0
  228. simo/core/utils/__pycache__/mixins.cpython-38.pyc +0 -0
  229. simo/core/utils/__pycache__/model_helpers.cpython-38.pyc +0 -0
  230. simo/core/utils/__pycache__/relay.cpython-38.pyc +0 -0
  231. simo/core/utils/__pycache__/type_constants.cpython-38.pyc +0 -0
  232. simo/core/utils/__pycache__/validators.cpython-38.pyc +0 -0
  233. simo/fleet/tasks.py +0 -25
  234. simo/generic/__pycache__/__init__.cpython-37.pyc +0 -0
  235. simo/generic/__pycache__/__init__.cpython-38.pyc +0 -0
  236. simo/generic/__pycache__/app_widgets.cpython-38.pyc +0 -0
  237. simo/generic/__pycache__/base_types.cpython-38.pyc +0 -0
  238. simo/generic/__pycache__/controllers.cpython-37.pyc +0 -0
  239. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  240. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  241. simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
  242. simo/generic/__pycache__/models.cpython-38.pyc +0 -0
  243. simo/generic/__pycache__/routing.cpython-38.pyc +0 -0
  244. simo/generic/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  245. simo/generic/__pycache__/tasks.cpython-38.pyc +0 -0
  246. simo/generic/__pycache__/widgets.cpython-37.pyc +0 -0
  247. simo/generic/tasks.py +0 -41
  248. simo/multimedia/__pycache__/__init__.cpython-38.pyc +0 -0
  249. simo/multimedia/__pycache__/admin.cpython-38.pyc +0 -0
  250. simo/multimedia/__pycache__/api.cpython-38.pyc +0 -0
  251. simo/multimedia/__pycache__/app_widgets.cpython-38.pyc +0 -0
  252. simo/multimedia/__pycache__/base_types.cpython-38.pyc +0 -0
  253. simo/multimedia/__pycache__/controllers.cpython-38.pyc +0 -0
  254. simo/multimedia/__pycache__/forms.cpython-38.pyc +0 -0
  255. simo/multimedia/__pycache__/models.cpython-38.pyc +0 -0
  256. simo/multimedia/__pycache__/serializers.cpython-38.pyc +0 -0
  257. simo/multimedia/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  258. simo/multimedia/migrations/__pycache__/0002_sound_length.cpython-38.pyc +0 -0
  259. simo/multimedia/migrations/__pycache__/0003_alter_sound_length.cpython-38.pyc +0 -0
  260. simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc +0 -0
  261. simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  262. simo/notifications/__pycache__/__init__.cpython-38.pyc +0 -0
  263. simo/notifications/__pycache__/admin.cpython-38.pyc +0 -0
  264. simo/notifications/__pycache__/api.cpython-38.pyc +0 -0
  265. simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
  266. simo/notifications/__pycache__/serializers.cpython-38.pyc +0 -0
  267. simo/notifications/__pycache__/utils.cpython-38.pyc +0 -0
  268. simo/notifications/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  269. simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.pyc +0 -0
  270. simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  271. simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  272. simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc +0 -0
  273. simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-38.pyc +0 -0
  274. simo/users/migrations/__pycache__/0004_user_secret_key.cpython-38.pyc +0 -0
  275. simo/users/migrations/__pycache__/0005_permissionsrole_instance.cpython-38.pyc +0 -0
  276. simo/users/migrations/__pycache__/0006_auto_20231003_0850.cpython-38.pyc +0 -0
  277. simo/users/migrations/__pycache__/0007_auto_20231003_1228.cpython-38.pyc +0 -0
  278. simo/users/migrations/__pycache__/0008_auto_20231003_1229.cpython-38.pyc +0 -0
  279. simo/users/migrations/__pycache__/0009_remove_user_role.cpython-38.pyc +0 -0
  280. simo/users/migrations/__pycache__/0010_auto_20231004_1313.cpython-38.pyc +0 -0
  281. simo/users/migrations/__pycache__/0011_auto_20231004_1313.cpython-38.pyc +0 -0
  282. simo/users/migrations/__pycache__/0012_alter_userinstancerole_unique_together.cpython-38.pyc +0 -0
  283. simo/users/migrations/__pycache__/0013_remove_user_roles.cpython-38.pyc +0 -0
  284. simo/users/migrations/__pycache__/0014_user_roles.cpython-38.pyc +0 -0
  285. simo/users/migrations/__pycache__/0015_remove_user_at_home.cpython-38.pyc +0 -0
  286. simo/users/migrations/__pycache__/0016_auto_20231005_1050.cpython-38.pyc +0 -0
  287. simo/users/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  288. {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/LICENSE.md +0 -0
  289. {simo-1.7.19.dist-info → simo-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,387 @@
1
+ from __future__ import absolute_import, print_function, unicode_literals
2
+ import unittest
3
+ from collections import OrderedDict
4
+ from datetime import datetime
5
+
6
+ import mock
7
+ import six
8
+ from django import forms
9
+ from rest_framework import fields, serializers
10
+
11
+ from ...serializers.form_serializer import (
12
+ FormSerializer,
13
+ FormSerializerBase,
14
+ FormSerializerFieldMixin,
15
+ FormSerializerMeta,
16
+ FormSerializerOptions,
17
+ LazyLoadingValidationsMixin,
18
+ make_form_serializer_field,
19
+ )
20
+
21
+
22
+ TESTING_MODULE = 'drf_braces.serializers.form_serializer'
23
+
24
+
25
+ class TestForm(forms.Form):
26
+ foo = forms.CharField(max_length=12)
27
+ bar = forms.IntegerField(max_value=500)
28
+ happy = forms.ChoiceField(required=False)
29
+ other = forms.DateTimeField()
30
+
31
+ def __init__(self, *args, **kwargs):
32
+ super(TestForm, self).__init__(*args, **kwargs)
33
+ self.fields['happy'].choices = [
34
+ ('happy', 'choices'),
35
+ ]
36
+
37
+ def clean(self):
38
+ data = super(TestForm, self).clean()
39
+ data.update({
40
+ 'bar': 257,
41
+ })
42
+ return data
43
+
44
+
45
+ class CaptureFailedFieldValidationFieldMixin(FormSerializerFieldMixin):
46
+
47
+ def capture_failed_field(self, field_name, field_data, error_msg):
48
+ self._failed_validation = {field_name: (field_data, error_msg)}
49
+
50
+
51
+ class TestFormSerializerFieldMixin(unittest.TestCase):
52
+ def setUp(self):
53
+ super(TestFormSerializerFieldMixin, self).setUp()
54
+
55
+ class Field(FormSerializerFieldMixin, fields.IntegerField):
56
+ pass
57
+
58
+ class CaptureFailedField(CaptureFailedFieldValidationFieldMixin, fields.TimeField):
59
+ pass
60
+
61
+ class Serializer(serializers.Serializer):
62
+ field = Field()
63
+ field_two = CaptureFailedField()
64
+
65
+ class Meta(object):
66
+ minimum_required = []
67
+ failure_mode = 'fail'
68
+
69
+ self.serializer = Serializer()
70
+ self.field = self.serializer.fields['field']
71
+
72
+ def test_run_validation_invalid(self):
73
+ with self.assertRaises(fields.ValidationError):
74
+ self.field.run_validation('a')
75
+
76
+ def test_run_validation_invalid_failure_mode(self):
77
+ self.serializer.partial = True
78
+
79
+ with self.assertRaises(fields.ValidationError):
80
+ self.field.run_validation('a')
81
+
82
+ def test_run_validation_invalid_required_field(self):
83
+ self.serializer.partial = True
84
+ self.serializer.Meta.failure_mode = 'drop'
85
+ self.serializer.Meta.minimum_required = ['field']
86
+
87
+ with self.assertRaises(fields.ValidationError):
88
+ self.field.run_validation('a')
89
+
90
+ def test_run_validation_skip(self):
91
+ self.serializer.partial = True
92
+ self.serializer.Meta.failure_mode = 'drop'
93
+ self.serializer.Meta.minimum_required = []
94
+
95
+ with self.assertRaises(fields.SkipField):
96
+ self.field.run_validation('a')
97
+
98
+ def test_run_validation_skip_capture(self):
99
+ self.serializer.partial = True
100
+ self.serializer.Meta.failure_mode = 'drop'
101
+ self.serializer.Meta.minimum_required = []
102
+ self.field_two = self.serializer.fields['field_two']
103
+
104
+ with self.assertRaises(fields.SkipField):
105
+ self.field_two.run_validation('Really Bad Time')
106
+
107
+ self.assertEqual('Really Bad Time', self.field_two._failed_validation['field_two'][0])
108
+ self.assertIn('Time has wrong format. Use one of these formats instead', six.text_type(self.field_two._failed_validation['field_two'][1]))
109
+
110
+
111
+ class TestUtils(unittest.TestCase):
112
+ def test_make_form_serializer_field(self):
113
+ field_class = make_form_serializer_field(fields.IntegerField)
114
+
115
+ self.assertEqual(field_class.__name__, 'IntegerFormSerializerField')
116
+ self.assertTrue(issubclass(field_class, FormSerializerFieldMixin))
117
+
118
+
119
+ class TestFormSerializerOptions(unittest.TestCase):
120
+ def test_init(self):
121
+ meta = mock.Mock(failure_mode='fail', foo='bar')
122
+
123
+ options = FormSerializerOptions(meta, 'foo')
124
+
125
+ self.assertEqual(options.form, meta.form)
126
+ self.assertEqual(options.failure_mode, 'fail')
127
+ self.assertEqual(options.minimum_required, meta.minimum_required)
128
+ self.assertEqual(options.field_mapping, meta.field_mapping)
129
+ self.assertEqual(options.foo, meta.foo)
130
+
131
+ def test_init_invalid(self):
132
+ with self.assertRaises(AssertionError):
133
+ FormSerializerOptions(mock.Mock(failure_mode='foo'), 'foo')
134
+ with self.assertRaises(AssertionError):
135
+ FormSerializerOptions(mock.Mock(form=None), 'foo')
136
+ with self.assertRaises(NotImplementedError):
137
+ FormSerializerOptions(mock.Mock(failure_mode='ignore'), 'foo')
138
+
139
+
140
+ class TestFormSerializerMeta(unittest.TestCase):
141
+ @mock.patch(TESTING_MODULE + '.FormSerializerOptions')
142
+ def test_no_parents(self, mock_form_serializer_options):
143
+ class TestSerializer(six.with_metaclass(FormSerializerMeta, serializers.Serializer)):
144
+ pass
145
+
146
+ self.assertFalse(mock_form_serializer_options.called)
147
+
148
+ def test_no_meta(self):
149
+ with self.assertRaises(AssertionError):
150
+ class TestSerializer(six.with_metaclass(FormSerializerMeta, FormSerializer)):
151
+ pass
152
+
153
+ def test_new(self):
154
+ class TestSerializer(six.with_metaclass(FormSerializerMeta, FormSerializer)):
155
+ class Meta(object):
156
+ form = TestForm
157
+
158
+ self.assertIsInstance(TestSerializer.Meta, FormSerializerOptions)
159
+ self.assertIs(TestSerializer.Meta.form, TestForm)
160
+
161
+
162
+ class TestFormSerializerBase(unittest.TestCase):
163
+ def setUp(self):
164
+ super(TestFormSerializerBase, self).setUp()
165
+
166
+ class Serializer(FormSerializerBase):
167
+ other = fields.CharField()
168
+
169
+ class Meta(object):
170
+ failure_mode = 'drop'
171
+ form = TestForm
172
+ minimum_required = ['foo']
173
+ field_mapping = {}
174
+
175
+ def capture_failed_fields(self, raw_data, form_errors):
176
+ self._failed_validation = {k: v for k, v in raw_data.items() if k in form_errors}
177
+
178
+ self.serializer_class = Serializer
179
+
180
+ def test_init(self):
181
+ serializer = self.serializer_class()
182
+
183
+ self.assertTrue(serializer.partial)
184
+
185
+ def test_get_form(self):
186
+ serializer = self.serializer_class()
187
+
188
+ form = serializer.get_form()
189
+
190
+ self.assertIsInstance(form, TestForm)
191
+ self.assertIn('foo', form.fields)
192
+ self.assertIn('bar', form.fields)
193
+ self.assertIn('other', form.fields)
194
+ self.assertTrue(form.fields['foo'].required)
195
+ self.assertFalse(form.fields['bar'].required)
196
+ self.assertFalse(form.fields['other'].required)
197
+
198
+ def test_get_fields(self):
199
+ serializer = self.serializer_class()
200
+ serializer.Meta.field_mapping.update({
201
+ forms.CharField: fields.BooleanField,
202
+ })
203
+
204
+ serializer_fields = serializer.get_fields()
205
+
206
+ self.assertIsInstance(serializer_fields, OrderedDict)
207
+ self.assertIn('foo', serializer_fields)
208
+ self.assertIn('bar', serializer_fields)
209
+ self.assertIn('other', serializer_fields)
210
+ self.assertIsInstance(serializer_fields['foo'], fields.BooleanField)
211
+ self.assertIsInstance(serializer_fields['bar'], fields.IntegerField)
212
+ self.assertIsInstance(serializer_fields['other'], fields.CharField)
213
+
214
+ def test_get_fields_excluded(self):
215
+ serializer = self.serializer_class()
216
+ serializer.Meta.exclude = ['foo']
217
+ serializer.Meta.field_mapping.update({
218
+ forms.CharField: fields.BooleanField,
219
+ })
220
+
221
+ serializer_fields = serializer.get_fields()
222
+
223
+ self.assertIsInstance(serializer_fields, OrderedDict)
224
+ self.assertNotIn('foo', serializer_fields)
225
+
226
+ def test_get_fields_not_mapped(self):
227
+ serializer = self.serializer_class()
228
+
229
+ class FooField(forms.Field):
230
+ pass
231
+
232
+ class FooForm(TestForm):
233
+ stuff = FooField()
234
+
235
+ serializer.Meta.form = FooForm
236
+
237
+ with self.assertRaises(TypeError):
238
+ serializer.get_fields()
239
+
240
+ def test_get_field(self):
241
+ serializer = self.serializer_class()
242
+ form_field = forms.ChoiceField(
243
+ choices=[('foo', 'bar')],
244
+ required=False,
245
+ validators=[mock.sentinel.validator],
246
+ )
247
+
248
+ field = serializer._get_field(form_field, fields.ChoiceField)
249
+
250
+ self.assertIsInstance(field, fields.ChoiceField)
251
+ self.assertTrue(field.allow_blank)
252
+ self.assertTrue(field.allow_null)
253
+ self.assertListEqual(field.validators, [mock.sentinel.validator])
254
+ self.assertDictEqual(field.choice_strings_to_values, {
255
+ 'foo': 'foo',
256
+ })
257
+
258
+ def test_get_field_kwargs(self):
259
+ serializer = self.serializer_class()
260
+ form_field = forms.IntegerField(
261
+ max_value=500,
262
+ initial=100,
263
+ required=True,
264
+ validators=[mock.sentinel.validator],
265
+ )
266
+
267
+ kwargs = serializer._get_field_kwargs(form_field, fields.IntegerField)
268
+
269
+ self.assertDictContainsSubset({
270
+ 'default': 100,
271
+ 'validators': [mock.sentinel.validator, mock.ANY],
272
+ }, kwargs)
273
+ self.assertNotIn('required', kwargs)
274
+
275
+ def test_get_field_kwargs_choice_field(self):
276
+ serializer = self.serializer_class()
277
+ form_field = forms.ChoiceField(
278
+ choices=[('foo', 'FOO'), ('bar', 'BAR')]
279
+ )
280
+
281
+ kwargs = serializer._get_field_kwargs(form_field, fields.ChoiceField)
282
+
283
+ self.assertDictContainsSubset({
284
+ 'choices': OrderedDict([
285
+ ('foo', 'foo'),
286
+ ('bar', 'bar'),
287
+ ]),
288
+ }, kwargs)
289
+
290
+ def test_validate(self):
291
+ serializer = self.serializer_class(data={
292
+ 'foo': 'hello',
293
+ 'bar': '100',
294
+ 'other': 'stuff',
295
+ })
296
+
297
+ self.assertTrue(serializer.is_valid())
298
+ self.assertDictEqual(serializer.validated_data, {
299
+ 'foo': 'hello',
300
+ 'bar': 257,
301
+ 'happy': '',
302
+ })
303
+
304
+ def test_validate_valid(self):
305
+ serializer = self.serializer_class(data={
306
+ 'foo': 'hello',
307
+ 'bar': '100',
308
+ 'other': '2015-01-01 12:30',
309
+ })
310
+
311
+ self.assertTrue(serializer.is_valid())
312
+ self.assertDictEqual(serializer.validated_data, {
313
+ 'other': datetime(2015, 1, 1, 12, 30),
314
+ 'foo': 'hello',
315
+ 'bar': 257,
316
+ 'happy': '',
317
+ })
318
+
319
+ def test_validate_fail(self):
320
+ self.serializer_class.Meta.failure_mode = 'fail'
321
+ serializer = self.serializer_class(data={
322
+ 'foo': 'hello',
323
+ 'bar': '100',
324
+ 'other': 'stuff',
325
+ })
326
+
327
+ self.assertFalse(serializer.is_valid())
328
+ self.assertDictEqual(serializer.errors, {
329
+ 'other': ['Enter a valid date/time.'],
330
+ })
331
+
332
+ def test_validate_capture_errors(self):
333
+ self.serializer_class.Meta.failure_mode = 'drop'
334
+ serializer = self.serializer_class(data={
335
+ 'foo': 'Chime Oduzo',
336
+ 'bar': 45,
337
+ 'other': 'Extremely bad time'
338
+ })
339
+ self.assertTrue(serializer.is_valid())
340
+ self.assertDictEqual(serializer.validated_data, {
341
+ 'foo': 'Chime Oduzo',
342
+ 'bar': 257,
343
+ 'happy': '',
344
+ })
345
+ self.assertDictEqual({'other': 'Extremely bad time'}, serializer._failed_validation)
346
+
347
+ def test_to_representation(self):
348
+ with self.assertRaises(NotImplementedError):
349
+ self.serializer_class().to_representation({})
350
+
351
+
352
+ class TestFormSerializer(unittest.TestCase):
353
+ def test_bases(self):
354
+ self.assertTrue(issubclass(FormSerializer, FormSerializerBase))
355
+ self.assertIsInstance(FormSerializer, FormSerializerMeta)
356
+
357
+
358
+ class TestLazyLoadingValidationsMixin(unittest.TestCase):
359
+ def setUp(self):
360
+ super(TestLazyLoadingValidationsMixin, self).setUp()
361
+
362
+ class Serializer(LazyLoadingValidationsMixin, FormSerializer):
363
+ class Meta(object):
364
+ form = TestForm
365
+
366
+ self.serializer_class = Serializer
367
+
368
+ def test_repopulate_form_fields(self):
369
+ serializer = self.serializer_class()
370
+
371
+ # sanity check
372
+ self.assertDictEqual(serializer.fields['happy'].choices, {})
373
+
374
+ serializer.repopulate_form_fields()
375
+
376
+ self.assertDictEqual(dict(serializer.fields['happy'].choices), {'happy': 'happy'})
377
+ self.assertDictEqual(dict(serializer.fields['happy'].choice_strings_to_values),
378
+ {'happy': 'happy'})
379
+
380
+ @mock.patch.object(serializers.Serializer, 'to_internal_value')
381
+ @mock.patch.object(LazyLoadingValidationsMixin, 'repopulate_form_fields')
382
+ def test_to_internal_value(self, mock_repopulate_form_fields, mock_super_to_internal_value):
383
+ serializer = self.serializer_class()
384
+
385
+ serializer.to_internal_value({})
386
+
387
+ mock_repopulate_form_fields.assert_called_once_with()
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import absolute_import, print_function, unicode_literals
3
+ import unittest
4
+
5
+ from rest_framework import serializers
6
+
7
+ from ...serializers.swapping import SwappingSerializerMixin
8
+
9
+
10
+ class ChildSerializer(serializers.Serializer):
11
+ foo = serializers.IntegerField()
12
+ bar = serializers.CharField()
13
+
14
+
15
+ class ChildAlternativeSerializer(serializers.Serializer):
16
+ foo = serializers.CharField()
17
+ bar = serializers.CharField()
18
+
19
+
20
+ class ParentSerializer(serializers.Serializer):
21
+ child = ChildSerializer()
22
+
23
+
24
+ class GrandParentSerializer(serializers.Serializer):
25
+ parent = ParentSerializer()
26
+ parents = ParentSerializer(many=True)
27
+
28
+
29
+ class TestSwappingSerializerMixin(unittest.TestCase):
30
+ def test_swapping(self):
31
+ class Swappable(SwappingSerializerMixin, GrandParentSerializer):
32
+ class Meta(object):
33
+ swappable_fields = {
34
+ ChildSerializer: ChildAlternativeSerializer,
35
+ }
36
+
37
+ swapped = Swappable()
38
+
39
+ self.assertIsInstance(swapped.fields['parent'].fields['child'], ChildAlternativeSerializer)
40
+ self.assertIsInstance(swapped.fields['parents'].child.fields['child'], ChildAlternativeSerializer)
@@ -0,0 +1,111 @@
1
+ from __future__ import absolute_import, print_function, unicode_literals
2
+ import unittest
3
+
4
+ import mock
5
+ from rest_framework.generics import GenericAPIView
6
+
7
+ from ..mixins import (
8
+ MapDataViewMixin,
9
+ MultipleSerializersViewMixin,
10
+ StrippingJSONViewMixin,
11
+ )
12
+
13
+
14
+ class TestMultipleSerializersViewMixin(unittest.TestCase):
15
+ def setUp(self):
16
+ super(TestMultipleSerializersViewMixin, self).setUp()
17
+
18
+ class View(MultipleSerializersViewMixin, GenericAPIView):
19
+ pass
20
+
21
+ self.view = View()
22
+
23
+ @mock.patch.object(GenericAPIView, 'get_serializer_context')
24
+ @mock.patch.object(GenericAPIView, 'get_serializer_class')
25
+ def test_get_serializer(self,
26
+ mock_get_serializer_class,
27
+ mock_get_serializer_context):
28
+ context = {'context': 'here'}
29
+ mock_get_serializer_context.return_value = context
30
+
31
+ serializer = self.view.get_serializer(hello='world')
32
+
33
+ self.assertEqual(serializer, mock_get_serializer_class.return_value.return_value)
34
+ mock_get_serializer_class.assert_called_once_with()
35
+ mock_get_serializer_class.return_value.assert_called_once_with(
36
+ hello='world', context=context
37
+ )
38
+ mock_get_serializer_context.assert_called_once_with()
39
+
40
+ @mock.patch.object(GenericAPIView, 'get_serializer_context')
41
+ @mock.patch.object(GenericAPIView, 'get_serializer_class')
42
+ def test_get_serializer_with_class(self,
43
+ mock_get_serializer_class,
44
+ mock_get_serializer_context):
45
+ context = {'context': 'here'}
46
+ mock_get_serializer_context.return_value = context
47
+ serializer_class = mock.MagicMock()
48
+
49
+ serializer = self.view.get_serializer(hello='world', serializer_class=serializer_class)
50
+
51
+ self.assertEqual(serializer, serializer_class.return_value)
52
+ self.assertFalse(mock_get_serializer_class.called)
53
+ serializer_class.assert_called_once_with(hello='world', context=context)
54
+ mock_get_serializer_context.assert_called_once_with()
55
+
56
+
57
+ class TestMapDataViewMixin(unittest.TestCase):
58
+ def setUp(self):
59
+ super(TestMapDataViewMixin, self).setUp()
60
+
61
+ class View(MapDataViewMixin, GenericAPIView):
62
+ pass
63
+
64
+ self.view = View()
65
+ self.view.request = mock.MagicMock(data=mock.sentinel.data)
66
+
67
+ def test_get_data_no_mapper(self):
68
+ actual = self.view.get_data()
69
+
70
+ self.assertEqual(actual, mock.sentinel.data)
71
+
72
+ @mock.patch.object(GenericAPIView, 'get_serializer_context')
73
+ def test_get_data_attribute_mapper(self, mock_get_serializer_context):
74
+ mapper = self.view.data_mapper_class = mock.MagicMock()
75
+ actual = self.view.get_data()
76
+
77
+ self.assertEqual(actual, mapper.return_value.return_value)
78
+ mapper.assert_called_once_with(
79
+ context=mock_get_serializer_context.return_value
80
+ )
81
+ mapper.return_value.assert_called_once_with(mock.sentinel.data)
82
+
83
+ @mock.patch.object(GenericAPIView, 'get_serializer_context')
84
+ def test_get_data_provided(self, mock_get_serializer_context):
85
+ mapper = mock.MagicMock()
86
+ actual = self.view.get_data(mapper_class=mapper)
87
+
88
+ self.assertEqual(actual, mapper.return_value.return_value)
89
+ mapper.assert_called_once_with(
90
+ context=mock_get_serializer_context.return_value
91
+ )
92
+ mapper.return_value.assert_called_once_with(mock.sentinel.data)
93
+
94
+
95
+ class TestStrippingJSONViewMixin(unittest.TestCase):
96
+ def setUp(self):
97
+ super(TestStrippingJSONViewMixin, self).setUp()
98
+
99
+ class View(StrippingJSONViewMixin, GenericAPIView):
100
+ pass
101
+
102
+ self.view = View()
103
+ self.view.request = mock.MagicMock()
104
+
105
+ def test_get_parser_context(self):
106
+ self.view.parser_root = mock.sentinel.parser_root
107
+
108
+ actual = self.view.get_parser_context(self.view.request)
109
+
110
+ self.assertIn('parse_root', actual)
111
+ self.assertEqual(actual['parse_root'], mock.sentinel.parser_root)
@@ -0,0 +1,73 @@
1
+ from __future__ import absolute_import, print_function, unicode_literals
2
+ import json
3
+ import unittest
4
+ from collections import OrderedDict
5
+
6
+ import six
7
+ from rest_framework import parsers
8
+
9
+ from ..parsers import SortedJSONParser, StrippingJSONParser
10
+
11
+
12
+ class TestSortedJSONParser(unittest.TestCase):
13
+ def setUp(self):
14
+ super(TestSortedJSONParser, self).setUp()
15
+ self.parser = SortedJSONParser()
16
+
17
+ def test_parser(self):
18
+ content = json.dumps({'hello': 'world'}).encode('utf-8')
19
+ stream = six.BytesIO(content)
20
+
21
+ actual_data = self.parser.parse(stream=stream)
22
+
23
+ self.assertEqual(actual_data, OrderedDict([('hello', 'world')]))
24
+
25
+ def test_parser_invalid_json(self):
26
+ content = (
27
+ json.dumps({'hello': 'world'})
28
+ .replace('"', "'")
29
+ .encode('utf-8')
30
+ )
31
+ stream = six.BytesIO(content)
32
+
33
+ with self.assertRaises(parsers.ParseError):
34
+ self.parser.parse(stream=stream)
35
+
36
+
37
+ class TestStrippingJSONParser(unittest.TestCase):
38
+ def setUp(self):
39
+ super(TestStrippingJSONParser, self).setUp()
40
+ self.parser = StrippingJSONParser()
41
+
42
+ def test_parser(self):
43
+ content = json.dumps({'root': {'hello': 'world'}}).encode('utf-8')
44
+ stream = six.BytesIO(content)
45
+
46
+ actual_data = self.parser.parse(
47
+ stream=stream,
48
+ parser_context={'parse_root': 'root'}
49
+ )
50
+
51
+ self.assertEqual(actual_data, OrderedDict([('hello', 'world')]))
52
+
53
+ def test_parser_no_root(self):
54
+ content = json.dumps({'root': {'hello': 'world'}}).encode('utf-8')
55
+ stream = six.BytesIO(content)
56
+
57
+ actual_data = self.parser.parse(
58
+ stream=stream,
59
+ parser_context={}
60
+ )
61
+
62
+ self.assertEqual(actual_data, {'root': {'hello': 'world'}})
63
+
64
+ def test_parser_different_root(self):
65
+ content = json.dumps({'root': {'hello': 'world'}}).encode('utf-8')
66
+ stream = six.BytesIO(content)
67
+
68
+ actual_data = self.parser.parse(
69
+ stream=stream,
70
+ parser_context={'parse_root': 'foo'}
71
+ )
72
+
73
+ self.assertEqual(actual_data, {'root': {'hello': 'world'}})
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import absolute_import, print_function, unicode_literals
3
+ import datetime
4
+ import json
5
+ import unittest
6
+
7
+ from ..renderers import DoubleAsStrJsonEncoder
8
+
9
+
10
+ class TestDoubleAsStrJsonEncoder(unittest.TestCase):
11
+ def test_encode(self):
12
+ self.assertEqual(
13
+ json.loads(json.dumps({
14
+ 'a': 12345678901234567890,
15
+ 'b': [123],
16
+ 'c': datetime.date(2010, 1, 2)
17
+ }, cls=DoubleAsStrJsonEncoder)),
18
+ {
19
+ 'a': '12345678901234567890',
20
+ 'b': [123],
21
+ 'c': '2010-01-02',
22
+ }
23
+ )