We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Task cleanup must not delete content nor artifacts
Deleting content or artifacts outside of orphan cleanup is breaking the rules.
And no, we cannot get away with that.


--- 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/tasking/util.py`
Content:
```
1 import logging
2 from gettext import gettext as _
3 
4 from django.db import transaction
5 from django.db import connection
6 
7 from pulpcore.app.models import Task
8 from pulpcore.constants import TASK_FINAL_STATES, TASK_INCOMPLETE_STATES, TASK_STATES
9 
10 _logger = logging.getLogger(__name__)
11 
12 
13 def cancel(task_id):
14     """
15     Cancel the task that is represented by the given task_id.
16 
17     This method cancels only the task with given task_id, not the spawned tasks. This also updates
18     task's state to either 'canceled' or 'canceling'.
19 
20     Args:
21         task_id (str): The ID of the task you wish to cancel
22 
23     Raises:
24         rest_framework.exceptions.NotFound: If a task with given task_id does not exist
25     """
26     task_status = Task.objects.get(pk=task_id)
27 
28     if task_status.state in TASK_FINAL_STATES:
29         # If the task is already done, just stop
30         _logger.debug(
31             "Task [{task_id}] already in a final state: {state}".format(
32                 task_id=task_id, state=task_status.state
33             )
34         )
35         return task_status
36 
37     _logger.info(_("Canceling task: {id}").format(id=task_id))
38 
39     task = task_status
40     # This is the only valid transition without holding the task lock
41     rows = Task.objects.filter(pk=task.pk, state__in=TASK_INCOMPLETE_STATES).update(
42         state=TASK_STATES.CANCELING
43     )
44     # Notify the worker that might be running that task and other workers to clean up
45     with connection.cursor() as cursor:
46         cursor.execute("SELECT pg_notify('pulp_worker_cancel', %s)", (str(task.pk),))
47         cursor.execute("NOTIFY pulp_worker_wakeup")
48     if rows == 1:
49         task.refresh_from_db()
50     return task
51 
52 
53 def _delete_incomplete_resources(task):
54     """
55     Delete all incomplete created-resources on a canceled task.
56 
57     Args:
58         task (Task): A task.
59     """
60     if task.state not in [TASK_STATES.CANCELED, TASK_STATES.CANCELING]:
61         raise RuntimeError(_("Task must be canceled."))
62     for model in (r.content_object for r in task.created_resources.all()):
63         try:
64             if model.complete:
65                 continue
66         except AttributeError:
67             continue
68         try:
69             with transaction.atomic():
70                 model.delete()
71         except Exception as error:
72             _logger.error(_("Delete created resource, failed: {}").format(str(error)))
73
```
--- 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/tasking/util.py b/pulpcore/tasking/util.py
--- a/pulpcore/tasking/util.py
+++ b/pulpcore/tasking/util.py
@@ -4,7 +4,7 @@
 from django.db import transaction
 from django.db import connection
 
-from pulpcore.app.models import Task
+from pulpcore.app.models import Artifact, Content, Task
 from pulpcore.constants import TASK_FINAL_STATES, TASK_INCOMPLETE_STATES, TASK_STATES
 
 _logger = logging.getLogger(__name__)
@@ -60,6 +60,8 @@
     if task.state not in [TASK_STATES.CANCELED, TASK_STATES.CANCELING]:
         raise RuntimeError(_("Task must be canceled."))
     for model in (r.content_object for r in task.created_resources.all()):
+        if isinstance(model, (Artifact, Content)):
+            continue
         try:
             if model.complete:
                 continue

{"golden_diff": "diff --git a/pulpcore/tasking/util.py b/pulpcore/tasking/util.py\n--- a/pulpcore/tasking/util.py\n+++ b/pulpcore/tasking/util.py\n@@ -4,7 +4,7 @@\n from django.db import transaction\n from django.db import connection\n \n-from pulpcore.app.models import Task\n+from pulpcore.app.models import Artifact, Content, Task\n from pulpcore.constants import TASK_FINAL_STATES, TASK_INCOMPLETE_STATES, TASK_STATES\n \n _logger = logging.getLogger(__name__)\n@@ -60,6 +60,8 @@\n     if task.state not in [TASK_STATES.CANCELED, TASK_STATES.CANCELING]:\n         raise RuntimeError(_(\"Task must be canceled.\"))\n     for model in (r.content_object for r in task.created_resources.all()):\n+        if isinstance(model, (Artifact, Content)):\n+            continue\n         try:\n             if model.complete:\n                 continue\n", "issue": "Task cleanup must not delete content nor artifacts\nDeleting content or artifacts outside of orphan cleanup is breaking the rules.\r\nAnd no, we cannot get away with that.\r\n\n", "before_files": [{"content": "import logging\nfrom gettext import gettext as _\n\nfrom django.db import transaction\nfrom django.db import connection\n\nfrom pulpcore.app.models import Task\nfrom pulpcore.constants import TASK_FINAL_STATES, TASK_INCOMPLETE_STATES, TASK_STATES\n\n_logger = logging.getLogger(__name__)\n\n\ndef cancel(task_id):\n    \"\"\"\n    Cancel the task that is represented by the given task_id.\n\n    This method cancels only the task with given task_id, not the spawned tasks. This also updates\n    task's state to either 'canceled' or 'canceling'.\n\n    Args:\n        task_id (str): The ID of the task you wish to cancel\n\n    Raises:\n        rest_framework.exceptions.NotFound: If a task with given task_id does not exist\n    \"\"\"\n    task_status = Task.objects.get(pk=task_id)\n\n    if task_status.state in TASK_FINAL_STATES:\n        # If the task is already done, just stop\n        _logger.debug(\n            \"Task [{task_id}] already in a final state: {state}\".format(\n                task_id=task_id, state=task_status.state\n            )\n        )\n        return task_status\n\n    _logger.info(_(\"Canceling task: {id}\").format(id=task_id))\n\n    task = task_status\n    # This is the only valid transition without holding the task lock\n    rows = Task.objects.filter(pk=task.pk, state__in=TASK_INCOMPLETE_STATES).update(\n        state=TASK_STATES.CANCELING\n    )\n    # Notify the worker that might be running that task and other workers to clean up\n    with connection.cursor() as cursor:\n        cursor.execute(\"SELECT pg_notify('pulp_worker_cancel', %s)\", (str(task.pk),))\n        cursor.execute(\"NOTIFY pulp_worker_wakeup\")\n    if rows == 1:\n        task.refresh_from_db()\n    return task\n\n\ndef _delete_incomplete_resources(task):\n    \"\"\"\n    Delete all incomplete created-resources on a canceled task.\n\n    Args:\n        task (Task): A task.\n    \"\"\"\n    if task.state not in [TASK_STATES.CANCELED, TASK_STATES.CANCELING]:\n        raise RuntimeError(_(\"Task must be canceled.\"))\n    for model in (r.content_object for r in task.created_resources.all()):\n        try:\n            if model.complete:\n                continue\n        except AttributeError:\n            continue\n        try:\n            with transaction.atomic():\n                model.delete()\n        except Exception as error:\n            _logger.error(_(\"Delete created resource, failed: {}\").format(str(error)))\n", "path": "pulpcore/tasking/util.py"}], "after_files": [{"content": "import logging\nfrom gettext import gettext as _\n\nfrom django.db import transaction\nfrom django.db import connection\n\nfrom pulpcore.app.models import Artifact, Content, Task\nfrom pulpcore.constants import TASK_FINAL_STATES, TASK_INCOMPLETE_STATES, TASK_STATES\n\n_logger = logging.getLogger(__name__)\n\n\ndef cancel(task_id):\n    \"\"\"\n    Cancel the task that is represented by the given task_id.\n\n    This method cancels only the task with given task_id, not the spawned tasks. This also updates\n    task's state to either 'canceled' or 'canceling'.\n\n    Args:\n        task_id (str): The ID of the task you wish to cancel\n\n    Raises:\n        rest_framework.exceptions.NotFound: If a task with given task_id does not exist\n    \"\"\"\n    task_status = Task.objects.get(pk=task_id)\n\n    if task_status.state in TASK_FINAL_STATES:\n        # If the task is already done, just stop\n        _logger.debug(\n            \"Task [{task_id}] already in a final state: {state}\".format(\n                task_id=task_id, state=task_status.state\n            )\n        )\n        return task_status\n\n    _logger.info(_(\"Canceling task: {id}\").format(id=task_id))\n\n    task = task_status\n    # This is the only valid transition without holding the task lock\n    rows = Task.objects.filter(pk=task.pk, state__in=TASK_INCOMPLETE_STATES).update(\n        state=TASK_STATES.CANCELING\n    )\n    # Notify the worker that might be running that task and other workers to clean up\n    with connection.cursor() as cursor:\n        cursor.execute(\"SELECT pg_notify('pulp_worker_cancel', %s)\", (str(task.pk),))\n        cursor.execute(\"NOTIFY pulp_worker_wakeup\")\n    if rows == 1:\n        task.refresh_from_db()\n    return task\n\n\ndef _delete_incomplete_resources(task):\n    \"\"\"\n    Delete all incomplete created-resources on a canceled task.\n\n    Args:\n        task (Task): A task.\n    \"\"\"\n    if task.state not in [TASK_STATES.CANCELED, TASK_STATES.CANCELING]:\n        raise RuntimeError(_(\"Task must be canceled.\"))\n    for model in (r.content_object for r in task.created_resources.all()):\n        if isinstance(model, (Artifact, Content)):\n            continue\n        try:\n            if model.complete:\n                continue\n        except AttributeError:\n            continue\n        try:\n            with transaction.atomic():\n                model.delete()\n        except Exception as error:\n            _logger.error(_(\"Delete created resource, failed: {}\").format(str(error)))\n", "path": "pulpcore/tasking/util.py"}]}