prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  [bug] Can't call `conan upload --recipe-only` twice with backup sources enabled
  ### Steps to reproduce
  
  1. Enable backup sources
  2. Export a recipe that downloads file
  3. Call conan upload only recipe for ref
  4. Do it again, it fails due to KeyError
  
  
  Found while prepping for https://github.com/conan-io/conan-center-index/pull/18082
  
  </issue>
  <code>
  [start of conans/client/downloaders/download_cache.py]
  1 import json
  2 import os
  3 from contextlib import contextmanager
  4 from threading import Lock
  5 
  6 from conans.util.dates import timestamp_now
  7 from conans.util.files import load, save
  8 from conans.util.locks import SimpleLock
  9 from conans.util.sha import sha256 as compute_sha256
  10 
  11 
  12 class DownloadCache:
  13     """ The download cache has 3 folders
  14     - "s": SOURCE_BACKUP for the files.download(internet_url) backup sources feature
  15     - "c": CONAN_CACHE: for caching Conan packages artifacts
  16     - "locks": The LOCKS folder containing the file locks for concurrent access to the cache
  17     """
  18     _LOCKS = "locks"
  19     _SOURCE_BACKUP = "s"
  20     _CONAN_CACHE = "c"
  21 
  22     def __init__(self, path: str):
  23         self._path: str = path
  24 
  25     def source_path(self, sha256):
  26         return os.path.join(self._path, self._SOURCE_BACKUP, sha256)
  27 
  28     def cached_path(self, url):
  29         h = compute_sha256(url.encode())
  30         return os.path.join(self._path, self._CONAN_CACHE, h), h
  31 
  32     _thread_locks = {}  # Needs to be shared among all instances
  33 
  34     @contextmanager
  35     def lock(self, lock_id):
  36         lock = os.path.join(self._path, self._LOCKS, lock_id)
  37         with SimpleLock(lock):
  38             # Once the process has access, make sure multithread is locked too
  39             # as SimpleLock doesn't work multithread
  40             thread_lock = self._thread_locks.setdefault(lock, Lock())
  41             thread_lock.acquire()
  42             try:
  43                 yield
  44             finally:
  45                 thread_lock.release()
  46 
  47     def get_backup_sources_files_to_upload(self, package_list, excluded_urls):
  48         """ from a package_list of packages to upload, collect from the backup-sources cache
  49         the matching references to upload those backups too
  50         """
  51         def should_upload_sources(package):
  52             return any(prev["upload"] for prev in package["revisions"].values())
  53 
  54         files_to_upload = []
  55         path_backups = os.path.join(self._path, self._SOURCE_BACKUP)
  56 
  57         if not os.path.exists(path_backups):
  58             return []
  59 
  60         if excluded_urls is None:
  61             excluded_urls = []
  62 
  63         all_refs = {str(k) for k, ref in package_list.refs()
  64                     if ref.get("upload") or any(should_upload_sources(p)
  65                                                 for p in ref["packages"].values())}
  66         for f in os.listdir(path_backups):
  67             if f.endswith(".json"):
  68                 f = os.path.join(path_backups, f)
  69                 content = json.loads(load(f))
  70                 refs = content["references"]
  71                 # unknown entries are not uploaded at this moment, the flow is not expected.
  72                 for ref, urls in refs.items():
  73                     is_excluded = all(any(url.startswith(excluded_url)
  74                                           for excluded_url in excluded_urls)
  75                                       for url in urls)
  76                     if not is_excluded and ref in all_refs:
  77                         files_to_upload.append(f)
  78                         files_to_upload.append(f[:-5])
  79                         break
  80         return files_to_upload
  81 
  82     @staticmethod
  83     def update_backup_sources_json(cached_path, conanfile, urls):
  84         """ create or update the sha256.json file with the references and new urls used
  85         """
  86         summary_path = cached_path + ".json"
  87         if os.path.exists(summary_path):
  88             summary = json.loads(load(summary_path))
  89         else:
  90             summary = {"references": {}, "timestamp": timestamp_now()}
  91 
  92         try:
  93             summary_key = str(conanfile.ref)
  94         except AttributeError:
  95             # The recipe path would be different between machines
  96             # So best we can do is to set this as unknown
  97             summary_key = "unknown"
  98 
  99         if not isinstance(urls, (list, tuple)):
  100             urls = [urls]
  101         existing_urls = summary["references"].setdefault(summary_key, [])
  102         existing_urls.extend(url for url in urls if url not in existing_urls)
  103         conanfile.output.verbose(f"Updating ${summary_path} summary file")
  104         summary_dump = json.dumps(summary)
  105         conanfile.output.debug(f"New summary: ${summary_dump}")
  106         save(summary_path, json.dumps(summary))
  107
  [end of conans/client/downloaders/download_cache.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/conans/client/downloaders/download_cache.py b/conans/client/downloaders/download_cache.py
  --- a/conans/client/downloaders/download_cache.py
  +++ b/conans/client/downloaders/download_cache.py
  @@ -60,9 +60,12 @@
           if excluded_urls is None:
               excluded_urls = []
   
  -        all_refs = {str(k) for k, ref in package_list.refs()
  -                    if ref.get("upload") or any(should_upload_sources(p)
  -                                                for p in ref["packages"].values())}
  +        all_refs = set()
  +        for k, ref in package_list.refs():
  +            packages = ref.get("packages", {}).values()
  +            if ref.get("upload") or any(should_upload_sources(p) for p in packages):
  +                all_refs.add(str(k))
  +
           for f in os.listdir(path_backups):
               if f.endswith(".json"):
                   f = os.path.join(path_backups, f)
  
verification_info: {"golden_diff": "diff --git a/conans/client/downloaders/download_cache.py b/conans/client/downloaders/download_cache.py\n--- a/conans/client/downloaders/download_cache.py\n+++ b/conans/client/downloaders/download_cache.py\n@@ -60,9 +60,12 @@\n         if excluded_urls is None:\n             excluded_urls = []\n \n-        all_refs = {str(k) for k, ref in package_list.refs()\n-                    if ref.get(\"upload\") or any(should_upload_sources(p)\n-                                                for p in ref[\"packages\"].values())}\n+        all_refs = set()\n+        for k, ref in package_list.refs():\n+            packages = ref.get(\"packages\", {}).values()\n+            if ref.get(\"upload\") or any(should_upload_sources(p) for p in packages):\n+                all_refs.add(str(k))\n+\n         for f in os.listdir(path_backups):\n             if f.endswith(\".json\"):\n                 f = os.path.join(path_backups, f)\n", "issue": "[bug] Can't call `conan upload --recipe-only` twice with backup sources enabled\n### Steps to reproduce\r\n\r\n1. Enable backup sources\r\n2. Export a recipe that downloads file\r\n3. Call conan upload only recipe for ref\r\n4. Do it again, it fails due to KeyError\r\n\r\n\r\nFound while prepping for https://github.com/conan-io/conan-center-index/pull/18082\n", "before_files": [{"content": "import json\nimport os\nfrom contextlib import contextmanager\nfrom threading import Lock\n\nfrom conans.util.dates import timestamp_now\nfrom conans.util.files import load, save\nfrom conans.util.locks import SimpleLock\nfrom conans.util.sha import sha256 as compute_sha256\n\n\nclass DownloadCache:\n    \"\"\" The download cache has 3 folders\n    - \"s\": SOURCE_BACKUP for the files.download(internet_url) backup sources feature\n    - \"c\": CONAN_CACHE: for caching Conan packages artifacts\n    - \"locks\": The LOCKS folder containing the file locks for concurrent access to the cache\n    \"\"\"\n    _LOCKS = \"locks\"\n    _SOURCE_BACKUP = \"s\"\n    _CONAN_CACHE = \"c\"\n\n    def __init__(self, path: str):\n        self._path: str = path\n\n    def source_path(self, sha256):\n        return os.path.join(self._path, self._SOURCE_BACKUP, sha256)\n\n    def cached_path(self, url):\n        h = compute_sha256(url.encode())\n        return os.path.join(self._path, self._CONAN_CACHE, h), h\n\n    _thread_locks = {}  # Needs to be shared among all instances\n\n    @contextmanager\n    def lock(self, lock_id):\n        lock = os.path.join(self._path, self._LOCKS, lock_id)\n        with SimpleLock(lock):\n            # Once the process has access, make sure multithread is locked too\n            # as SimpleLock doesn't work multithread\n            thread_lock = self._thread_locks.setdefault(lock, Lock())\n            thread_lock.acquire()\n            try:\n                yield\n            finally:\n                thread_lock.release()\n\n    def get_backup_sources_files_to_upload(self, package_list, excluded_urls):\n        \"\"\" from a package_list of packages to upload, collect from the backup-sources cache\n        the matching references to upload those backups too\n        \"\"\"\n        def should_upload_sources(package):\n            return any(prev[\"upload\"] for prev in package[\"revisions\"].values())\n\n        files_to_upload = []\n        path_backups = os.path.join(self._path, self._SOURCE_BACKUP)\n\n        if not os.path.exists(path_backups):\n            return []\n\n        if excluded_urls is None:\n            excluded_urls = []\n\n        all_refs = {str(k) for k, ref in package_list.refs()\n                    if ref.get(\"upload\") or any(should_upload_sources(p)\n                                                for p in ref[\"packages\"].values())}\n        for f in os.listdir(path_backups):\n            if f.endswith(\".json\"):\n                f = os.path.join(path_backups, f)\n                content = json.loads(load(f))\n                refs = content[\"references\"]\n                # unknown entries are not uploaded at this moment, the flow is not expected.\n                for ref, urls in refs.items():\n                    is_excluded = all(any(url.startswith(excluded_url)\n                                          for excluded_url in excluded_urls)\n                                      for url in urls)\n                    if not is_excluded and ref in all_refs:\n                        files_to_upload.append(f)\n                        files_to_upload.append(f[:-5])\n                        break\n        return files_to_upload\n\n    @staticmethod\n    def update_backup_sources_json(cached_path, conanfile, urls):\n        \"\"\" create or update the sha256.json file with the references and new urls used\n        \"\"\"\n        summary_path = cached_path + \".json\"\n        if os.path.exists(summary_path):\n            summary = json.loads(load(summary_path))\n        else:\n            summary = {\"references\": {}, \"timestamp\": timestamp_now()}\n\n        try:\n            summary_key = str(conanfile.ref)\n        except AttributeError:\n            # The recipe path would be different between machines\n            # So best we can do is to set this as unknown\n            summary_key = \"unknown\"\n\n        if not isinstance(urls, (list, tuple)):\n            urls = [urls]\n        existing_urls = summary[\"references\"].setdefault(summary_key, [])\n        existing_urls.extend(url for url in urls if url not in existing_urls)\n        conanfile.output.verbose(f\"Updating ${summary_path} summary file\")\n        summary_dump = json.dumps(summary)\n        conanfile.output.debug(f\"New summary: ${summary_dump}\")\n        save(summary_path, json.dumps(summary))\n", "path": "conans/client/downloaders/download_cache.py"}]}