From 282ac40db3213c88f791e889e6b62c1027096020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Thu, 19 Dec 2024 23:34:23 +0100 Subject: [PATCH 01/20] Clean command sketch --- conan/api/subapi/config.py | 5 +++++ conan/cli/commands/config.py | 14 ++++++++++++++ conans/client/migrations.py | 12 +++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index e1e5d9d3a93..d517b2b5abb 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -188,3 +188,8 @@ def appending_recursive_dict_update(d, u): appending_recursive_dict_update(settings, settings_user) return Settings(settings) + + def clean(self): + from conans.client.migrations import ClientMigrator + migrator = ClientMigrator(self.conan_api.cache_folder, conan_version) + migrator.clean() diff --git a/conan/cli/commands/config.py b/conan/cli/commands/config.py index 9aeaa2e5f69..0512a4db09b 100644 --- a/conan/cli/commands/config.py +++ b/conan/cli/commands/config.py @@ -1,3 +1,4 @@ +from conan.api.input import UserInput from conan.api.model import Remote from conan.api.output import cli_out_write from conan.cli.command import conan_command, conan_subcommand, OnceArgument @@ -132,3 +133,16 @@ def config_show(conan_api, parser, subparser, *args): args = parser.parse_args(*args) return conan_api.config.show(args.pattern) + + +@conan_subcommand() +def config_clean(conan_api, parser, subparser, *args): + """ + Clean the configuration files in the Conan home folder. (Keeping installed packages) + """ + subparser.add_argument("-c", "--confirm", action='store_true', + help="Do not request confirmation") + args = parser.parse_args(*args) + ui = UserInput(conan_api.config.get("core:non_interactive")) + if args.confirm or ui.request_boolean("Clear all configuration files?"): + conan_api.config.clean() diff --git a/conans/client/migrations.py b/conans/client/migrations.py index 82997ba4e9e..1af79e1bf42 100644 --- a/conans/client/migrations.py +++ b/conans/client/migrations.py @@ -7,7 +7,7 @@ from conan.internal.default_settings import migrate_settings_file from conans.migrations import Migrator from conans.util.dates import timestamp_now -from conans.util.files import load, save +from conans.util.files import load, save, rmdir CONAN_GENERATED_COMMENT = "This file was generated by Conan" @@ -61,6 +61,16 @@ def _apply_migrations(self, old_version): if old_version is None or old_version < "2.4": _migrate_default_compatibility(self.cache_folder) + def clean(self): + # TODO: Check what we're deleting + contents = os.listdir(self.cache_folder) + for contents in contents: + # keep packages + if contents not in ("p", "version.txt"): + rmdir(os.path.join(self.cache_folder, contents)) + # CHECK: This also generates a remotes.json that is not there after a conan profile show? + self._apply_migrations(None) + def _migrate_default_compatibility(cache_folder): # just the back migration From 22dab92dbcaf1e75e5c1aed271259ebc3ccc367e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 24 Dec 2024 09:48:10 +0100 Subject: [PATCH 02/20] Make clean/Migrate part of the ConfigAPI --- conan/api/conan_api.py | 10 ++++------ conan/api/subapi/config.py | 19 +++++++++++++++---- conans/client/migrations.py | 10 ---------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index 0b280e5ed58..b11a3c71b18 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -6,7 +6,6 @@ from conan.api.subapi.local import LocalAPI from conan.api.subapi.lockfile import LockfileAPI from conan.api.subapi.workspace import WorkspaceAPI -from conan import conan_version from conan.api.subapi.config import ConfigAPI from conan.api.subapi.download import DownloadAPI from conan.api.subapi.export import ExportAPI @@ -19,7 +18,6 @@ from conan.api.subapi.remove import RemoveAPI from conan.api.subapi.search import SearchAPI from conan.api.subapi.upload import UploadAPI -from conans.client.migrations import ClientMigrator from conan.errors import ConanException from conan.internal.paths import get_conan_user_home from conans.model.version_range import validate_conan_version @@ -37,10 +35,11 @@ def __init__(self, cache_folder=None): self.cache_folder = self.workspace.home_folder() or cache_folder or get_conan_user_home() self.home_folder = self.cache_folder # Lets call it home, deprecate "cache" - # Migration system - migrator = ClientMigrator(self.cache_folder, conan_version) - migrator.migrate() + self.reinit() + def reinit(self): + self.config = ConfigAPI(self) + self.config.migrate() self.command = CommandAPI(self) self.remotes = RemotesAPI(self) # Search recipes by wildcard and packages filtering by configuracion @@ -52,7 +51,6 @@ def __init__(self, cache_folder=None): self.graph = GraphAPI(self) self.export = ExportAPI(self) self.remove = RemoveAPI(self) - self.config = ConfigAPI(self) self.new = NewAPI(self) self.upload = UploadAPI(self) self.download = DownloadAPI(self) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index d517b2b5abb..275d797079b 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -20,7 +20,7 @@ from conans.model.pkg_type import PackageType from conans.model.recipe_ref import RecipeReference from conans.model.settings import Settings -from conans.util.files import load, save +from conans.util.files import load, save, rmdir class ConfigAPI: @@ -189,7 +189,18 @@ def appending_recursive_dict_update(d, u): return Settings(settings) - def clean(self): + def migrate(self): + # Migration system from conans.client.migrations import ClientMigrator - migrator = ClientMigrator(self.conan_api.cache_folder, conan_version) - migrator.clean() + migrator = ClientMigrator(self.home(), conan_version) + migrator.migrate() + + def clean(self): + # TODO: Check what we're deleting + contents = os.listdir(self.home()) + for contents in contents: + # keep packages + if contents not in ("p", "version.txt"): + rmdir(os.path.join(self.home(), contents)) + # CHECK: This also generates a remotes.json that is not there after a conan profile show? + self.migrate() diff --git a/conans/client/migrations.py b/conans/client/migrations.py index 1af79e1bf42..9d1a9170918 100644 --- a/conans/client/migrations.py +++ b/conans/client/migrations.py @@ -61,16 +61,6 @@ def _apply_migrations(self, old_version): if old_version is None or old_version < "2.4": _migrate_default_compatibility(self.cache_folder) - def clean(self): - # TODO: Check what we're deleting - contents = os.listdir(self.cache_folder) - for contents in contents: - # keep packages - if contents not in ("p", "version.txt"): - rmdir(os.path.join(self.cache_folder, contents)) - # CHECK: This also generates a remotes.json that is not there after a conan profile show? - self._apply_migrations(None) - def _migrate_default_compatibility(cache_folder): # just the back migration From ef6a93069eab5b2cc8e8c4f836482ac271e7653d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 24 Dec 2024 12:29:45 +0100 Subject: [PATCH 03/20] Test --- conan/api/subapi/config.py | 13 +++++++++---- conans/client/migrations.py | 2 +- test/integration/command/config_test.py | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index 275d797079b..8679ab0f69a 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -20,7 +20,7 @@ from conans.model.pkg_type import PackageType from conans.model.recipe_ref import RecipeReference from conans.model.settings import Settings -from conans.util.files import load, save, rmdir +from conans.util.files import load, save, rmdir, remove class ConfigAPI: @@ -198,9 +198,14 @@ def migrate(self): def clean(self): # TODO: Check what we're deleting contents = os.listdir(self.home()) - for contents in contents: + for content in contents: # keep packages - if contents not in ("p", "version.txt"): - rmdir(os.path.join(self.home(), contents)) + if content not in ("p", "version.txt"): + content_path = os.path.join(self.home(), content) + ConanOutput().debug(f"Removing {content_path}") + if os.path.isdir(content_path): + rmdir(content_path) + else: + remove(content_path) # CHECK: This also generates a remotes.json that is not there after a conan profile show? self.migrate() diff --git a/conans/client/migrations.py b/conans/client/migrations.py index 9d1a9170918..82997ba4e9e 100644 --- a/conans/client/migrations.py +++ b/conans/client/migrations.py @@ -7,7 +7,7 @@ from conan.internal.default_settings import migrate_settings_file from conans.migrations import Migrator from conans.util.dates import timestamp_now -from conans.util.files import load, save, rmdir +from conans.util.files import load, save CONAN_GENERATED_COMMENT = "This file was generated by Conan" diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py index a85a51e2b84..4e5404d36fd 100644 --- a/test/integration/command/config_test.py +++ b/test/integration/command/config_test.py @@ -201,3 +201,22 @@ def test_config_show(): tc.run("config show zlib/*:foo") assert "zlib/*:user.mycategory:foo" in tc.out assert "zlib/*:user.myothercategory:foo" in tc.out + + +def test_config_clean(): + tc = TestClient(light=True) + tc.run("profile detect --name=foo") + tc.run("remote add bar http://fakeurl") + tc.save_home({"global.conf": "core.upload:retry=7\n", + "extensions/compatibility/mycomp.py": "", + "extensions/commands/cmd_foo.py": "", + }) + tc.run("config clean -c") + tc.run("profile list") + assert "foo" not in tc.out + tc.run("remote list") + assert "bar" not in tc.out + tc.run("config show core.upload:retry") + assert "7" not in tc.out + assert not os.path.exists(os.path.join(tc.cache_folder, "extensions")) + From 12455d748fa27fd078f1cd9ce2369f56fbccd506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 24 Dec 2024 12:30:16 +0100 Subject: [PATCH 04/20] Comment --- conan/api/subapi/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index 8679ab0f69a..eeac3ad3ef4 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -191,6 +191,7 @@ def appending_recursive_dict_update(d, u): def migrate(self): # Migration system + # TODO: Still needs refactoring - using it here for now for tests from conans.client.migrations import ClientMigrator migrator = ClientMigrator(self.home(), conan_version) migrator.migrate() From 56523e278a44b994caba56ff73f3ec29efb09562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Thu, 9 Jan 2025 15:48:08 +0100 Subject: [PATCH 05/20] Basic --- conan/api/conan_api.py | 36 ++++++++++++++++++++++++++++++------ conan/api/subapi/cache.py | 3 +++ conan/api/subapi/command.py | 3 +++ conan/api/subapi/config.py | 3 +++ conan/api/subapi/download.py | 3 +++ conan/api/subapi/export.py | 3 +++ conan/api/subapi/graph.py | 3 +++ conan/api/subapi/install.py | 3 +++ conan/api/subapi/list.py | 3 +++ conan/api/subapi/local.py | 4 ++++ conan/api/subapi/lockfile.py | 3 +++ conan/api/subapi/new.py | 3 +++ conan/api/subapi/profiles.py | 3 +++ conan/api/subapi/remotes.py | 3 +++ conan/api/subapi/remove.py | 3 +++ conan/api/subapi/search.py | 3 +++ conan/api/subapi/upload.py | 3 +++ 17 files changed, 79 insertions(+), 6 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index b11a3c71b18..a1759aa1efd 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -35,11 +35,11 @@ def __init__(self, cache_folder=None): self.cache_folder = self.workspace.home_folder() or cache_folder or get_conan_user_home() self.home_folder = self.cache_folder # Lets call it home, deprecate "cache" - self.reinit() - - def reinit(self): + # This API is depended upon by the subsequent ones, it should be initialized first self.config = ConfigAPI(self) self.config.migrate() + + self.remotes = RemotesAPI(self) self.command = CommandAPI(self) self.remotes = RemotesAPI(self) # Search recipes by wildcard and packages filtering by configuracion @@ -58,6 +58,30 @@ def reinit(self): self.lockfile = LockfileAPI(self) self.local = LocalAPI(self) - required_range_new = self.config.global_conf.get("core:required_conan_version") - if required_range_new: - validate_conan_version(required_range_new) + _check_conan_version(self) + + def reinit(self): + self.config.reinit() + self.remotes.reinit() + # self.command.reinit() + # self.search.reinit() + # self.list.reinit() + # self.profiles.reinit() + # self.install.reinit() + # self.graph.reinit() + # self.export.reinit() + # self.remove.reinit() + # self.new.reinit() + # self.upload.reinit() + # self.download.reinit() + # self.cache.reinit() + # self.lockfile.reinit() + self.local.reinit() + + _check_conan_version(self) + + +def _check_conan_version(conan_api): + required_range_new = conan_api.config.global_conf.get("core:required_conan_version") + if required_range_new: + validate_conan_version(required_range_new) diff --git a/conan/api/subapi/cache.py b/conan/api/subapi/cache.py index b3cf0b2159c..4dd8fecf889 100644 --- a/conan/api/subapi/cache.py +++ b/conan/api/subapi/cache.py @@ -242,6 +242,9 @@ def get_backup_sources(self, package_list=None, exclude=True, only_upload=True): download_cache = DownloadCache(download_cache_path) return download_cache.get_backup_sources_files(excluded_urls, package_list, only_upload) + def reinit(self): + pass + def _resolve_latest_ref(cache, ref): if ref.revision is None or ref.revision == "latest": diff --git a/conan/api/subapi/command.py b/conan/api/subapi/command.py index dc2048d9f2f..bb538547a3c 100644 --- a/conan/api/subapi/command.py +++ b/conan/api/subapi/command.py @@ -8,6 +8,9 @@ def __init__(self, conan_api): self.conan_api = conan_api self.cli = None + def reinit(self): + pass + def run(self, cmd): if isinstance(cmd, str): cmd = cmd.split() diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index eeac3ad3ef4..19d61d8c8d3 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -210,3 +210,6 @@ def clean(self): remove(content_path) # CHECK: This also generates a remotes.json that is not there after a conan profile show? self.migrate() + + def reinit(self): + self._new_config = None diff --git a/conan/api/subapi/download.py b/conan/api/subapi/download.py index 18c2bee9eaa..e5dc7c5a965 100644 --- a/conan/api/subapi/download.py +++ b/conan/api/subapi/download.py @@ -102,3 +102,6 @@ def _download_pkglist(pkglist): elapsed = time.time() - t ConanOutput().success(f"Download completed in {int(elapsed)}s\n") + + def reinit(self): + pass diff --git a/conan/api/subapi/export.py b/conan/api/subapi/export.py index bfba33d9127..cdbd5ac11e2 100644 --- a/conan/api/subapi/export.py +++ b/conan/api/subapi/export.py @@ -63,3 +63,6 @@ def export_pkg(self, deps_graph, source_folder, output_folder): conanfile.folders.set_base_package(final_folder) out.info(f"Package folder {final_folder}") out.success("Exported package binary") + + def reinit(self): + pass diff --git a/conan/api/subapi/graph.py b/conan/api/subapi/graph.py index b6ff4f844de..bb0f8679d4e 100644 --- a/conan/api/subapi/graph.py +++ b/conan/api/subapi/graph.py @@ -200,3 +200,6 @@ def analyze_binaries(self, graph, build_mode=None, remotes=None, update=None, lo binaries_analyzer = GraphBinariesAnalyzer(conan_app, self.conan_api.config.global_conf) binaries_analyzer.evaluate_graph(graph, build_mode, lockfile, remotes, update, build_modes_test, tested_graph) + + def reinit(self): + pass diff --git a/conan/api/subapi/install.py b/conan/api/subapi/install.py index 5177992d40a..f768b1f837c 100644 --- a/conan/api/subapi/install.py +++ b/conan/api/subapi/install.py @@ -89,3 +89,6 @@ def install_consumer(self, deps_graph, generators=None, source_folder=None, outp hook_manager = HookManager(HomePaths(self.conan_api.home_folder).hooks_path) write_generators(conanfile, hook_manager, self.conan_api.home_folder, envs_generation=envs_generation) + + def reinit(self): + pass diff --git a/conan/api/subapi/list.py b/conan/api/subapi/list.py index 9ede07a1868..331c2034fb3 100644 --- a/conan/api/subapi/list.py +++ b/conan/api/subapi/list.py @@ -263,6 +263,9 @@ def explain_missing_binaries(self, ref, conaninfo, remotes): rev_dict["packages"][pref.package_id]["remote"] = remote return pkglist + def reinit(self): + pass + class _BinaryDistance: def __init__(self, pref, binary, expected, remote=None): diff --git a/conan/api/subapi/local.py b/conan/api/subapi/local.py index 24c47d34e12..b346828dc37 100644 --- a/conan/api/subapi/local.py +++ b/conan/api/subapi/local.py @@ -118,3 +118,7 @@ def inspect(self, conanfile_path, remotes, lockfile, name=None, version=None, us conanfile = app.loader.load_named(conanfile_path, name=name, version=version, user=user, channel=channel, remotes=remotes, graph_lock=lockfile) return conanfile + + def reinit(self): + # TODO: Check if this is risky + self.editable_packages = EditablePackages(self._conan_api.home_folder) diff --git a/conan/api/subapi/lockfile.py b/conan/api/subapi/lockfile.py index 76a80717976..744204639d8 100644 --- a/conan/api/subapi/lockfile.py +++ b/conan/api/subapi/lockfile.py @@ -103,3 +103,6 @@ def save_lockfile(lockfile, lockfile_out, path=None): lockfile_out = make_abs_path(lockfile_out, path) lockfile.save(lockfile_out) ConanOutput().info(f"Generated lockfile: {lockfile_out}") + + def reinit(self): + pass diff --git a/conan/api/subapi/new.py b/conan/api/subapi/new.py index 8b3089a0c19..d7a9ba46c77 100644 --- a/conan/api/subapi/new.py +++ b/conan/api/subapi/new.py @@ -152,3 +152,6 @@ def as_name(ref): "Required definitions are: {}" .format(", ".join("'{}'".format(var) for var in template_vars))) return result + + def reinit(self): + pass diff --git a/conan/api/subapi/profiles.py b/conan/api/subapi/profiles.py index 1caf38c14c2..9c2427abca3 100644 --- a/conan/api/subapi/profiles.py +++ b/conan/api/subapi/profiles.py @@ -167,3 +167,6 @@ def _load_profile_plugin(self): mod, _ = load_python_file(profile_plugin) if hasattr(mod, "profile_plugin"): return mod.profile_plugin + + def reinit(self): + pass diff --git a/conan/api/subapi/remotes.py b/conan/api/subapi/remotes.py index 0d2318e4330..d1a57caf695 100644 --- a/conan/api/subapi/remotes.py +++ b/conan/api/subapi/remotes.py @@ -32,6 +32,9 @@ def __init__(self, conan_api): self._home_folder = conan_api.home_folder self._remotes_file = HomePaths(self._home_folder).remotes_path + def reinit(self): + pass + def list(self, pattern=None, only_enabled=True): """ Obtain a list of ``Remote`` objects matching the pattern. diff --git a/conan/api/subapi/remove.py b/conan/api/subapi/remove.py index 24af4f466b7..f5ec4629bd3 100644 --- a/conan/api/subapi/remove.py +++ b/conan/api/subapi/remove.py @@ -49,3 +49,6 @@ def package(self, pref: PkgReference, remote: Remote): else: package_layout = app.cache.pkg_layout(pref) app.cache.remove_package_layout(package_layout) + + def reinit(self): + pass diff --git a/conan/api/subapi/search.py b/conan/api/subapi/search.py index 997aa8d8bf6..01201af4bce 100644 --- a/conan/api/subapi/search.py +++ b/conan/api/subapi/search.py @@ -22,3 +22,6 @@ def recipes(self, query: str, remote=None): if not only_none_user_channel or (r.user is None and r.channel is None): ret.append(r) return sorted(ret) + + def reinit(self): + pass diff --git a/conan/api/subapi/upload.py b/conan/api/subapi/upload.py index 0522096e697..ca0821bee42 100644 --- a/conan/api/subapi/upload.py +++ b/conan/api/subapi/upload.py @@ -141,3 +141,6 @@ def upload_backup_sources(self, files): output.success("Upload backup sources complete\n") return files + + def reinit(self): + pass From 2d0523eb3e54777b4ac9d69695f6c126db48b287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 10 Jan 2025 09:13:31 +0100 Subject: [PATCH 06/20] Only reinit for valid apis --- conan/api/conan_api.py | 13 ------------- conan/api/subapi/cache.py | 2 -- conan/api/subapi/command.py | 3 --- conan/api/subapi/download.py | 3 --- conan/api/subapi/export.py | 3 --- conan/api/subapi/graph.py | 3 --- conan/api/subapi/install.py | 3 --- conan/api/subapi/list.py | 3 --- conan/api/subapi/lockfile.py | 3 --- conan/api/subapi/new.py | 3 --- conan/api/subapi/profiles.py | 3 --- conan/api/subapi/remove.py | 3 --- conan/api/subapi/search.py | 3 --- conan/api/subapi/upload.py | 3 --- 14 files changed, 51 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index a1759aa1efd..b0b4f73316d 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -63,19 +63,6 @@ def __init__(self, cache_folder=None): def reinit(self): self.config.reinit() self.remotes.reinit() - # self.command.reinit() - # self.search.reinit() - # self.list.reinit() - # self.profiles.reinit() - # self.install.reinit() - # self.graph.reinit() - # self.export.reinit() - # self.remove.reinit() - # self.new.reinit() - # self.upload.reinit() - # self.download.reinit() - # self.cache.reinit() - # self.lockfile.reinit() self.local.reinit() _check_conan_version(self) diff --git a/conan/api/subapi/cache.py b/conan/api/subapi/cache.py index 4dd8fecf889..8c2dd089784 100644 --- a/conan/api/subapi/cache.py +++ b/conan/api/subapi/cache.py @@ -242,8 +242,6 @@ def get_backup_sources(self, package_list=None, exclude=True, only_upload=True): download_cache = DownloadCache(download_cache_path) return download_cache.get_backup_sources_files(excluded_urls, package_list, only_upload) - def reinit(self): - pass def _resolve_latest_ref(cache, ref): diff --git a/conan/api/subapi/command.py b/conan/api/subapi/command.py index bb538547a3c..dc2048d9f2f 100644 --- a/conan/api/subapi/command.py +++ b/conan/api/subapi/command.py @@ -8,9 +8,6 @@ def __init__(self, conan_api): self.conan_api = conan_api self.cli = None - def reinit(self): - pass - def run(self, cmd): if isinstance(cmd, str): cmd = cmd.split() diff --git a/conan/api/subapi/download.py b/conan/api/subapi/download.py index e5dc7c5a965..18c2bee9eaa 100644 --- a/conan/api/subapi/download.py +++ b/conan/api/subapi/download.py @@ -102,6 +102,3 @@ def _download_pkglist(pkglist): elapsed = time.time() - t ConanOutput().success(f"Download completed in {int(elapsed)}s\n") - - def reinit(self): - pass diff --git a/conan/api/subapi/export.py b/conan/api/subapi/export.py index cdbd5ac11e2..bfba33d9127 100644 --- a/conan/api/subapi/export.py +++ b/conan/api/subapi/export.py @@ -63,6 +63,3 @@ def export_pkg(self, deps_graph, source_folder, output_folder): conanfile.folders.set_base_package(final_folder) out.info(f"Package folder {final_folder}") out.success("Exported package binary") - - def reinit(self): - pass diff --git a/conan/api/subapi/graph.py b/conan/api/subapi/graph.py index bb0f8679d4e..b6ff4f844de 100644 --- a/conan/api/subapi/graph.py +++ b/conan/api/subapi/graph.py @@ -200,6 +200,3 @@ def analyze_binaries(self, graph, build_mode=None, remotes=None, update=None, lo binaries_analyzer = GraphBinariesAnalyzer(conan_app, self.conan_api.config.global_conf) binaries_analyzer.evaluate_graph(graph, build_mode, lockfile, remotes, update, build_modes_test, tested_graph) - - def reinit(self): - pass diff --git a/conan/api/subapi/install.py b/conan/api/subapi/install.py index f768b1f837c..5177992d40a 100644 --- a/conan/api/subapi/install.py +++ b/conan/api/subapi/install.py @@ -89,6 +89,3 @@ def install_consumer(self, deps_graph, generators=None, source_folder=None, outp hook_manager = HookManager(HomePaths(self.conan_api.home_folder).hooks_path) write_generators(conanfile, hook_manager, self.conan_api.home_folder, envs_generation=envs_generation) - - def reinit(self): - pass diff --git a/conan/api/subapi/list.py b/conan/api/subapi/list.py index 331c2034fb3..9ede07a1868 100644 --- a/conan/api/subapi/list.py +++ b/conan/api/subapi/list.py @@ -263,9 +263,6 @@ def explain_missing_binaries(self, ref, conaninfo, remotes): rev_dict["packages"][pref.package_id]["remote"] = remote return pkglist - def reinit(self): - pass - class _BinaryDistance: def __init__(self, pref, binary, expected, remote=None): diff --git a/conan/api/subapi/lockfile.py b/conan/api/subapi/lockfile.py index 744204639d8..76a80717976 100644 --- a/conan/api/subapi/lockfile.py +++ b/conan/api/subapi/lockfile.py @@ -103,6 +103,3 @@ def save_lockfile(lockfile, lockfile_out, path=None): lockfile_out = make_abs_path(lockfile_out, path) lockfile.save(lockfile_out) ConanOutput().info(f"Generated lockfile: {lockfile_out}") - - def reinit(self): - pass diff --git a/conan/api/subapi/new.py b/conan/api/subapi/new.py index d7a9ba46c77..8b3089a0c19 100644 --- a/conan/api/subapi/new.py +++ b/conan/api/subapi/new.py @@ -152,6 +152,3 @@ def as_name(ref): "Required definitions are: {}" .format(", ".join("'{}'".format(var) for var in template_vars))) return result - - def reinit(self): - pass diff --git a/conan/api/subapi/profiles.py b/conan/api/subapi/profiles.py index 9c2427abca3..1caf38c14c2 100644 --- a/conan/api/subapi/profiles.py +++ b/conan/api/subapi/profiles.py @@ -167,6 +167,3 @@ def _load_profile_plugin(self): mod, _ = load_python_file(profile_plugin) if hasattr(mod, "profile_plugin"): return mod.profile_plugin - - def reinit(self): - pass diff --git a/conan/api/subapi/remove.py b/conan/api/subapi/remove.py index f5ec4629bd3..24af4f466b7 100644 --- a/conan/api/subapi/remove.py +++ b/conan/api/subapi/remove.py @@ -49,6 +49,3 @@ def package(self, pref: PkgReference, remote: Remote): else: package_layout = app.cache.pkg_layout(pref) app.cache.remove_package_layout(package_layout) - - def reinit(self): - pass diff --git a/conan/api/subapi/search.py b/conan/api/subapi/search.py index 01201af4bce..997aa8d8bf6 100644 --- a/conan/api/subapi/search.py +++ b/conan/api/subapi/search.py @@ -22,6 +22,3 @@ def recipes(self, query: str, remote=None): if not only_none_user_channel or (r.user is None and r.channel is None): ret.append(r) return sorted(ret) - - def reinit(self): - pass diff --git a/conan/api/subapi/upload.py b/conan/api/subapi/upload.py index ca0821bee42..0522096e697 100644 --- a/conan/api/subapi/upload.py +++ b/conan/api/subapi/upload.py @@ -141,6 +141,3 @@ def upload_backup_sources(self, files): output.success("Upload backup sources complete\n") return files - - def reinit(self): - pass From 26f101d40f3f35208c9f848665b4516447f4e67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 10 Jan 2025 09:38:42 +0100 Subject: [PATCH 07/20] Add reinit test() --- test/integration/command/config_test.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py index 4e5404d36fd..68ba1f4d0ec 100644 --- a/test/integration/command/config_test.py +++ b/test/integration/command/config_test.py @@ -220,3 +220,24 @@ def test_config_clean(): assert "7" not in tc.out assert not os.path.exists(os.path.join(tc.cache_folder, "extensions")) + +def test_config_reinit(): + custom_global_conf = "core.upload:retry=7" + global_conf_folder = temp_folder() + with open(os.path.join(global_conf_folder, "global.conf"), "w") as f: + f.write(custom_global_conf) + + cache_folder = temp_folder() + conan_api = ConanAPI(cache_folder=cache_folder) + # Ensure reinitialization does not invalidate references + config_api = conan_api.config + assert config_api.global_conf.get("core.upload:retry", check_type=int) != 7 + + conan_api.config.install(global_conf_folder, verify_ssl=False) + + # No effect yet, we haven't reinitialized the API after the config installation + assert config_api.global_conf.get("core.upload:retry", check_type=int) != 7 + + conan_api.reinit() + + assert config_api.global_conf.get("core.upload:retry", check_type=int) == 7 From 6d63a693d78ee76a9c7d3dcc484b1b89f01c4d80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 10 Jan 2025 09:47:31 +0100 Subject: [PATCH 08/20] Missing reinit() --- conan/api/subapi/remotes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/api/subapi/remotes.py b/conan/api/subapi/remotes.py index 124a5f85088..33e85253bb9 100644 --- a/conan/api/subapi/remotes.py +++ b/conan/api/subapi/remotes.py @@ -36,7 +36,7 @@ def __init__(self, conan_api): self._requester = ConanRequester(self.conan_api.config.global_conf, self.conan_api.cache_folder) def reinit(self): - pass + self._requester = ConanRequester(self.conan_api.config.global_conf, self.conan_api.cache_folder) def list(self, pattern=None, only_enabled=True): """ From 493cf31a702b4f23467381b6fcf81bd8c3060152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 10 Jan 2025 10:08:00 +0100 Subject: [PATCH 09/20] Review comments --- conan/api/conan_api.py | 1 + conan/api/subapi/cache.py | 1 - conan/api/subapi/config.py | 3 +-- conan/cli/commands/config.py | 8 ++------ test/integration/command/config_test.py | 2 +- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index a3961ec967e..cc9b8c84dd4 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -61,6 +61,7 @@ def __init__(self, cache_folder=None): def reinit(self): self.config.reinit() + self.config.migrate() self.remotes.reinit() self.local.reinit() diff --git a/conan/api/subapi/cache.py b/conan/api/subapi/cache.py index 8c2dd089784..b3cf0b2159c 100644 --- a/conan/api/subapi/cache.py +++ b/conan/api/subapi/cache.py @@ -243,7 +243,6 @@ def get_backup_sources(self, package_list=None, exclude=True, only_upload=True): return download_cache.get_backup_sources_files(excluded_urls, package_list, only_upload) - def _resolve_latest_ref(cache, ref): if ref.revision is None or ref.revision == "latest": ref.revision = None diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index dd1daf72f00..e1942c4928d 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -194,13 +194,12 @@ def appending_recursive_dict_update(d, u): def migrate(self): # Migration system - # TODO: Still needs refactoring - using it here for now for tests + # TODO: A prettier refactoring of migrators would be nice from conans.client.migrations import ClientMigrator migrator = ClientMigrator(self.home(), conan_version) migrator.migrate() def clean(self): - # TODO: Check what we're deleting contents = os.listdir(self.home()) for content in contents: # keep packages diff --git a/conan/cli/commands/config.py b/conan/cli/commands/config.py index 0512a4db09b..8454d1401b3 100644 --- a/conan/cli/commands/config.py +++ b/conan/cli/commands/config.py @@ -140,9 +140,5 @@ def config_clean(conan_api, parser, subparser, *args): """ Clean the configuration files in the Conan home folder. (Keeping installed packages) """ - subparser.add_argument("-c", "--confirm", action='store_true', - help="Do not request confirmation") - args = parser.parse_args(*args) - ui = UserInput(conan_api.config.get("core:non_interactive")) - if args.confirm or ui.request_boolean("Clear all configuration files?"): - conan_api.config.clean() + parser.parse_args(*args) + conan_api.config.clean() diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py index 68ba1f4d0ec..a742ab06d54 100644 --- a/test/integration/command/config_test.py +++ b/test/integration/command/config_test.py @@ -211,7 +211,7 @@ def test_config_clean(): "extensions/compatibility/mycomp.py": "", "extensions/commands/cmd_foo.py": "", }) - tc.run("config clean -c") + tc.run("config clean") tc.run("profile list") assert "foo" not in tc.out tc.run("remote list") From 6781b2b48f8122e48aa22bbec9e4d649bdd1963e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 10 Jan 2025 10:10:09 +0100 Subject: [PATCH 10/20] Remove todo --- conan/api/subapi/local.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conan/api/subapi/local.py b/conan/api/subapi/local.py index b346828dc37..a50884b9eeb 100644 --- a/conan/api/subapi/local.py +++ b/conan/api/subapi/local.py @@ -120,5 +120,4 @@ def inspect(self, conanfile_path, remotes, lockfile, name=None, version=None, us return conanfile def reinit(self): - # TODO: Check if this is risky self.editable_packages = EditablePackages(self._conan_api.home_folder) From 954c501e576d8bf16dcb9f06c17a8c4b15744d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Mon, 13 Jan 2025 07:16:58 +0100 Subject: [PATCH 11/20] fix tests --- conan/api/conan_api.py | 1 - conan/api/subapi/config.py | 2 +- conans/client/migrations.py | 2 +- test/integration/command/config_test.py | 2 -- test/integration/test_migrations.py | 2 +- 5 files changed, 3 insertions(+), 6 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index cc9b8c84dd4..a3961ec967e 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -61,7 +61,6 @@ def __init__(self, cache_folder=None): def reinit(self): self.config.reinit() - self.config.migrate() self.remotes.reinit() self.local.reinit() diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index e1942c4928d..3c90ced40ef 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -12,6 +12,7 @@ from conan.internal.cache.home_paths import HomePaths from conan.internal.conan_app import ConanApp from conan.internal.default_settings import default_settings_yml +from conans.client.migrations import ClientMigrator from conans.client.graph.graph import CONTEXT_HOST, RECIPE_VIRTUAL, Node from conans.client.graph.graph_builder import DepsGraphBuilder from conans.client.graph.profile_node_definer import consumer_definer @@ -195,7 +196,6 @@ def appending_recursive_dict_update(d, u): def migrate(self): # Migration system # TODO: A prettier refactoring of migrators would be nice - from conans.client.migrations import ClientMigrator migrator = ClientMigrator(self.home(), conan_version) migrator.migrate() diff --git a/conans/client/migrations.py b/conans/client/migrations.py index 82997ba4e9e..026ef8b819b 100644 --- a/conans/client/migrations.py +++ b/conans/client/migrations.py @@ -3,7 +3,6 @@ import textwrap from conan.api.output import ConanOutput -from conan.api.subapi.config import ConfigAPI from conan.internal.default_settings import migrate_settings_file from conans.migrations import Migrator from conans.util.dates import timestamp_now @@ -76,6 +75,7 @@ def migrate(home_folder): def _migrate_pkg_db_lru(cache_folder, old_version): + from conan.api.subapi.config import ConfigAPI config = ConfigAPI.load_config(cache_folder) storage = config.get("core.cache:storage_path") or os.path.join(cache_folder, "p") db_filename = os.path.join(storage, 'cache.sqlite3') diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py index a742ab06d54..8d480bb996f 100644 --- a/test/integration/command/config_test.py +++ b/test/integration/command/config_test.py @@ -234,10 +234,8 @@ def test_config_reinit(): assert config_api.global_conf.get("core.upload:retry", check_type=int) != 7 conan_api.config.install(global_conf_folder, verify_ssl=False) - # No effect yet, we haven't reinitialized the API after the config installation assert config_api.global_conf.get("core.upload:retry", check_type=int) != 7 conan_api.reinit() - assert config_api.global_conf.get("core.upload:retry", check_type=int) == 7 diff --git a/test/integration/test_migrations.py b/test/integration/test_migrations.py index f8454443150..61020230305 100644 --- a/test/integration/test_migrations.py +++ b/test/integration/test_migrations.py @@ -148,6 +148,6 @@ def test_back_default_compatibility_migration(): # simulate that we are in 2.3.2 and the old one is latest conan_version migrator = ClientMigrator(t.cache_folder, Version("2.3.2")) - with patch('conan.api.conan_api.ClientMigrator', new=lambda *args, **kwargs: migrator): + with patch('conan.api.subapi.config.ClientMigrator', new=lambda *args, **kwargs: migrator): t.run("-v") # Fire the backward migration assert f"WARN: Downgrading cache from Conan {conan_version} to 2.3.2" in t.out From 021cece8c0eee83cefdd54db277ae7c766756826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 14 Jan 2025 09:40:27 +0100 Subject: [PATCH 12/20] Migrate is part of the full api now --- conan/api/conan_api.py | 10 +++++++++- conan/api/subapi/config.py | 9 +-------- test/integration/test_migrations.py | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index a3961ec967e..500078e73a2 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -20,6 +20,7 @@ from conan.api.subapi.upload import UploadAPI from conan.errors import ConanException from conan.internal.paths import get_conan_user_home +from conans.client.migrations import ClientMigrator from conans.model.version_range import validate_conan_version @@ -34,10 +35,10 @@ def __init__(self, cache_folder=None): self.workspace = WorkspaceAPI(self) self.cache_folder = self.workspace.home_folder() or cache_folder or get_conan_user_home() self.home_folder = self.cache_folder # Lets call it home, deprecate "cache" + self.migrate() # This API is depended upon by the subsequent ones, it should be initialized first self.config = ConfigAPI(self) - self.config.migrate() self.remotes = RemotesAPI(self) self.command = CommandAPI(self) @@ -66,6 +67,13 @@ def reinit(self): _check_conan_version(self) + def migrate(self): + # Migration system + # TODO: A prettier refactoring of migrators would be nice + from conan import conan_version + migrator = ClientMigrator(self.cache_folder, conan_version) + migrator.migrate() + def _check_conan_version(conan_api): required_range_new = conan_api.config.global_conf.get("core:required_conan_version") diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index 3c90ced40ef..abd901c4746 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -12,7 +12,6 @@ from conan.internal.cache.home_paths import HomePaths from conan.internal.conan_app import ConanApp from conan.internal.default_settings import default_settings_yml -from conans.client.migrations import ClientMigrator from conans.client.graph.graph import CONTEXT_HOST, RECIPE_VIRTUAL, Node from conans.client.graph.graph_builder import DepsGraphBuilder from conans.client.graph.profile_node_definer import consumer_definer @@ -193,12 +192,6 @@ def appending_recursive_dict_update(d, u): return Settings(settings) - def migrate(self): - # Migration system - # TODO: A prettier refactoring of migrators would be nice - migrator = ClientMigrator(self.home(), conan_version) - migrator.migrate() - def clean(self): contents = os.listdir(self.home()) for content in contents: @@ -211,7 +204,7 @@ def clean(self): else: remove(content_path) # CHECK: This also generates a remotes.json that is not there after a conan profile show? - self.migrate() + self.conan_api.migrate() def reinit(self): self._new_config = None diff --git a/test/integration/test_migrations.py b/test/integration/test_migrations.py index 61020230305..f8454443150 100644 --- a/test/integration/test_migrations.py +++ b/test/integration/test_migrations.py @@ -148,6 +148,6 @@ def test_back_default_compatibility_migration(): # simulate that we are in 2.3.2 and the old one is latest conan_version migrator = ClientMigrator(t.cache_folder, Version("2.3.2")) - with patch('conan.api.subapi.config.ClientMigrator', new=lambda *args, **kwargs: migrator): + with patch('conan.api.conan_api.ClientMigrator', new=lambda *args, **kwargs: migrator): t.run("-v") # Fire the backward migration assert f"WARN: Downgrading cache from Conan {conan_version} to 2.3.2" in t.out From 7c6e9377f35452f4fee18c00172201fb1b65c73f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Tue, 14 Jan 2025 09:57:23 +0100 Subject: [PATCH 13/20] Listen to core.cache:storage_path --- conan/api/subapi/config.py | 17 +++++++++-------- test/integration/command/config_test.py | 23 +++++++++++++++++++---- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index abd901c4746..57272b1d6af 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -194,15 +194,16 @@ def appending_recursive_dict_update(d, u): def clean(self): contents = os.listdir(self.home()) + packages_folder = self.global_conf.get("core.cache:storage_path") or os.path.join(self.home(), "p") for content in contents: - # keep packages - if content not in ("p", "version.txt"): - content_path = os.path.join(self.home(), content) - ConanOutput().debug(f"Removing {content_path}") - if os.path.isdir(content_path): - rmdir(content_path) - else: - remove(content_path) + content_path = os.path.join(self.home(), content) + if content_path == packages_folder or content == "version.txt": + continue + ConanOutput().debug(f"Removing {content_path}") + if os.path.isdir(content_path): + rmdir(content_path) + else: + remove(content_path) # CHECK: This also generates a remotes.json that is not there after a conan profile show? self.conan_api.migrate() diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py index 8d480bb996f..90e0148d280 100644 --- a/test/integration/command/config_test.py +++ b/test/integration/command/config_test.py @@ -2,7 +2,10 @@ import os import textwrap +import pytest + from conan.api.conan_api import ConanAPI +from conan.test.assets.genconanfile import GenConanfile from conans.model.conf import BUILT_IN_CONFS from conan.test.utils.test_files import temp_folder from conan.test.utils.tools import TestClient @@ -203,14 +206,25 @@ def test_config_show(): assert "zlib/*:user.myothercategory:foo" in tc.out -def test_config_clean(): +@pytest.mark.parametrize("storage_path", [None, "p", "../foo"]) +def test_config_clean(storage_path): tc = TestClient(light=True) - tc.run("profile detect --name=foo") - tc.run("remote add bar http://fakeurl") - tc.save_home({"global.conf": "core.upload:retry=7\n", + absolut_storage_path = os.path.abspath(os.path.join(tc.current_folder, storage_path)) if storage_path else os.path.join(tc.cache_folder, "p") + + storage = f"core.cache:storage_path={storage_path}" if storage_path else "" + tc.save_home({"global.conf": f"core.upload:retry=7\n{storage}", "extensions/compatibility/mycomp.py": "", "extensions/commands/cmd_foo.py": "", }) + + tc.run("profile detect --name=foo") + tc.run("remote add bar http://fakeurl") + + tc.save({"conanfile.py": GenConanfile("pkg", "0.1")}) + tc.run("create .") + + assert os.path.exists(absolut_storage_path) + tc.run("config clean") tc.run("profile list") assert "foo" not in tc.out @@ -219,6 +233,7 @@ def test_config_clean(): tc.run("config show core.upload:retry") assert "7" not in tc.out assert not os.path.exists(os.path.join(tc.cache_folder, "extensions")) + assert os.path.exists(absolut_storage_path) def test_config_reinit(): From d2690339d84e8c841b7acab2d62d996498b94a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Wed, 15 Jan 2025 10:36:27 +0100 Subject: [PATCH 14/20] Reinit api when config is cleaned --- conan/api/subapi/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index 04820c349cc..c64427d8638 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -204,6 +204,7 @@ def clean(self): rmdir(content_path) else: remove(content_path) + self.conan_api.reinit() # CHECK: This also generates a remotes.json that is not there after a conan profile show? self.conan_api.migrate() From b1a36549a8a814949bb3c8c935b8c80895fccba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 17 Jan 2025 09:26:10 +0100 Subject: [PATCH 15/20] Reinit conan_api when using --core-conf --- conan/cli/command.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conan/cli/command.py b/conan/cli/command.py index 5447203b37b..5b870db2b9f 100644 --- a/conan/cli/command.py +++ b/conan/cli/command.py @@ -134,6 +134,8 @@ def parse_args(self, args=None, namespace=None): confs.loads("\n".join(args.core_conf)) confs.validate() global_conf.update_conf_definition(confs) + # Last but not least, apply the new configuration + self._conan_api.reinit() # TODO: This might be even better moved to the ConanAPI so users without doing custom # commands can benefit from it From e9c5c00df3d27b51a724b287a195b292b8c91bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 17 Jan 2025 11:37:21 +0100 Subject: [PATCH 16/20] First initial approach for fixed reinit --- conan/api/conan_api.py | 4 ++-- conan/api/subapi/config.py | 21 ++++++++++++++++++--- conan/cli/command.py | 12 +----------- test/integration/command/config_test.py | 17 +++++++++++++++++ 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index 7b6fd14fec1..cc059b99921 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -60,8 +60,8 @@ def __init__(self, cache_folder=None): _check_conan_version(self) - def reinit(self): - self.config.reinit() + def reinit(self, keep_conf=False): + self.config.reinit(keep_conf) self.remotes.reinit() self.local.reinit() diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index c64427d8638..d7777c1c845 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -16,7 +16,7 @@ from conans.client.graph.graph_builder import DepsGraphBuilder from conans.client.graph.profile_node_definer import consumer_definer from conan.errors import ConanException -from conan.internal.model.conf import ConfDefinition, BUILT_IN_CONFS +from conan.internal.model.conf import ConfDefinition, BUILT_IN_CONFS, CORE_CONF_PATTERN from conan.internal.model.pkg_type import PackageType from conan.internal.model.recipe_ref import RecipeReference from conan.internal.model.settings import Settings @@ -40,6 +40,7 @@ def install(self, path_or_url, verify_ssl, config_type=None, args=None, requester = self.conan_api.remotes.requester configuration_install(cache_folder, requester, path_or_url, verify_ssl, config_type=config_type, args=args, source_folder=source_folder, target_folder=target_folder) + self.conan_api.reinit() def install_pkg(self, ref, lockfile=None, force=False, remotes=None, profile=None): ConanOutput().warning("The 'conan config install-pkg' is experimental", @@ -100,6 +101,7 @@ def install_pkg(self, ref, lockfile=None, force=False, remotes=None, profile=Non config_versions = {ref.split("/", 1)[0]: ref for ref in config_versions} config_versions[pkg.pref.ref.name] = pkg.pref.repr_notime() save(config_version_file, json.dumps({"config_version": list(config_versions.values())})) + self.conan_api.reinit() return pkg.pref def get(self, name, default=None, check_type=None): @@ -208,5 +210,18 @@ def clean(self): # CHECK: This also generates a remotes.json that is not there after a conan profile show? self.conan_api.migrate() - def reinit(self): - self._new_config = None + def update_global_conf(self, core_confs): + from conan.internal.model.conf import ConfDefinition + confs = ConfDefinition() + for c in core_confs: + if not CORE_CONF_PATTERN.match(c): + raise ConanException(f"Only core. values are allowed in --core-conf. Got {c}") + confs.loads("\n".join(core_confs)) + confs.validate() + self.global_conf.update_conf_definition(confs) + # Last but not least, apply the new configuration + self.conan_api.reinit(keep_conf=True) + + def reinit(self, keep_conf=False): + if not keep_conf: + self._new_config = None diff --git a/conan/cli/command.py b/conan/cli/command.py index 5b870db2b9f..ab2a319b695 100644 --- a/conan/cli/command.py +++ b/conan/cli/command.py @@ -4,7 +4,6 @@ from conan.api.output import ConanOutput from conan.errors import ConanException -from conan.internal.model.conf import CORE_CONF_PATTERN class OnceArgument(argparse.Action): @@ -126,16 +125,7 @@ def parse_args(self, args=None, namespace=None): ConanOutput().error("The --lockfile-packages arg is private and shouldn't be used") global_conf = self._conan_api.config.global_conf if args.core_conf: - from conan.internal.model.conf import ConfDefinition - confs = ConfDefinition() - for c in args.core_conf: - if not CORE_CONF_PATTERN.match(c): - raise ConanException(f"Only core. values are allowed in --core-conf. Got {c}") - confs.loads("\n".join(args.core_conf)) - confs.validate() - global_conf.update_conf_definition(confs) - # Last but not least, apply the new configuration - self._conan_api.reinit() + self._conan_api.config.update_global_conf(args.core_conf) # TODO: This might be even better moved to the ConanAPI so users without doing custom # commands can benefit from it diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py index 4b833606b89..5cd8c30463d 100644 --- a/test/integration/command/config_test.py +++ b/test/integration/command/config_test.py @@ -254,3 +254,20 @@ def test_config_reinit(): conan_api.reinit() assert config_api.global_conf.get("core.upload:retry", check_type=int) == 7 + + +def test_config_reinit_core_conf(): + tc = TestClient(light=True) + tc.save_home({"extensions/commands/cmd_foo.py": textwrap.dedent(""" + import json + from conan.cli.command import conan_command + from conan.api.output import ConanOutput + + @conan_command() + def foo(conan_api, parser, *args, **kwargs): + ''' Foo ''' + parser.parse_args(*args) + ConanOutput().info(f"Retry: {conan_api.config.global_conf.get('core.upload:retry', check_type=int)}") + """)}) + tc.run("foo -cc core.upload:retry=7") + assert "Retry: 7" in tc.out From 2be74e0c9e448f76bdcf8d6132d65bcfd2ebb78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 17 Jan 2025 12:11:41 +0100 Subject: [PATCH 17/20] Better reinit --- conan/api/conan_api.py | 4 ++-- conan/api/subapi/config.py | 14 ++++++++------ conan/cli/command.py | 2 +- test/integration/command/config_test.py | 5 +---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/conan/api/conan_api.py b/conan/api/conan_api.py index cc059b99921..7b6fd14fec1 100644 --- a/conan/api/conan_api.py +++ b/conan/api/conan_api.py @@ -60,8 +60,8 @@ def __init__(self, cache_folder=None): _check_conan_version(self) - def reinit(self, keep_conf=False): - self.config.reinit(keep_conf) + def reinit(self): + self.config.reinit() self.remotes.reinit() self.local.reinit() diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index d7777c1c845..ab6fc6d1519 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -28,6 +28,7 @@ class ConfigAPI: def __init__(self, conan_api): self.conan_api = conan_api self._new_config = None + self._cli_core_confs = None def home(self): return self.conan_api.cache_folder @@ -119,6 +120,8 @@ def global_conf(self): if self._new_config is None: cache_folder = self.conan_api.cache_folder self._new_config = self.load_config(cache_folder) + if self._cli_core_confs is not None: + self._new_config.update_conf_definition(self._cli_core_confs) return self._new_config @staticmethod @@ -210,7 +213,7 @@ def clean(self): # CHECK: This also generates a remotes.json that is not there after a conan profile show? self.conan_api.migrate() - def update_global_conf(self, core_confs): + def set_core_confs(self, core_confs): from conan.internal.model.conf import ConfDefinition confs = ConfDefinition() for c in core_confs: @@ -218,10 +221,9 @@ def update_global_conf(self, core_confs): raise ConanException(f"Only core. values are allowed in --core-conf. Got {c}") confs.loads("\n".join(core_confs)) confs.validate() - self.global_conf.update_conf_definition(confs) + self._cli_core_confs = confs # Last but not least, apply the new configuration - self.conan_api.reinit(keep_conf=True) + self.conan_api.reinit() - def reinit(self, keep_conf=False): - if not keep_conf: - self._new_config = None + def reinit(self): + self._new_config = None diff --git a/conan/cli/command.py b/conan/cli/command.py index ab2a319b695..4a7e80971b5 100644 --- a/conan/cli/command.py +++ b/conan/cli/command.py @@ -125,7 +125,7 @@ def parse_args(self, args=None, namespace=None): ConanOutput().error("The --lockfile-packages arg is private and shouldn't be used") global_conf = self._conan_api.config.global_conf if args.core_conf: - self._conan_api.config.update_global_conf(args.core_conf) + self._conan_api.config.set_core_confs(args.core_conf) # TODO: This might be even better moved to the ConanAPI so users without doing custom # commands can benefit from it diff --git a/test/integration/command/config_test.py b/test/integration/command/config_test.py index 5cd8c30463d..c84ea3e3680 100644 --- a/test/integration/command/config_test.py +++ b/test/integration/command/config_test.py @@ -249,10 +249,7 @@ def test_config_reinit(): assert config_api.global_conf.get("core.upload:retry", check_type=int) != 7 conan_api.config.install(global_conf_folder, verify_ssl=False) - # No effect yet, we haven't reinitialized the API after the config installation - assert config_api.global_conf.get("core.upload:retry", check_type=int) != 7 - - conan_api.reinit() + # Already has an effect, the config installation reinitializes the config assert config_api.global_conf.get("core.upload:retry", check_type=int) == 7 From 1bd50f70eee21a09bd138f6f0f14bf0ac6006ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 17 Jan 2025 13:14:25 +0100 Subject: [PATCH 18/20] Fix ConanOutput confs --- conan/cli/command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/cli/command.py b/conan/cli/command.py index 4a7e80971b5..b61b4841bb4 100644 --- a/conan/cli/command.py +++ b/conan/cli/command.py @@ -123,10 +123,10 @@ def parse_args(self, args=None, namespace=None): ConanOutput.define_log_level(args.v) if getattr(args, "lockfile_packages", None): ConanOutput().error("The --lockfile-packages arg is private and shouldn't be used") - global_conf = self._conan_api.config.global_conf if args.core_conf: self._conan_api.config.set_core_confs(args.core_conf) + global_conf = self._conan_api.config.global_conf # TODO: This might be even better moved to the ConanAPI so users without doing custom # commands can benefit from it ConanOutput.set_warnings_as_errors(global_conf.get("core:warnings_as_errors", From 54905cb2ca1298bc33a77452f174fde1d0b94f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 17 Jan 2025 13:26:08 +0100 Subject: [PATCH 19/20] Dont invalidate reinit global_conf --- conan/api/subapi/config.py | 18 +++++++++++++----- conan/cli/command.py | 2 +- conan/internal/model/conf.py | 3 +++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index ab6fc6d1519..38154ddabd7 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -117,13 +117,19 @@ def global_conf(self): configuration defined with the new syntax as in profiles, this config will be composed to the profile ones and passed to the conanfiles.conf, which can be passed to collaborators """ + # Lazy loading if self._new_config is None: - cache_folder = self.conan_api.cache_folder - self._new_config = self.load_config(cache_folder) - if self._cli_core_confs is not None: - self._new_config.update_conf_definition(self._cli_core_confs) + self._new_config = ConfDefinition() + self._populate_global_conf() return self._new_config + def _populate_global_conf(self): + cache_folder = self.conan_api.cache_folder + new_config = self.load_config(cache_folder) + self._new_config.update_conf_definition(new_config) + if self._cli_core_confs is not None: + self._new_config.update_conf_definition(self._cli_core_confs) + @staticmethod def load_config(home_folder): # Do not document yet, keep it private @@ -226,4 +232,6 @@ def set_core_confs(self, core_confs): self.conan_api.reinit() def reinit(self): - self._new_config = None + if self._new_config is not None: + self._new_config.clear() + self._populate_global_conf() diff --git a/conan/cli/command.py b/conan/cli/command.py index b61b4841bb4..4a7e80971b5 100644 --- a/conan/cli/command.py +++ b/conan/cli/command.py @@ -123,10 +123,10 @@ def parse_args(self, args=None, namespace=None): ConanOutput.define_log_level(args.v) if getattr(args, "lockfile_packages", None): ConanOutput().error("The --lockfile-packages arg is private and shouldn't be used") + global_conf = self._conan_api.config.global_conf if args.core_conf: self._conan_api.config.set_core_confs(args.core_conf) - global_conf = self._conan_api.config.global_conf # TODO: This might be even better moved to the ConanAPI so users without doing custom # commands can benefit from it ConanOutput.set_warnings_as_errors(global_conf.get("core:warnings_as_errors", diff --git a/conan/internal/model/conf.py b/conan/internal/model/conf.py index 065af309854..4f035f04afd 100644 --- a/conan/internal/model/conf.py +++ b/conan/internal/model/conf.py @@ -705,3 +705,6 @@ def loads(self, text, profile=False): def validate(self): for conf in self._pattern_confs.values(): conf.validate() + + def clear(self): + self._pattern_confs.clear() From 3d23f5597c2b9444b90eab1fc9e4da9e467fba42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abril=20Rinc=C3=B3n=20Blanco?= Date: Fri, 17 Jan 2025 13:34:13 +0100 Subject: [PATCH 20/20] Remove useless import --- conan/api/subapi/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conan/api/subapi/config.py b/conan/api/subapi/config.py index 38154ddabd7..4e56cc4435d 100644 --- a/conan/api/subapi/config.py +++ b/conan/api/subapi/config.py @@ -220,7 +220,6 @@ def clean(self): self.conan_api.migrate() def set_core_confs(self, core_confs): - from conan.internal.model.conf import ConfDefinition confs = ConfDefinition() for c in core_confs: if not CORE_CONF_PATTERN.match(c):