Repository: conan-io/conan

Issue:
tools.files.get doesn't work in export_sources().  [bug]
### Describe the bug

We're trying to use `tools.files.get` to download external source to bundle it with the recipe during export.  The `get` call fails when `tools.files.download` tries to read `conanfile.conf` which isn't populated.

```
Error in export_sources() method, line 41
        get(
        AttributeError: 'Bzip2Conan' object has no attribute 'conf'
```

### How to reproduce it

We removed the `source()` method from a conan-center recipe and appended the following to `export_sources()`.

```
        get(
            self,
            **self.conan_data["sources"][self.version],
            destination='src',
            strip_root=True)
        copy(self, 'src/*', self.recipe_folder, self.export_sources_folder)
```


Discussion:
Hi @IceJohnS 

Thanks for your report.

I'd say this is intended behavior, at least at the moment. The ``export_source()`` method is a method to copy files from the current repo to the recipe, but it was never intended to download things and do a copy of those in the recipe.

Maybe you are looking for the source-backup feature? If you haven't yet, please have a look to https://blog.conan.io/2023/10/03/backup-sources-feature.html. It can store the sources from the internet in your own server, to not rely on internet sources, which I'd say it is more or less your concern when you are trying to capture those sources as recipe "export-sources".

I'll have a look anyway to the ``conf`` issue in ``exports_sources``. I don't think this is solvable, at least to use profile ``conf`` information, because the ``export`` stage doesn't use the profiles at all (the ``conan export`` command doesn't get profile arguments at all, for example).
If we assume that only ``global.conf`` information is going to be available at those methods, I think it might be ok to give access. I am trying it in https://github.com/conan-io/conan/pull/16034 (intended for next Conan 2.3 release), it should be relatively low risk, and something similar is already being done for the ``source()`` method in the ``conan source`` command, which also acts on the "local" recipe and is independent of profile information.



Patch:
diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py
index 4ff4f40d721..93ae8bc26b6 100644
--- a/conans/client/cmd/export.py
+++ b/conans/client/cmd/export.py
@@ -25,6 +25,7 @@ def cmd_export(app, global_conf, conanfile_path, name, version, user, channel, g
     ref.validate_ref(allow_uppercase=global_conf.get("core:allow_uppercase_pkg_names",
                                                      check_type=bool))
 
+    conanfile.conf = global_conf.get_conanfile_conf(ref, is_consumer=True)
     conanfile.display_name = str(ref)
     conanfile.output.scope = conanfile.display_name
     scoped_output = conanfile.output


Test patch:
diff --git a/conans/test/functional/tools/test_files.py b/conans/test/functional/tools/test_files.py
index 2aef28f1844..c6f5331661f 100644
--- a/conans/test/functional/tools/test_files.py
+++ b/conans/test/functional/tools/test_files.py
@@ -86,6 +86,32 @@ def source(self):
         client.save({"profile": profile})
         client.run("create . -pr=profile")
 
+    def test_download_export_sources(self):
+        client = TestClient(light=True)
+        file_server = TestFileServer()
+        client.servers["file_server"] = file_server
+        save(os.path.join(file_server.store, "myfile.txt"), "some content")
+        save(os.path.join(file_server.store, "myfile2.txt"), "some content")
+
+        conanfile = textwrap.dedent(f"""
+            import os
+            from conan import ConanFile
+            from conan.tools.files import download
+
+            class Pkg(ConanFile):
+                name = "mypkg"
+                version = "1.0"
+                def export(self):
+                    download(self, "{file_server.fake_url}/myfile.txt", "myfile.txt")
+                    assert os.path.exists("myfile.txt")
+                def export_sources(self):
+                    download(self, "{file_server.fake_url}/myfile2.txt", "myfile2.txt")
+                    assert os.path.exists("myfile2.txt")
+            """)
+
+        client.save({"conanfile.py": conanfile})
+        client.run("create .")
+
 
 def test_patch(mock_patch_ng):
     conanfile = textwrap.dedent("""