1. import importlib
    
  2. import unittest
    
  3. from io import StringIO
    
  4. 
    
  5. from django.core import management, serializers
    
  6. from django.core.serializers.base import DeserializationError
    
  7. from django.test import SimpleTestCase, TestCase, TransactionTestCase
    
  8. 
    
  9. from .models import Author
    
  10. from .tests import SerializersTestBase, SerializersTransactionTestBase
    
  11. 
    
  12. try:
    
  13.     import yaml
    
  14. 
    
  15.     HAS_YAML = True
    
  16. except ImportError:
    
  17.     HAS_YAML = False
    
  18. 
    
  19. YAML_IMPORT_ERROR_MESSAGE = r"No module named yaml"
    
  20. 
    
  21. 
    
  22. class YamlImportModuleMock:
    
  23.     """Provides a wrapped import_module function to simulate yaml ImportError
    
  24. 
    
  25.     In order to run tests that verify the behavior of the YAML serializer
    
  26.     when run on a system that has yaml installed (like the django CI server),
    
  27.     mock import_module, so that it raises an ImportError when the yaml
    
  28.     serializer is being imported.  The importlib.import_module() call is
    
  29.     being made in the serializers.register_serializer().
    
  30. 
    
  31.     Refs: #12756
    
  32.     """
    
  33. 
    
  34.     def __init__(self):
    
  35.         self._import_module = importlib.import_module
    
  36. 
    
  37.     def import_module(self, module_path):
    
  38.         if module_path == serializers.BUILTIN_SERIALIZERS["yaml"]:
    
  39.             raise ImportError(YAML_IMPORT_ERROR_MESSAGE)
    
  40. 
    
  41.         return self._import_module(module_path)
    
  42. 
    
  43. 
    
  44. class NoYamlSerializerTestCase(SimpleTestCase):
    
  45.     """Not having pyyaml installed provides a misleading error
    
  46. 
    
  47.     Refs: #12756
    
  48.     """
    
  49. 
    
  50.     @classmethod
    
  51.     def setUpClass(cls):
    
  52.         """Removes imported yaml and stubs importlib.import_module"""
    
  53.         super().setUpClass()
    
  54. 
    
  55.         cls._import_module_mock = YamlImportModuleMock()
    
  56.         importlib.import_module = cls._import_module_mock.import_module
    
  57. 
    
  58.         # clear out cached serializers to emulate yaml missing
    
  59.         serializers._serializers = {}
    
  60. 
    
  61.     @classmethod
    
  62.     def tearDownClass(cls):
    
  63.         """Puts yaml back if necessary"""
    
  64.         super().tearDownClass()
    
  65. 
    
  66.         importlib.import_module = cls._import_module_mock._import_module
    
  67. 
    
  68.         # clear out cached serializers to clean out BadSerializer instances
    
  69.         serializers._serializers = {}
    
  70. 
    
  71.     def test_serializer_pyyaml_error_message(self):
    
  72.         """Using yaml serializer without pyyaml raises ImportError"""
    
  73.         jane = Author(name="Jane")
    
  74.         with self.assertRaises(ImportError):
    
  75.             serializers.serialize("yaml", [jane])
    
  76. 
    
  77.     def test_deserializer_pyyaml_error_message(self):
    
  78.         """Using yaml deserializer without pyyaml raises ImportError"""
    
  79.         with self.assertRaises(ImportError):
    
  80.             serializers.deserialize("yaml", "")
    
  81. 
    
  82.     def test_dumpdata_pyyaml_error_message(self):
    
  83.         """Calling dumpdata produces an error when yaml package missing"""
    
  84.         with self.assertRaisesMessage(
    
  85.             management.CommandError, YAML_IMPORT_ERROR_MESSAGE
    
  86.         ):
    
  87.             management.call_command("dumpdata", format="yaml")
    
  88. 
    
  89. 
    
  90. @unittest.skipUnless(HAS_YAML, "No yaml library detected")
    
  91. class YamlSerializerTestCase(SerializersTestBase, TestCase):
    
  92.     serializer_name = "yaml"
    
  93.     pkless_str = """- model: serializers.category
    
  94.   pk: null
    
  95.   fields:
    
  96.     name: Reference
    
  97. - model: serializers.category
    
  98.   fields:
    
  99.     name: Non-fiction"""
    
  100. 
    
  101.     mapping_ordering_str = (
    
  102.         """- model: serializers.article
    
  103.   pk: %(article_pk)s
    
  104.   fields:
    
  105.     author: %(author_pk)s
    
  106.     headline: Poker has no place on ESPN
    
  107.     pub_date: 2006-06-16 11:00:00
    
  108.     categories:"""
    
  109.         + (
    
  110.             " [%(first_category_pk)s, %(second_category_pk)s]"
    
  111.             if HAS_YAML and yaml.__version__ < "5.1"
    
  112.             else "\n    - %(first_category_pk)s\n    - %(second_category_pk)s"
    
  113.         )
    
  114.         + """
    
  115.     meta_data: []
    
  116. """
    
  117.     )
    
  118. 
    
  119.     @staticmethod
    
  120.     def _validate_output(serial_str):
    
  121.         try:
    
  122.             yaml.safe_load(StringIO(serial_str))
    
  123.         except Exception:
    
  124.             return False
    
  125.         else:
    
  126.             return True
    
  127. 
    
  128.     @staticmethod
    
  129.     def _get_pk_values(serial_str):
    
  130.         ret_list = []
    
  131.         stream = StringIO(serial_str)
    
  132.         for obj_dict in yaml.safe_load(stream):
    
  133.             ret_list.append(obj_dict["pk"])
    
  134.         return ret_list
    
  135. 
    
  136.     @staticmethod
    
  137.     def _get_field_values(serial_str, field_name):
    
  138.         ret_list = []
    
  139.         stream = StringIO(serial_str)
    
  140.         for obj_dict in yaml.safe_load(stream):
    
  141.             if "fields" in obj_dict and field_name in obj_dict["fields"]:
    
  142.                 field_value = obj_dict["fields"][field_name]
    
  143.                 # yaml.safe_load will return non-string objects for some
    
  144.                 # of the fields we are interested in, this ensures that
    
  145.                 # everything comes back as a string
    
  146.                 if isinstance(field_value, str):
    
  147.                     ret_list.append(field_value)
    
  148.                 else:
    
  149.                     ret_list.append(str(field_value))
    
  150.         return ret_list
    
  151. 
    
  152.     def test_yaml_deserializer_exception(self):
    
  153.         with self.assertRaises(DeserializationError):
    
  154.             for obj in serializers.deserialize("yaml", "{"):
    
  155.                 pass
    
  156. 
    
  157. 
    
  158. @unittest.skipUnless(HAS_YAML, "No yaml library detected")
    
  159. class YamlSerializerTransactionTestCase(
    
  160.     SerializersTransactionTestBase, TransactionTestCase
    
  161. ):
    
  162.     serializer_name = "yaml"
    
  163.     fwd_ref_str = """- model: serializers.article
    
  164.   pk: 1
    
  165.   fields:
    
  166.     headline: Forward references pose no problem
    
  167.     pub_date: 2006-06-16 15:00:00
    
  168.     categories: [1]
    
  169.     author: 1
    
  170. - model: serializers.category
    
  171.   pk: 1
    
  172.   fields:
    
  173.     name: Reference
    
  174. - model: serializers.author
    
  175.   pk: 1
    
  176.   fields:
    
  177.     name: Agnes"""