prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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.
  
  
  </issue>
  <code>
  [start of pulpcore/tasking/util.py]
  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 of pulpcore/tasking/util.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
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"}]}