prompt: 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()
  
  ```
  
golden_diff: 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
  
verification_info: {"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"}]}