We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Allow whitespace and comments in db key file
It would be useful if the db key file parsed generously around additional whitespace.

Also in order to help guide an admin while rotating keys, we should allow to add comments.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pulpcore/app/models/fields.py`
Content:
```
1 import json
2 import logging
3 import os
4 from gettext import gettext as _
5 from functools import lru_cache
6 
7 from cryptography.fernet import Fernet, MultiFernet
8 from django.conf import settings
9 from django.core.exceptions import ImproperlyConfigured
10 from django.db.models import Lookup, FileField, JSONField
11 from django.db.models.fields import Field, TextField
12 from django.utils.encoding import force_bytes, force_str
13 
14 
15 from pulpcore.app.files import TemporaryDownloadedFile
16 from pulpcore.app.loggers import deprecation_logger
17 
18 _logger = logging.getLogger(__name__)
19 
20 
21 @lru_cache(maxsize=1)
22 def _fernet():
23     # Cache the enryption keys once per application.
24     _logger.debug(f"Loading encryption key from {settings.DB_ENCRYPTION_KEY}")
25     with open(settings.DB_ENCRYPTION_KEY, "rb") as key_file:
26         return MultiFernet([Fernet(key) for key in key_file.readlines()])
27 
28 
29 class ArtifactFileField(FileField):
30     """
31     A custom FileField that always saves files to location specified by 'upload_to'.
32 
33     The field can be set as either a path to the file or File object. In both cases the file is
34     moved or copied to the location specified by 'upload_to' field parameter.
35     """
36 
37     def pre_save(self, model_instance, add):
38         """
39         Return FieldFile object which specifies path to the file to be stored in database.
40 
41         There are two ways to get artifact into Pulp: sync and upload.
42 
43         The upload case
44          - file is not stored yet, aka file._committed = False
45          - nothing to do here in addition to Django pre_save actions
46 
47         The sync case:
48          - file is already stored in a temporary location, aka file._committed = True
49          - it needs to be moved into Pulp artifact storage if it's not there
50          - TemporaryDownloadedFile takes care of correctly set storage path
51          - only then Django pre_save actions should be performed
52 
53         Args:
54             model_instance (`class::pulpcore.plugin.Artifact`): The instance this field belongs to.
55             add (bool): Whether the instance is being saved to the database for the first time.
56                         Ignored by Django pre_save method.
57 
58         Returns:
59             FieldFile object just before saving.
60 
61         """
62         file = model_instance.file
63         artifact_storage_path = self.upload_to(model_instance, "")
64 
65         already_in_place = file.name in [
66             artifact_storage_path,
67             os.path.join(settings.MEDIA_ROOT, artifact_storage_path),
68         ]
69         is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, "artifact"))
70 
71         if not already_in_place and is_in_artifact_storage:
72             raise ValueError(
73                 _(
74                     "The file referenced by the Artifact is already present in "
75                     "Artifact storage. Files must be stored outside this location "
76                     "prior to Artifact creation."
77                 )
78             )
79 
80         move = file._committed and file.name != artifact_storage_path
81         if move:
82             if not already_in_place:
83                 file._file = TemporaryDownloadedFile(open(file.name, "rb"))
84             file._committed = False
85 
86         return super().pre_save(model_instance, add)
87 
88 
89 class EncryptedTextField(TextField):
90     """A field mixin that encrypts text using settings.DB_ENCRYPTION_KEY."""
91 
92     def __init__(self, *args, **kwargs):
93         if kwargs.get("primary_key"):
94             raise ImproperlyConfigured("EncryptedTextField does not support primary_key=True.")
95         if kwargs.get("unique"):
96             raise ImproperlyConfigured("EncryptedTextField does not support unique=True.")
97         if kwargs.get("db_index"):
98             raise ImproperlyConfigured("EncryptedTextField does not support db_index=True.")
99         super().__init__(*args, **kwargs)
100 
101     def get_prep_value(self, value):
102         if value is not None:
103             assert isinstance(value, str)
104             value = force_str(_fernet().encrypt(force_bytes(value)))
105         return super().get_prep_value(value)
106 
107     def from_db_value(self, value, expression, connection):
108         if value is not None:
109             value = force_str(_fernet().decrypt(force_bytes(value)))
110         return value
111 
112 
113 class EncryptedJSONField(JSONField):
114     """A Field mixin that encrypts the JSON text using settings.DP_ENCRYPTION_KEY."""
115 
116     def __init__(self, *args, **kwargs):
117         if kwargs.get("primary_key"):
118             raise ImproperlyConfigured("EncryptedJSONField does not support primary_key=True.")
119         if kwargs.get("unique"):
120             raise ImproperlyConfigured("EncryptedJSONField does not support unique=True.")
121         if kwargs.get("db_index"):
122             raise ImproperlyConfigured("EncryptedJSONField does not support db_index=True.")
123         super().__init__(*args, **kwargs)
124 
125     def encrypt(self, value):
126         if isinstance(value, dict):
127             return {k: self.encrypt(v) for k, v in value.items()}
128         elif isinstance(value, (list, tuple, set)):
129             return [self.encrypt(v) for v in value]
130 
131         return force_str(_fernet().encrypt(force_bytes(json.dumps(value, cls=self.encoder))))
132 
133     def decrypt(self, value):
134         if isinstance(value, dict):
135             return {k: self.decrypt(v) for k, v in value.items()}
136         elif isinstance(value, (list, tuple, set)):
137             return [self.decrypt(v) for v in value]
138 
139         dec_value = force_str(_fernet().decrypt(force_bytes(value)))
140         try:
141             return json.loads(dec_value, cls=self.decoder)
142         except json.JSONDecodeError:
143             deprecation_logger.info(
144                 "Failed to decode json in an EncryptedJSONField. Falling back to eval. "
145                 "Please run pulpcore-manager rotate-db-key to repair."
146                 "This is deprecated and will be removed in pulpcore 3.40."
147             )
148             return eval(dec_value)
149 
150     def get_prep_value(self, value):
151         if value is not None:
152             if hasattr(value, "as_sql"):
153                 return value
154             value = self.encrypt(value)
155         return super().get_prep_value(value)
156 
157     def from_db_value(self, value, expression, connection):
158         if value is not None:
159             value = self.decrypt(super().from_db_value(value, expression, connection))
160         return value
161 
162 
163 @Field.register_lookup
164 class NotEqualLookup(Lookup):
165     # this is copied from https://docs.djangoproject.com/en/3.2/howto/custom-lookups/
166     lookup_name = "ne"
167 
168     def as_sql(self, compiler, connection):
169         lhs, lhs_params = self.process_lhs(compiler, connection)
170         rhs, rhs_params = self.process_rhs(compiler, connection)
171         params = lhs_params + rhs_params
172         return "%s <> %s" % (lhs, rhs), params
173
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pulpcore/app/models/fields.py b/pulpcore/app/models/fields.py
--- a/pulpcore/app/models/fields.py
+++ b/pulpcore/app/models/fields.py
@@ -1,17 +1,15 @@
 import json
 import logging
 import os
-from gettext import gettext as _
 from functools import lru_cache
+from gettext import gettext as _
 
 from cryptography.fernet import Fernet, MultiFernet
 from django.conf import settings
 from django.core.exceptions import ImproperlyConfigured
-from django.db.models import Lookup, FileField, JSONField
+from django.db.models import FileField, JSONField, Lookup
 from django.db.models.fields import Field, TextField
 from django.utils.encoding import force_bytes, force_str
-
-
 from pulpcore.app.files import TemporaryDownloadedFile
 from pulpcore.app.loggers import deprecation_logger
 
@@ -23,7 +21,13 @@
     # Cache the enryption keys once per application.
     _logger.debug(f"Loading encryption key from {settings.DB_ENCRYPTION_KEY}")
     with open(settings.DB_ENCRYPTION_KEY, "rb") as key_file:
-        return MultiFernet([Fernet(key) for key in key_file.readlines()])
+        return MultiFernet(
+            [
+                Fernet(key.strip())
+                for key in key_file.readlines()
+                if not key.startswith(b"#") and key.strip() != b""
+            ]
+        )
 
 
 class ArtifactFileField(FileField):

{"golden_diff": "diff --git a/pulpcore/app/models/fields.py b/pulpcore/app/models/fields.py\n--- a/pulpcore/app/models/fields.py\n+++ b/pulpcore/app/models/fields.py\n@@ -1,17 +1,15 @@\n import json\n import logging\n import os\n-from gettext import gettext as _\n from functools import lru_cache\n+from gettext import gettext as _\n \n from cryptography.fernet import Fernet, MultiFernet\n from django.conf import settings\n from django.core.exceptions import ImproperlyConfigured\n-from django.db.models import Lookup, FileField, JSONField\n+from django.db.models import FileField, JSONField, Lookup\n from django.db.models.fields import Field, TextField\n from django.utils.encoding import force_bytes, force_str\n-\n-\n from pulpcore.app.files import TemporaryDownloadedFile\n from pulpcore.app.loggers import deprecation_logger\n \n@@ -23,7 +21,13 @@\n     # Cache the enryption keys once per application.\n     _logger.debug(f\"Loading encryption key from {settings.DB_ENCRYPTION_KEY}\")\n     with open(settings.DB_ENCRYPTION_KEY, \"rb\") as key_file:\n-        return MultiFernet([Fernet(key) for key in key_file.readlines()])\n+        return MultiFernet(\n+            [\n+                Fernet(key.strip())\n+                for key in key_file.readlines()\n+                if not key.startswith(b\"#\") and key.strip() != b\"\"\n+            ]\n+        )\n \n \n class ArtifactFileField(FileField):\n", "issue": "Allow whitespace and comments in db key file\nIt would be useful if the db key file parsed generously around additional whitespace.\r\n\r\nAlso in order to help guide an admin while rotating keys, we should allow to add comments.\n", "before_files": [{"content": "import json\nimport logging\nimport os\nfrom gettext import gettext as _\nfrom functools import lru_cache\n\nfrom cryptography.fernet import Fernet, MultiFernet\nfrom django.conf import settings\nfrom django.core.exceptions import ImproperlyConfigured\nfrom django.db.models import Lookup, FileField, JSONField\nfrom django.db.models.fields import Field, TextField\nfrom django.utils.encoding import force_bytes, force_str\n\n\nfrom pulpcore.app.files import TemporaryDownloadedFile\nfrom pulpcore.app.loggers import deprecation_logger\n\n_logger = logging.getLogger(__name__)\n\n\n@lru_cache(maxsize=1)\ndef _fernet():\n    # Cache the enryption keys once per application.\n    _logger.debug(f\"Loading encryption key from {settings.DB_ENCRYPTION_KEY}\")\n    with open(settings.DB_ENCRYPTION_KEY, \"rb\") as key_file:\n        return MultiFernet([Fernet(key) for key in key_file.readlines()])\n\n\nclass ArtifactFileField(FileField):\n    \"\"\"\n    A custom FileField that always saves files to location specified by 'upload_to'.\n\n    The field can be set as either a path to the file or File object. In both cases the file is\n    moved or copied to the location specified by 'upload_to' field parameter.\n    \"\"\"\n\n    def pre_save(self, model_instance, add):\n        \"\"\"\n        Return FieldFile object which specifies path to the file to be stored in database.\n\n        There are two ways to get artifact into Pulp: sync and upload.\n\n        The upload case\n         - file is not stored yet, aka file._committed = False\n         - nothing to do here in addition to Django pre_save actions\n\n        The sync case:\n         - file is already stored in a temporary location, aka file._committed = True\n         - it needs to be moved into Pulp artifact storage if it's not there\n         - TemporaryDownloadedFile takes care of correctly set storage path\n         - only then Django pre_save actions should be performed\n\n        Args:\n            model_instance (`class::pulpcore.plugin.Artifact`): The instance this field belongs to.\n            add (bool): Whether the instance is being saved to the database for the first time.\n                        Ignored by Django pre_save method.\n\n        Returns:\n            FieldFile object just before saving.\n\n        \"\"\"\n        file = model_instance.file\n        artifact_storage_path = self.upload_to(model_instance, \"\")\n\n        already_in_place = file.name in [\n            artifact_storage_path,\n            os.path.join(settings.MEDIA_ROOT, artifact_storage_path),\n        ]\n        is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, \"artifact\"))\n\n        if not already_in_place and is_in_artifact_storage:\n            raise ValueError(\n                _(\n                    \"The file referenced by the Artifact is already present in \"\n                    \"Artifact storage. Files must be stored outside this location \"\n                    \"prior to Artifact creation.\"\n                )\n            )\n\n        move = file._committed and file.name != artifact_storage_path\n        if move:\n            if not already_in_place:\n                file._file = TemporaryDownloadedFile(open(file.name, \"rb\"))\n            file._committed = False\n\n        return super().pre_save(model_instance, add)\n\n\nclass EncryptedTextField(TextField):\n    \"\"\"A field mixin that encrypts text using settings.DB_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            assert isinstance(value, str)\n            value = force_str(_fernet().encrypt(force_bytes(value)))\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = force_str(_fernet().decrypt(force_bytes(value)))\n        return value\n\n\nclass EncryptedJSONField(JSONField):\n    \"\"\"A Field mixin that encrypts the JSON text using settings.DP_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def encrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.encrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.encrypt(v) for v in value]\n\n        return force_str(_fernet().encrypt(force_bytes(json.dumps(value, cls=self.encoder))))\n\n    def decrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.decrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.decrypt(v) for v in value]\n\n        dec_value = force_str(_fernet().decrypt(force_bytes(value)))\n        try:\n            return json.loads(dec_value, cls=self.decoder)\n        except json.JSONDecodeError:\n            deprecation_logger.info(\n                \"Failed to decode json in an EncryptedJSONField. Falling back to eval. \"\n                \"Please run pulpcore-manager rotate-db-key to repair.\"\n                \"This is deprecated and will be removed in pulpcore 3.40.\"\n            )\n            return eval(dec_value)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            if hasattr(value, \"as_sql\"):\n                return value\n            value = self.encrypt(value)\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = self.decrypt(super().from_db_value(value, expression, connection))\n        return value\n\n\n@Field.register_lookup\nclass NotEqualLookup(Lookup):\n    # this is copied from https://docs.djangoproject.com/en/3.2/howto/custom-lookups/\n    lookup_name = \"ne\"\n\n    def as_sql(self, compiler, connection):\n        lhs, lhs_params = self.process_lhs(compiler, connection)\n        rhs, rhs_params = self.process_rhs(compiler, connection)\n        params = lhs_params + rhs_params\n        return \"%s <> %s\" % (lhs, rhs), params\n", "path": "pulpcore/app/models/fields.py"}], "after_files": [{"content": "import json\nimport logging\nimport os\nfrom functools import lru_cache\nfrom gettext import gettext as _\n\nfrom cryptography.fernet import Fernet, MultiFernet\nfrom django.conf import settings\nfrom django.core.exceptions import ImproperlyConfigured\nfrom django.db.models import FileField, JSONField, Lookup\nfrom django.db.models.fields import Field, TextField\nfrom django.utils.encoding import force_bytes, force_str\nfrom pulpcore.app.files import TemporaryDownloadedFile\nfrom pulpcore.app.loggers import deprecation_logger\n\n_logger = logging.getLogger(__name__)\n\n\n@lru_cache(maxsize=1)\ndef _fernet():\n    # Cache the enryption keys once per application.\n    _logger.debug(f\"Loading encryption key from {settings.DB_ENCRYPTION_KEY}\")\n    with open(settings.DB_ENCRYPTION_KEY, \"rb\") as key_file:\n        return MultiFernet(\n            [\n                Fernet(key.strip())\n                for key in key_file.readlines()\n                if not key.startswith(b\"#\") and key.strip() != b\"\"\n            ]\n        )\n\n\nclass ArtifactFileField(FileField):\n    \"\"\"\n    A custom FileField that always saves files to location specified by 'upload_to'.\n\n    The field can be set as either a path to the file or File object. In both cases the file is\n    moved or copied to the location specified by 'upload_to' field parameter.\n    \"\"\"\n\n    def pre_save(self, model_instance, add):\n        \"\"\"\n        Return FieldFile object which specifies path to the file to be stored in database.\n\n        There are two ways to get artifact into Pulp: sync and upload.\n\n        The upload case\n         - file is not stored yet, aka file._committed = False\n         - nothing to do here in addition to Django pre_save actions\n\n        The sync case:\n         - file is already stored in a temporary location, aka file._committed = True\n         - it needs to be moved into Pulp artifact storage if it's not there\n         - TemporaryDownloadedFile takes care of correctly set storage path\n         - only then Django pre_save actions should be performed\n\n        Args:\n            model_instance (`class::pulpcore.plugin.Artifact`): The instance this field belongs to.\n            add (bool): Whether the instance is being saved to the database for the first time.\n                        Ignored by Django pre_save method.\n\n        Returns:\n            FieldFile object just before saving.\n\n        \"\"\"\n        file = model_instance.file\n        artifact_storage_path = self.upload_to(model_instance, \"\")\n\n        already_in_place = file.name in [\n            artifact_storage_path,\n            os.path.join(settings.MEDIA_ROOT, artifact_storage_path),\n        ]\n        is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, \"artifact\"))\n\n        if not already_in_place and is_in_artifact_storage:\n            raise ValueError(\n                _(\n                    \"The file referenced by the Artifact is already present in \"\n                    \"Artifact storage. Files must be stored outside this location \"\n                    \"prior to Artifact creation.\"\n                )\n            )\n\n        move = file._committed and file.name != artifact_storage_path\n        if move:\n            if not already_in_place:\n                file._file = TemporaryDownloadedFile(open(file.name, \"rb\"))\n            file._committed = False\n\n        return super().pre_save(model_instance, add)\n\n\nclass EncryptedTextField(TextField):\n    \"\"\"A field mixin that encrypts text using settings.DB_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedTextField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            assert isinstance(value, str)\n            value = force_str(_fernet().encrypt(force_bytes(value)))\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = force_str(_fernet().decrypt(force_bytes(value)))\n        return value\n\n\nclass EncryptedJSONField(JSONField):\n    \"\"\"A Field mixin that encrypts the JSON text using settings.DP_ENCRYPTION_KEY.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get(\"primary_key\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support primary_key=True.\")\n        if kwargs.get(\"unique\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support unique=True.\")\n        if kwargs.get(\"db_index\"):\n            raise ImproperlyConfigured(\"EncryptedJSONField does not support db_index=True.\")\n        super().__init__(*args, **kwargs)\n\n    def encrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.encrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.encrypt(v) for v in value]\n\n        return force_str(_fernet().encrypt(force_bytes(json.dumps(value, cls=self.encoder))))\n\n    def decrypt(self, value):\n        if isinstance(value, dict):\n            return {k: self.decrypt(v) for k, v in value.items()}\n        elif isinstance(value, (list, tuple, set)):\n            return [self.decrypt(v) for v in value]\n\n        dec_value = force_str(_fernet().decrypt(force_bytes(value)))\n        try:\n            return json.loads(dec_value, cls=self.decoder)\n        except json.JSONDecodeError:\n            deprecation_logger.info(\n                \"Failed to decode json in an EncryptedJSONField. Falling back to eval. \"\n                \"Please run pulpcore-manager rotate-db-key to repair.\"\n                \"This is deprecated and will be removed in pulpcore 3.40.\"\n            )\n            return eval(dec_value)\n\n    def get_prep_value(self, value):\n        if value is not None:\n            if hasattr(value, \"as_sql\"):\n                return value\n            value = self.encrypt(value)\n        return super().get_prep_value(value)\n\n    def from_db_value(self, value, expression, connection):\n        if value is not None:\n            value = self.decrypt(super().from_db_value(value, expression, connection))\n        return value\n\n\n@Field.register_lookup\nclass NotEqualLookup(Lookup):\n    # this is copied from https://docs.djangoproject.com/en/3.2/howto/custom-lookups/\n    lookup_name = \"ne\"\n\n    def as_sql(self, compiler, connection):\n        lhs, lhs_params = self.process_lhs(compiler, connection)\n        rhs, rhs_params = self.process_rhs(compiler, connection)\n        params = lhs_params + rhs_params\n        return \"%s <> %s\" % (lhs, rhs), params\n", "path": "pulpcore/app/models/fields.py"}]}