We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[feature] show uploaded files size in `conan upload`
<!-- What is your suggestion? Please be as specific as possible! -->
It would be very convenient to provide the size of files which will be upload to specific remote via `conan upload ...` in diagnostic purposes, e.g. sometimes artifacts could be very large and CI could refuse its uploading, see e.g. [this StackOverflow question](https://stackoverflow.com/questions/64329087/gitlab-self-hosted-error-uploading-artifacts-as-archive-to-coordinator-to) as example of related CI error. With this change CI administrators could adjust the file limit at CI without trial and error changing of CI configs.

- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `conans/client/cmd/uploader.py`
Content:
```
1 import os
2 import shutil
3 import time
4 
5 from conan.internal.conan_app import ConanApp
6 from conan.api.output import ConanOutput
7 from conans.client.source import retrieve_exports_sources
8 from conans.errors import ConanException, NotFoundException
9 from conans.paths import (CONAN_MANIFEST, CONANFILE, EXPORT_SOURCES_TGZ_NAME,
10                           EXPORT_TGZ_NAME, PACKAGE_TGZ_NAME, CONANINFO)
11 from conans.util.files import (clean_dirty, is_dirty, gather_files,
12                                gzopen_without_timestamps, set_dirty_context_manager, mkdir)
13 
14 UPLOAD_POLICY_FORCE = "force-upload"
15 UPLOAD_POLICY_SKIP = "skip-upload"
16 
17 
18 class UploadUpstreamChecker:
19     """ decides if something needs to be uploaded or force-uploaded checking if that exact
20     revision already exists in the remote server, or if the --force parameter is forcing the upload
21     This is completely irrespective of the actual package contents, it only uses the local
22     computed revision and the remote one
23     """
24     def __init__(self, app: ConanApp):
25         self._app = app
26 
27     def check(self, upload_bundle, remote, force):
28         for ref, recipe_bundle in upload_bundle.refs().items():
29             self._check_upstream_recipe(ref, recipe_bundle, remote, force)
30             for pref, prev_bundle in upload_bundle.prefs(ref, recipe_bundle).items():
31                 self._check_upstream_package(pref, prev_bundle, remote, force)
32 
33     def _check_upstream_recipe(self, ref, ref_bundle, remote, force):
34         output = ConanOutput(scope=str(ref))
35         output.info("Checking which revisions exist in the remote server")
36         try:
37             assert ref.revision
38             # TODO: It is a bit ugly, interface-wise to ask for revisions to check existence
39             server_ref = self._app.remote_manager.get_recipe_revision_reference(ref, remote)
40             assert server_ref  # If successful (not raising NotFoundException), this will exist
41         except NotFoundException:
42             ref_bundle["force_upload"] = False
43             ref_bundle["upload"] = True
44         else:
45             if force:
46                 output.info(f"Recipe '{ref.repr_notime()}' already in server, forcing upload")
47                 ref_bundle["force_upload"] = True
48                 ref_bundle["upload"] = True
49             else:
50                 output.info(f"Recipe '{ref.repr_notime()}' already in server, skipping upload")
51                 ref_bundle["upload"] = False
52                 ref_bundle["force_upload"] = False
53 
54     def _check_upstream_package(self, pref, prev_bundle, remote, force):
55         assert (pref.revision is not None), "Cannot upload a package without PREV"
56         assert (pref.ref.revision is not None), "Cannot upload a package without RREV"
57 
58         try:
59             # TODO: It is a bit ugly, interface-wise to ask for revisions to check existence
60             server_revisions = self._app.remote_manager.get_package_revision_reference(pref, remote)
61             assert server_revisions
62         except NotFoundException:
63             prev_bundle["force_upload"] = False
64             prev_bundle["upload"] = True
65         else:
66             output = ConanOutput(scope=str(pref.ref))
67             if force:
68                 output.info(f"Package '{pref.repr_notime()}' already in server, forcing upload")
69                 prev_bundle["force_upload"] = True
70                 prev_bundle["upload"] = True
71             else:
72                 output.info(f"Package '{pref.repr_notime()}' already in server, skipping upload")
73                 prev_bundle["force_upload"] = False
74                 prev_bundle["upload"] = False
75 
76 
77 class PackagePreparator:
78     def __init__(self, app: ConanApp, global_conf):
79         self._app = app
80         self._global_conf = global_conf
81 
82     def prepare(self, upload_bundle, enabled_remotes):
83         for ref, bundle in upload_bundle.refs().items():
84             layout = self._app.cache.recipe_layout(ref)
85             conanfile_path = layout.conanfile()
86             conanfile = self._app.loader.load_basic(conanfile_path)
87 
88             if bundle.get("upload"):
89                 self._prepare_recipe(ref, bundle, conanfile, enabled_remotes)
90             for pref, prev_bundle in upload_bundle.prefs(ref, bundle).items():
91                 if prev_bundle.get("upload"):
92                     self._prepare_package(pref, prev_bundle)
93 
94     def _prepare_recipe(self, ref, ref_bundle, conanfile, remotes):
95         """ do a bunch of things that are necessary before actually executing the upload:
96         - retrieve exports_sources to complete the recipe if necessary
97         - compress the artifacts in conan_export.tgz and conan_export_sources.tgz
98         """
99         try:
100             recipe_layout = self._app.cache.recipe_layout(ref)
101             retrieve_exports_sources(self._app.remote_manager, recipe_layout, conanfile, ref,
102                                      remotes)
103             cache_files = self._compress_recipe_files(recipe_layout, ref)
104             ref_bundle["files"] = cache_files
105         except Exception as e:
106             raise ConanException(f"{ref} Error while compressing: {e}")
107 
108     def _compress_recipe_files(self, layout, ref):
109         download_export_folder = layout.download_export()
110 
111         output = ConanOutput(scope=str(ref))
112         for f in (EXPORT_TGZ_NAME, EXPORT_SOURCES_TGZ_NAME):
113             tgz_path = os.path.join(download_export_folder, f)
114             if is_dirty(tgz_path):
115                 output.warning("Removing %s, marked as dirty" % f)
116                 os.remove(tgz_path)
117                 clean_dirty(tgz_path)
118 
119         export_folder = layout.export()
120         files, symlinked_folders = gather_files(export_folder)
121         files.update(symlinked_folders)
122         if CONANFILE not in files or CONAN_MANIFEST not in files:
123             raise ConanException("Cannot upload corrupted recipe '%s'" % str(ref))
124         export_src_folder = layout.export_sources()
125         src_files, src_symlinked_folders = gather_files(export_src_folder)
126         src_files.update(src_symlinked_folders)
127 
128         # We do a copy of conanfile and conanmanifest to the download_export_folder
129         # so it is identical as when it is downloaded, and all files are from the same location
130         # to be uploaded
131         mkdir(download_export_folder)
132         shutil.copy2(os.path.join(export_folder, CONANFILE),
133                      os.path.join(download_export_folder, CONANFILE))
134         shutil.copy2(os.path.join(export_folder, CONAN_MANIFEST),
135                      os.path.join(download_export_folder, CONAN_MANIFEST))
136         result = {CONANFILE: os.path.join(download_export_folder, CONANFILE),
137                   CONAN_MANIFEST: os.path.join(download_export_folder, CONAN_MANIFEST)}
138         # Files NOT included in the tgz
139         files.pop(CONANFILE)
140         files.pop(CONAN_MANIFEST)
141 
142         def add_tgz(tgz_name, tgz_files):
143             tgz = os.path.join(download_export_folder, tgz_name)
144             if os.path.isfile(tgz):
145                 result[tgz_name] = tgz
146             elif tgz_files:
147                 compresslevel = self._global_conf.get("core.gzip:compresslevel", check_type=int)
148                 tgz = compress_files(tgz_files, tgz_name, download_export_folder,
149                                      compresslevel=compresslevel, ref=ref)
150                 result[tgz_name] = tgz
151 
152         add_tgz(EXPORT_TGZ_NAME, files)
153         add_tgz(EXPORT_SOURCES_TGZ_NAME, src_files)
154         return result
155 
156     def _prepare_package(self, pref, prev_bundle):
157         pkg_layout = self._app.cache.pkg_layout(pref)
158         if pkg_layout.package_is_dirty():
159             raise ConanException(f"Package {pref} is corrupted, aborting upload.\n"
160                                  f"Remove it with 'conan remove {pref}'")
161         cache_files = self._compress_package_files(pkg_layout, pref)
162         prev_bundle["files"] = cache_files
163 
164     def _compress_package_files(self, layout, pref):
165         output = ConanOutput(scope=str(pref))
166         download_pkg_folder = layout.download_package()
167         package_tgz = os.path.join(download_pkg_folder, PACKAGE_TGZ_NAME)
168         if is_dirty(package_tgz):
169             output.warning("Removing %s, marked as dirty" % PACKAGE_TGZ_NAME)
170             os.remove(package_tgz)
171             clean_dirty(package_tgz)
172 
173         # Get all the files in that directory
174         # existing package, will use short paths if defined
175         package_folder = layout.package()
176         files, symlinked_folders = gather_files(package_folder)
177         files.update(symlinked_folders)
178 
179         if CONANINFO not in files or CONAN_MANIFEST not in files:
180             raise ConanException("Cannot upload corrupted package '%s'" % str(pref))
181 
182         # Do a copy so the location of CONANINFO and MANIFEST is the "download" folder one
183         mkdir(download_pkg_folder)
184         shutil.copy2(os.path.join(package_folder, CONANINFO),
185                      os.path.join(download_pkg_folder, CONANINFO))
186         shutil.copy2(os.path.join(package_folder, CONAN_MANIFEST),
187                      os.path.join(download_pkg_folder, CONAN_MANIFEST))
188         # Files NOT included in the tgz
189         files.pop(CONANINFO)
190         files.pop(CONAN_MANIFEST)
191 
192         if not os.path.isfile(package_tgz):
193             tgz_files = {f: path for f, path in files.items()}
194             compresslevel = self._global_conf.get("core.gzip:compresslevel", check_type=int)
195             tgz_path = compress_files(tgz_files, PACKAGE_TGZ_NAME, download_pkg_folder,
196                                       compresslevel=compresslevel, ref=pref)
197             assert tgz_path == package_tgz
198             assert os.path.exists(package_tgz)
199 
200         return {PACKAGE_TGZ_NAME: package_tgz,
201                 CONANINFO: os.path.join(download_pkg_folder, CONANINFO),
202                 CONAN_MANIFEST: os.path.join(download_pkg_folder, CONAN_MANIFEST)}
203 
204 
205 class UploadExecutor:
206     """ does the actual file transfer to the remote. The files to be uploaded have already
207     been computed and are passed in the ``upload_data`` parameter, so this executor is also
208     agnostic about which files are transferred
209     """
210     def __init__(self, app: ConanApp):
211         self._app = app
212 
213     def upload(self, upload_data, remote):
214         for ref, bundle in upload_data.refs().items():
215             if bundle.get("upload"):
216                 self.upload_recipe(ref, bundle, remote)
217             for pref, prev_bundle in upload_data.prefs(ref, bundle).items():
218                 if prev_bundle.get("upload"):
219                     self.upload_package(pref, prev_bundle, remote)
220 
221     def upload_recipe(self, ref, bundle, remote):
222         output = ConanOutput(scope=str(ref))
223         output.info(f"Uploading recipe '{ref.repr_notime()}'")
224         t1 = time.time()
225         cache_files = bundle["files"]
226 
227         self._app.remote_manager.upload_recipe(ref, cache_files, remote)
228 
229         duration = time.time() - t1
230         output.debug(f"Upload {ref} in {duration} time")
231         return ref
232 
233     def upload_package(self, pref, prev_bundle, remote):
234         output = ConanOutput(scope=str(pref.ref))
235         output.info(f"Uploading package '{pref.repr_notime()}'")
236         cache_files = prev_bundle["files"]
237         assert (pref.revision is not None), "Cannot upload a package without PREV"
238         assert (pref.ref.revision is not None), "Cannot upload a package without RREV"
239 
240         t1 = time.time()
241         self._app.remote_manager.upload_package(pref, cache_files, remote)
242         duration = time.time() - t1
243         output.debug(f"Upload {pref} in {duration} time")
244 
245 
246 def compress_files(files, name, dest_dir, compresslevel=None, ref=None):
247     t1 = time.time()
248     # FIXME, better write to disk sequentially and not keep tgz contents in memory
249     tgz_path = os.path.join(dest_dir, name)
250     ConanOutput(scope=str(ref)).info(f"Compressing {name}")
251     with set_dirty_context_manager(tgz_path), open(tgz_path, "wb") as tgz_handle:
252         tgz = gzopen_without_timestamps(name, mode="w", fileobj=tgz_handle,
253                                         compresslevel=compresslevel)
254         for filename, abs_path in sorted(files.items()):
255             # recursive is False in case it is a symlink to a folder
256             tgz.add(abs_path, filename, recursive=False)
257         tgz.close()
258 
259     duration = time.time() - t1
260     ConanOutput().debug(f"{name} compressed in {duration} time")
261     return tgz_path
262
```
--- 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/conans/client/cmd/uploader.py b/conans/client/cmd/uploader.py
--- a/conans/client/cmd/uploader.py
+++ b/conans/client/cmd/uploader.py
@@ -9,7 +9,8 @@
 from conans.paths import (CONAN_MANIFEST, CONANFILE, EXPORT_SOURCES_TGZ_NAME,
                           EXPORT_TGZ_NAME, PACKAGE_TGZ_NAME, CONANINFO)
 from conans.util.files import (clean_dirty, is_dirty, gather_files,
-                               gzopen_without_timestamps, set_dirty_context_manager, mkdir)
+                               gzopen_without_timestamps, set_dirty_context_manager, mkdir,
+                               human_size)
 
 UPLOAD_POLICY_FORCE = "force-upload"
 UPLOAD_POLICY_SKIP = "skip-upload"
@@ -220,10 +221,11 @@
 
     def upload_recipe(self, ref, bundle, remote):
         output = ConanOutput(scope=str(ref))
-        output.info(f"Uploading recipe '{ref.repr_notime()}'")
-        t1 = time.time()
         cache_files = bundle["files"]
 
+        output.info(f"Uploading recipe '{ref.repr_notime()}' ({_total_size(cache_files)})")
+
+        t1 = time.time()
         self._app.remote_manager.upload_recipe(ref, cache_files, remote)
 
         duration = time.time() - t1
@@ -232,11 +234,12 @@
 
     def upload_package(self, pref, prev_bundle, remote):
         output = ConanOutput(scope=str(pref.ref))
-        output.info(f"Uploading package '{pref.repr_notime()}'")
         cache_files = prev_bundle["files"]
         assert (pref.revision is not None), "Cannot upload a package without PREV"
         assert (pref.ref.revision is not None), "Cannot upload a package without RREV"
 
+        output.info(f"Uploading package '{pref.repr_notime()}' ({_total_size(cache_files)})")
+
         t1 = time.time()
         self._app.remote_manager.upload_package(pref, cache_files, remote)
         duration = time.time() - t1
@@ -259,3 +262,11 @@
     duration = time.time() - t1
     ConanOutput().debug(f"{name} compressed in {duration} time")
     return tgz_path
+
+
+def _total_size(cache_files):
+    total_size = 0
+    for file in cache_files.values():
+        stat = os.stat(file)
+        total_size += stat.st_size
+    return human_size(total_size)

{"golden_diff": "diff --git a/conans/client/cmd/uploader.py b/conans/client/cmd/uploader.py\n--- a/conans/client/cmd/uploader.py\n+++ b/conans/client/cmd/uploader.py\n@@ -9,7 +9,8 @@\n from conans.paths import (CONAN_MANIFEST, CONANFILE, EXPORT_SOURCES_TGZ_NAME,\n                           EXPORT_TGZ_NAME, PACKAGE_TGZ_NAME, CONANINFO)\n from conans.util.files import (clean_dirty, is_dirty, gather_files,\n-                               gzopen_without_timestamps, set_dirty_context_manager, mkdir)\n+                               gzopen_without_timestamps, set_dirty_context_manager, mkdir,\n+                               human_size)\n \n UPLOAD_POLICY_FORCE = \"force-upload\"\n UPLOAD_POLICY_SKIP = \"skip-upload\"\n@@ -220,10 +221,11 @@\n \n     def upload_recipe(self, ref, bundle, remote):\n         output = ConanOutput(scope=str(ref))\n-        output.info(f\"Uploading recipe '{ref.repr_notime()}'\")\n-        t1 = time.time()\n         cache_files = bundle[\"files\"]\n \n+        output.info(f\"Uploading recipe '{ref.repr_notime()}' ({_total_size(cache_files)})\")\n+\n+        t1 = time.time()\n         self._app.remote_manager.upload_recipe(ref, cache_files, remote)\n \n         duration = time.time() - t1\n@@ -232,11 +234,12 @@\n \n     def upload_package(self, pref, prev_bundle, remote):\n         output = ConanOutput(scope=str(pref.ref))\n-        output.info(f\"Uploading package '{pref.repr_notime()}'\")\n         cache_files = prev_bundle[\"files\"]\n         assert (pref.revision is not None), \"Cannot upload a package without PREV\"\n         assert (pref.ref.revision is not None), \"Cannot upload a package without RREV\"\n \n+        output.info(f\"Uploading package '{pref.repr_notime()}' ({_total_size(cache_files)})\")\n+\n         t1 = time.time()\n         self._app.remote_manager.upload_package(pref, cache_files, remote)\n         duration = time.time() - t1\n@@ -259,3 +262,11 @@\n     duration = time.time() - t1\n     ConanOutput().debug(f\"{name} compressed in {duration} time\")\n     return tgz_path\n+\n+\n+def _total_size(cache_files):\n+    total_size = 0\n+    for file in cache_files.values():\n+        stat = os.stat(file)\n+        total_size += stat.st_size\n+    return human_size(total_size)\n", "issue": "[feature] show uploaded files size in `conan upload`\n<!-- What is your suggestion? Please be as specific as possible! -->\r\nIt would be very convenient to provide the size of files which will be upload to specific remote via `conan upload ...` in diagnostic purposes, e.g. sometimes artifacts could be very large and CI could refuse its uploading, see e.g. [this StackOverflow question](https://stackoverflow.com/questions/64329087/gitlab-self-hosted-error-uploading-artifacts-as-archive-to-coordinator-to) as example of related CI error. With this change CI administrators could adjust the file limit at CI without trial and error changing of CI configs.\r\n\r\n- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).\r\n\n", "before_files": [{"content": "import os\nimport shutil\nimport time\n\nfrom conan.internal.conan_app import ConanApp\nfrom conan.api.output import ConanOutput\nfrom conans.client.source import retrieve_exports_sources\nfrom conans.errors import ConanException, NotFoundException\nfrom conans.paths import (CONAN_MANIFEST, CONANFILE, EXPORT_SOURCES_TGZ_NAME,\n                          EXPORT_TGZ_NAME, PACKAGE_TGZ_NAME, CONANINFO)\nfrom conans.util.files import (clean_dirty, is_dirty, gather_files,\n                               gzopen_without_timestamps, set_dirty_context_manager, mkdir)\n\nUPLOAD_POLICY_FORCE = \"force-upload\"\nUPLOAD_POLICY_SKIP = \"skip-upload\"\n\n\nclass UploadUpstreamChecker:\n    \"\"\" decides if something needs to be uploaded or force-uploaded checking if that exact\n    revision already exists in the remote server, or if the --force parameter is forcing the upload\n    This is completely irrespective of the actual package contents, it only uses the local\n    computed revision and the remote one\n    \"\"\"\n    def __init__(self, app: ConanApp):\n        self._app = app\n\n    def check(self, upload_bundle, remote, force):\n        for ref, recipe_bundle in upload_bundle.refs().items():\n            self._check_upstream_recipe(ref, recipe_bundle, remote, force)\n            for pref, prev_bundle in upload_bundle.prefs(ref, recipe_bundle).items():\n                self._check_upstream_package(pref, prev_bundle, remote, force)\n\n    def _check_upstream_recipe(self, ref, ref_bundle, remote, force):\n        output = ConanOutput(scope=str(ref))\n        output.info(\"Checking which revisions exist in the remote server\")\n        try:\n            assert ref.revision\n            # TODO: It is a bit ugly, interface-wise to ask for revisions to check existence\n            server_ref = self._app.remote_manager.get_recipe_revision_reference(ref, remote)\n            assert server_ref  # If successful (not raising NotFoundException), this will exist\n        except NotFoundException:\n            ref_bundle[\"force_upload\"] = False\n            ref_bundle[\"upload\"] = True\n        else:\n            if force:\n                output.info(f\"Recipe '{ref.repr_notime()}' already in server, forcing upload\")\n                ref_bundle[\"force_upload\"] = True\n                ref_bundle[\"upload\"] = True\n            else:\n                output.info(f\"Recipe '{ref.repr_notime()}' already in server, skipping upload\")\n                ref_bundle[\"upload\"] = False\n                ref_bundle[\"force_upload\"] = False\n\n    def _check_upstream_package(self, pref, prev_bundle, remote, force):\n        assert (pref.revision is not None), \"Cannot upload a package without PREV\"\n        assert (pref.ref.revision is not None), \"Cannot upload a package without RREV\"\n\n        try:\n            # TODO: It is a bit ugly, interface-wise to ask for revisions to check existence\n            server_revisions = self._app.remote_manager.get_package_revision_reference(pref, remote)\n            assert server_revisions\n        except NotFoundException:\n            prev_bundle[\"force_upload\"] = False\n            prev_bundle[\"upload\"] = True\n        else:\n            output = ConanOutput(scope=str(pref.ref))\n            if force:\n                output.info(f\"Package '{pref.repr_notime()}' already in server, forcing upload\")\n                prev_bundle[\"force_upload\"] = True\n                prev_bundle[\"upload\"] = True\n            else:\n                output.info(f\"Package '{pref.repr_notime()}' already in server, skipping upload\")\n                prev_bundle[\"force_upload\"] = False\n                prev_bundle[\"upload\"] = False\n\n\nclass PackagePreparator:\n    def __init__(self, app: ConanApp, global_conf):\n        self._app = app\n        self._global_conf = global_conf\n\n    def prepare(self, upload_bundle, enabled_remotes):\n        for ref, bundle in upload_bundle.refs().items():\n            layout = self._app.cache.recipe_layout(ref)\n            conanfile_path = layout.conanfile()\n            conanfile = self._app.loader.load_basic(conanfile_path)\n\n            if bundle.get(\"upload\"):\n                self._prepare_recipe(ref, bundle, conanfile, enabled_remotes)\n            for pref, prev_bundle in upload_bundle.prefs(ref, bundle).items():\n                if prev_bundle.get(\"upload\"):\n                    self._prepare_package(pref, prev_bundle)\n\n    def _prepare_recipe(self, ref, ref_bundle, conanfile, remotes):\n        \"\"\" do a bunch of things that are necessary before actually executing the upload:\n        - retrieve exports_sources to complete the recipe if necessary\n        - compress the artifacts in conan_export.tgz and conan_export_sources.tgz\n        \"\"\"\n        try:\n            recipe_layout = self._app.cache.recipe_layout(ref)\n            retrieve_exports_sources(self._app.remote_manager, recipe_layout, conanfile, ref,\n                                     remotes)\n            cache_files = self._compress_recipe_files(recipe_layout, ref)\n            ref_bundle[\"files\"] = cache_files\n        except Exception as e:\n            raise ConanException(f\"{ref} Error while compressing: {e}\")\n\n    def _compress_recipe_files(self, layout, ref):\n        download_export_folder = layout.download_export()\n\n        output = ConanOutput(scope=str(ref))\n        for f in (EXPORT_TGZ_NAME, EXPORT_SOURCES_TGZ_NAME):\n            tgz_path = os.path.join(download_export_folder, f)\n            if is_dirty(tgz_path):\n                output.warning(\"Removing %s, marked as dirty\" % f)\n                os.remove(tgz_path)\n                clean_dirty(tgz_path)\n\n        export_folder = layout.export()\n        files, symlinked_folders = gather_files(export_folder)\n        files.update(symlinked_folders)\n        if CONANFILE not in files or CONAN_MANIFEST not in files:\n            raise ConanException(\"Cannot upload corrupted recipe '%s'\" % str(ref))\n        export_src_folder = layout.export_sources()\n        src_files, src_symlinked_folders = gather_files(export_src_folder)\n        src_files.update(src_symlinked_folders)\n\n        # We do a copy of conanfile and conanmanifest to the download_export_folder\n        # so it is identical as when it is downloaded, and all files are from the same location\n        # to be uploaded\n        mkdir(download_export_folder)\n        shutil.copy2(os.path.join(export_folder, CONANFILE),\n                     os.path.join(download_export_folder, CONANFILE))\n        shutil.copy2(os.path.join(export_folder, CONAN_MANIFEST),\n                     os.path.join(download_export_folder, CONAN_MANIFEST))\n        result = {CONANFILE: os.path.join(download_export_folder, CONANFILE),\n                  CONAN_MANIFEST: os.path.join(download_export_folder, CONAN_MANIFEST)}\n        # Files NOT included in the tgz\n        files.pop(CONANFILE)\n        files.pop(CONAN_MANIFEST)\n\n        def add_tgz(tgz_name, tgz_files):\n            tgz = os.path.join(download_export_folder, tgz_name)\n            if os.path.isfile(tgz):\n                result[tgz_name] = tgz\n            elif tgz_files:\n                compresslevel = self._global_conf.get(\"core.gzip:compresslevel\", check_type=int)\n                tgz = compress_files(tgz_files, tgz_name, download_export_folder,\n                                     compresslevel=compresslevel, ref=ref)\n                result[tgz_name] = tgz\n\n        add_tgz(EXPORT_TGZ_NAME, files)\n        add_tgz(EXPORT_SOURCES_TGZ_NAME, src_files)\n        return result\n\n    def _prepare_package(self, pref, prev_bundle):\n        pkg_layout = self._app.cache.pkg_layout(pref)\n        if pkg_layout.package_is_dirty():\n            raise ConanException(f\"Package {pref} is corrupted, aborting upload.\\n\"\n                                 f\"Remove it with 'conan remove {pref}'\")\n        cache_files = self._compress_package_files(pkg_layout, pref)\n        prev_bundle[\"files\"] = cache_files\n\n    def _compress_package_files(self, layout, pref):\n        output = ConanOutput(scope=str(pref))\n        download_pkg_folder = layout.download_package()\n        package_tgz = os.path.join(download_pkg_folder, PACKAGE_TGZ_NAME)\n        if is_dirty(package_tgz):\n            output.warning(\"Removing %s, marked as dirty\" % PACKAGE_TGZ_NAME)\n            os.remove(package_tgz)\n            clean_dirty(package_tgz)\n\n        # Get all the files in that directory\n        # existing package, will use short paths if defined\n        package_folder = layout.package()\n        files, symlinked_folders = gather_files(package_folder)\n        files.update(symlinked_folders)\n\n        if CONANINFO not in files or CONAN_MANIFEST not in files:\n            raise ConanException(\"Cannot upload corrupted package '%s'\" % str(pref))\n\n        # Do a copy so the location of CONANINFO and MANIFEST is the \"download\" folder one\n        mkdir(download_pkg_folder)\n        shutil.copy2(os.path.join(package_folder, CONANINFO),\n                     os.path.join(download_pkg_folder, CONANINFO))\n        shutil.copy2(os.path.join(package_folder, CONAN_MANIFEST),\n                     os.path.join(download_pkg_folder, CONAN_MANIFEST))\n        # Files NOT included in the tgz\n        files.pop(CONANINFO)\n        files.pop(CONAN_MANIFEST)\n\n        if not os.path.isfile(package_tgz):\n            tgz_files = {f: path for f, path in files.items()}\n            compresslevel = self._global_conf.get(\"core.gzip:compresslevel\", check_type=int)\n            tgz_path = compress_files(tgz_files, PACKAGE_TGZ_NAME, download_pkg_folder,\n                                      compresslevel=compresslevel, ref=pref)\n            assert tgz_path == package_tgz\n            assert os.path.exists(package_tgz)\n\n        return {PACKAGE_TGZ_NAME: package_tgz,\n                CONANINFO: os.path.join(download_pkg_folder, CONANINFO),\n                CONAN_MANIFEST: os.path.join(download_pkg_folder, CONAN_MANIFEST)}\n\n\nclass UploadExecutor:\n    \"\"\" does the actual file transfer to the remote. The files to be uploaded have already\n    been computed and are passed in the ``upload_data`` parameter, so this executor is also\n    agnostic about which files are transferred\n    \"\"\"\n    def __init__(self, app: ConanApp):\n        self._app = app\n\n    def upload(self, upload_data, remote):\n        for ref, bundle in upload_data.refs().items():\n            if bundle.get(\"upload\"):\n                self.upload_recipe(ref, bundle, remote)\n            for pref, prev_bundle in upload_data.prefs(ref, bundle).items():\n                if prev_bundle.get(\"upload\"):\n                    self.upload_package(pref, prev_bundle, remote)\n\n    def upload_recipe(self, ref, bundle, remote):\n        output = ConanOutput(scope=str(ref))\n        output.info(f\"Uploading recipe '{ref.repr_notime()}'\")\n        t1 = time.time()\n        cache_files = bundle[\"files\"]\n\n        self._app.remote_manager.upload_recipe(ref, cache_files, remote)\n\n        duration = time.time() - t1\n        output.debug(f\"Upload {ref} in {duration} time\")\n        return ref\n\n    def upload_package(self, pref, prev_bundle, remote):\n        output = ConanOutput(scope=str(pref.ref))\n        output.info(f\"Uploading package '{pref.repr_notime()}'\")\n        cache_files = prev_bundle[\"files\"]\n        assert (pref.revision is not None), \"Cannot upload a package without PREV\"\n        assert (pref.ref.revision is not None), \"Cannot upload a package without RREV\"\n\n        t1 = time.time()\n        self._app.remote_manager.upload_package(pref, cache_files, remote)\n        duration = time.time() - t1\n        output.debug(f\"Upload {pref} in {duration} time\")\n\n\ndef compress_files(files, name, dest_dir, compresslevel=None, ref=None):\n    t1 = time.time()\n    # FIXME, better write to disk sequentially and not keep tgz contents in memory\n    tgz_path = os.path.join(dest_dir, name)\n    ConanOutput(scope=str(ref)).info(f\"Compressing {name}\")\n    with set_dirty_context_manager(tgz_path), open(tgz_path, \"wb\") as tgz_handle:\n        tgz = gzopen_without_timestamps(name, mode=\"w\", fileobj=tgz_handle,\n                                        compresslevel=compresslevel)\n        for filename, abs_path in sorted(files.items()):\n            # recursive is False in case it is a symlink to a folder\n            tgz.add(abs_path, filename, recursive=False)\n        tgz.close()\n\n    duration = time.time() - t1\n    ConanOutput().debug(f\"{name} compressed in {duration} time\")\n    return tgz_path\n", "path": "conans/client/cmd/uploader.py"}], "after_files": [{"content": "import os\nimport shutil\nimport time\n\nfrom conan.internal.conan_app import ConanApp\nfrom conan.api.output import ConanOutput\nfrom conans.client.source import retrieve_exports_sources\nfrom conans.errors import ConanException, NotFoundException\nfrom conans.paths import (CONAN_MANIFEST, CONANFILE, EXPORT_SOURCES_TGZ_NAME,\n                          EXPORT_TGZ_NAME, PACKAGE_TGZ_NAME, CONANINFO)\nfrom conans.util.files import (clean_dirty, is_dirty, gather_files,\n                               gzopen_without_timestamps, set_dirty_context_manager, mkdir,\n                               human_size)\n\nUPLOAD_POLICY_FORCE = \"force-upload\"\nUPLOAD_POLICY_SKIP = \"skip-upload\"\n\n\nclass UploadUpstreamChecker:\n    \"\"\" decides if something needs to be uploaded or force-uploaded checking if that exact\n    revision already exists in the remote server, or if the --force parameter is forcing the upload\n    This is completely irrespective of the actual package contents, it only uses the local\n    computed revision and the remote one\n    \"\"\"\n    def __init__(self, app: ConanApp):\n        self._app = app\n\n    def check(self, upload_bundle, remote, force):\n        for ref, recipe_bundle in upload_bundle.refs().items():\n            self._check_upstream_recipe(ref, recipe_bundle, remote, force)\n            for pref, prev_bundle in upload_bundle.prefs(ref, recipe_bundle).items():\n                self._check_upstream_package(pref, prev_bundle, remote, force)\n\n    def _check_upstream_recipe(self, ref, ref_bundle, remote, force):\n        output = ConanOutput(scope=str(ref))\n        output.info(\"Checking which revisions exist in the remote server\")\n        try:\n            assert ref.revision\n            # TODO: It is a bit ugly, interface-wise to ask for revisions to check existence\n            server_ref = self._app.remote_manager.get_recipe_revision_reference(ref, remote)\n            assert server_ref  # If successful (not raising NotFoundException), this will exist\n        except NotFoundException:\n            ref_bundle[\"force_upload\"] = False\n            ref_bundle[\"upload\"] = True\n        else:\n            if force:\n                output.info(f\"Recipe '{ref.repr_notime()}' already in server, forcing upload\")\n                ref_bundle[\"force_upload\"] = True\n                ref_bundle[\"upload\"] = True\n            else:\n                output.info(f\"Recipe '{ref.repr_notime()}' already in server, skipping upload\")\n                ref_bundle[\"upload\"] = False\n                ref_bundle[\"force_upload\"] = False\n\n    def _check_upstream_package(self, pref, prev_bundle, remote, force):\n        assert (pref.revision is not None), \"Cannot upload a package without PREV\"\n        assert (pref.ref.revision is not None), \"Cannot upload a package without RREV\"\n\n        try:\n            # TODO: It is a bit ugly, interface-wise to ask for revisions to check existence\n            server_revisions = self._app.remote_manager.get_package_revision_reference(pref, remote)\n            assert server_revisions\n        except NotFoundException:\n            prev_bundle[\"force_upload\"] = False\n            prev_bundle[\"upload\"] = True\n        else:\n            output = ConanOutput(scope=str(pref.ref))\n            if force:\n                output.info(f\"Package '{pref.repr_notime()}' already in server, forcing upload\")\n                prev_bundle[\"force_upload\"] = True\n                prev_bundle[\"upload\"] = True\n            else:\n                output.info(f\"Package '{pref.repr_notime()}' already in server, skipping upload\")\n                prev_bundle[\"force_upload\"] = False\n                prev_bundle[\"upload\"] = False\n\n\nclass PackagePreparator:\n    def __init__(self, app: ConanApp, global_conf):\n        self._app = app\n        self._global_conf = global_conf\n\n    def prepare(self, upload_bundle, enabled_remotes):\n        for ref, bundle in upload_bundle.refs().items():\n            layout = self._app.cache.recipe_layout(ref)\n            conanfile_path = layout.conanfile()\n            conanfile = self._app.loader.load_basic(conanfile_path)\n\n            if bundle.get(\"upload\"):\n                self._prepare_recipe(ref, bundle, conanfile, enabled_remotes)\n            for pref, prev_bundle in upload_bundle.prefs(ref, bundle).items():\n                if prev_bundle.get(\"upload\"):\n                    self._prepare_package(pref, prev_bundle)\n\n    def _prepare_recipe(self, ref, ref_bundle, conanfile, remotes):\n        \"\"\" do a bunch of things that are necessary before actually executing the upload:\n        - retrieve exports_sources to complete the recipe if necessary\n        - compress the artifacts in conan_export.tgz and conan_export_sources.tgz\n        \"\"\"\n        try:\n            recipe_layout = self._app.cache.recipe_layout(ref)\n            retrieve_exports_sources(self._app.remote_manager, recipe_layout, conanfile, ref,\n                                     remotes)\n            cache_files = self._compress_recipe_files(recipe_layout, ref)\n            ref_bundle[\"files\"] = cache_files\n        except Exception as e:\n            raise ConanException(f\"{ref} Error while compressing: {e}\")\n\n    def _compress_recipe_files(self, layout, ref):\n        download_export_folder = layout.download_export()\n\n        output = ConanOutput(scope=str(ref))\n        for f in (EXPORT_TGZ_NAME, EXPORT_SOURCES_TGZ_NAME):\n            tgz_path = os.path.join(download_export_folder, f)\n            if is_dirty(tgz_path):\n                output.warning(\"Removing %s, marked as dirty\" % f)\n                os.remove(tgz_path)\n                clean_dirty(tgz_path)\n\n        export_folder = layout.export()\n        files, symlinked_folders = gather_files(export_folder)\n        files.update(symlinked_folders)\n        if CONANFILE not in files or CONAN_MANIFEST not in files:\n            raise ConanException(\"Cannot upload corrupted recipe '%s'\" % str(ref))\n        export_src_folder = layout.export_sources()\n        src_files, src_symlinked_folders = gather_files(export_src_folder)\n        src_files.update(src_symlinked_folders)\n\n        # We do a copy of conanfile and conanmanifest to the download_export_folder\n        # so it is identical as when it is downloaded, and all files are from the same location\n        # to be uploaded\n        mkdir(download_export_folder)\n        shutil.copy2(os.path.join(export_folder, CONANFILE),\n                     os.path.join(download_export_folder, CONANFILE))\n        shutil.copy2(os.path.join(export_folder, CONAN_MANIFEST),\n                     os.path.join(download_export_folder, CONAN_MANIFEST))\n        result = {CONANFILE: os.path.join(download_export_folder, CONANFILE),\n                  CONAN_MANIFEST: os.path.join(download_export_folder, CONAN_MANIFEST)}\n        # Files NOT included in the tgz\n        files.pop(CONANFILE)\n        files.pop(CONAN_MANIFEST)\n\n        def add_tgz(tgz_name, tgz_files):\n            tgz = os.path.join(download_export_folder, tgz_name)\n            if os.path.isfile(tgz):\n                result[tgz_name] = tgz\n            elif tgz_files:\n                compresslevel = self._global_conf.get(\"core.gzip:compresslevel\", check_type=int)\n                tgz = compress_files(tgz_files, tgz_name, download_export_folder,\n                                     compresslevel=compresslevel, ref=ref)\n                result[tgz_name] = tgz\n\n        add_tgz(EXPORT_TGZ_NAME, files)\n        add_tgz(EXPORT_SOURCES_TGZ_NAME, src_files)\n        return result\n\n    def _prepare_package(self, pref, prev_bundle):\n        pkg_layout = self._app.cache.pkg_layout(pref)\n        if pkg_layout.package_is_dirty():\n            raise ConanException(f\"Package {pref} is corrupted, aborting upload.\\n\"\n                                 f\"Remove it with 'conan remove {pref}'\")\n        cache_files = self._compress_package_files(pkg_layout, pref)\n        prev_bundle[\"files\"] = cache_files\n\n    def _compress_package_files(self, layout, pref):\n        output = ConanOutput(scope=str(pref))\n        download_pkg_folder = layout.download_package()\n        package_tgz = os.path.join(download_pkg_folder, PACKAGE_TGZ_NAME)\n        if is_dirty(package_tgz):\n            output.warning(\"Removing %s, marked as dirty\" % PACKAGE_TGZ_NAME)\n            os.remove(package_tgz)\n            clean_dirty(package_tgz)\n\n        # Get all the files in that directory\n        # existing package, will use short paths if defined\n        package_folder = layout.package()\n        files, symlinked_folders = gather_files(package_folder)\n        files.update(symlinked_folders)\n\n        if CONANINFO not in files or CONAN_MANIFEST not in files:\n            raise ConanException(\"Cannot upload corrupted package '%s'\" % str(pref))\n\n        # Do a copy so the location of CONANINFO and MANIFEST is the \"download\" folder one\n        mkdir(download_pkg_folder)\n        shutil.copy2(os.path.join(package_folder, CONANINFO),\n                     os.path.join(download_pkg_folder, CONANINFO))\n        shutil.copy2(os.path.join(package_folder, CONAN_MANIFEST),\n                     os.path.join(download_pkg_folder, CONAN_MANIFEST))\n        # Files NOT included in the tgz\n        files.pop(CONANINFO)\n        files.pop(CONAN_MANIFEST)\n\n        if not os.path.isfile(package_tgz):\n            tgz_files = {f: path for f, path in files.items()}\n            compresslevel = self._global_conf.get(\"core.gzip:compresslevel\", check_type=int)\n            tgz_path = compress_files(tgz_files, PACKAGE_TGZ_NAME, download_pkg_folder,\n                                      compresslevel=compresslevel, ref=pref)\n            assert tgz_path == package_tgz\n            assert os.path.exists(package_tgz)\n\n        return {PACKAGE_TGZ_NAME: package_tgz,\n                CONANINFO: os.path.join(download_pkg_folder, CONANINFO),\n                CONAN_MANIFEST: os.path.join(download_pkg_folder, CONAN_MANIFEST)}\n\n\nclass UploadExecutor:\n    \"\"\" does the actual file transfer to the remote. The files to be uploaded have already\n    been computed and are passed in the ``upload_data`` parameter, so this executor is also\n    agnostic about which files are transferred\n    \"\"\"\n    def __init__(self, app: ConanApp):\n        self._app = app\n\n    def upload(self, upload_data, remote):\n        for ref, bundle in upload_data.refs().items():\n            if bundle.get(\"upload\"):\n                self.upload_recipe(ref, bundle, remote)\n            for pref, prev_bundle in upload_data.prefs(ref, bundle).items():\n                if prev_bundle.get(\"upload\"):\n                    self.upload_package(pref, prev_bundle, remote)\n\n    def upload_recipe(self, ref, bundle, remote):\n        output = ConanOutput(scope=str(ref))\n        cache_files = bundle[\"files\"]\n\n        output.info(f\"Uploading recipe '{ref.repr_notime()}' ({_total_size(cache_files)})\")\n\n        t1 = time.time()\n        self._app.remote_manager.upload_recipe(ref, cache_files, remote)\n\n        duration = time.time() - t1\n        output.debug(f\"Upload {ref} in {duration} time\")\n        return ref\n\n    def upload_package(self, pref, prev_bundle, remote):\n        output = ConanOutput(scope=str(pref.ref))\n        cache_files = prev_bundle[\"files\"]\n        assert (pref.revision is not None), \"Cannot upload a package without PREV\"\n        assert (pref.ref.revision is not None), \"Cannot upload a package without RREV\"\n\n        output.info(f\"Uploading package '{pref.repr_notime()}' ({_total_size(cache_files)})\")\n\n        t1 = time.time()\n        self._app.remote_manager.upload_package(pref, cache_files, remote)\n        duration = time.time() - t1\n        output.debug(f\"Upload {pref} in {duration} time\")\n\n\ndef compress_files(files, name, dest_dir, compresslevel=None, ref=None):\n    t1 = time.time()\n    # FIXME, better write to disk sequentially and not keep tgz contents in memory\n    tgz_path = os.path.join(dest_dir, name)\n    ConanOutput(scope=str(ref)).info(f\"Compressing {name}\")\n    with set_dirty_context_manager(tgz_path), open(tgz_path, \"wb\") as tgz_handle:\n        tgz = gzopen_without_timestamps(name, mode=\"w\", fileobj=tgz_handle,\n                                        compresslevel=compresslevel)\n        for filename, abs_path in sorted(files.items()):\n            # recursive is False in case it is a symlink to a folder\n            tgz.add(abs_path, filename, recursive=False)\n        tgz.close()\n\n    duration = time.time() - t1\n    ConanOutput().debug(f\"{name} compressed in {duration} time\")\n    return tgz_path\n\n\ndef _total_size(cache_files):\n    total_size = 0\n    for file in cache_files.values():\n        stat = os.stat(file)\n        total_size += stat.st_size\n    return human_size(total_size)\n", "path": "conans/client/cmd/uploader.py"}]}