Skip to content

Commit

Permalink
ConanAPI refactor (#17479)
Browse files Browse the repository at this point in the history
  • Loading branch information
memsharded authored Dec 17, 2024
1 parent 57af759 commit 2c76ac3
Show file tree
Hide file tree
Showing 22 changed files with 116 additions and 132 deletions.
6 changes: 3 additions & 3 deletions conan/api/subapi/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from conan.api.model import Remote, PackagesList
from conan.api.output import ConanOutput
from conan.internal.conan_app import ConanApp
from conan.internal.conan_app import ConanBasicApp
from conan.errors import ConanException
from conans.model.package_ref import PkgReference
from conans.model.recipe_ref import RecipeReference
Expand All @@ -20,7 +20,7 @@ def recipe(self, ref: RecipeReference, remote: Remote, metadata: Optional[List[s
If the recipe is already in the cache it will be skipped,
but the specified metadata will be downloaded."""
output = ConanOutput()
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
assert ref.revision, f"Reference '{ref}' must have revision"
try:
app.cache.recipe_layout(ref) # raises if not found
Expand Down Expand Up @@ -53,7 +53,7 @@ def package(self, pref: PkgReference, remote: Remote, metadata: Optional[List[st
If the package is already in the cache it will be skipped,
but the specified metadata will be downloaded."""
output = ConanOutput()
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
try:
app.cache.recipe_layout(pref.ref) # raises if not found
except ConanException:
Expand Down
8 changes: 4 additions & 4 deletions conan/api/subapi/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from conan.internal.api.install.generators import write_generators
from conan.internal.cache.home_paths import HomePaths
from conan.internal.conan_app import ConanApp
from conan.internal.conan_app import ConanBasicApp
from conan.internal.deploy import do_deploys

from conans.client.graph.install_graph import InstallGraph
Expand All @@ -21,7 +21,7 @@ def install_binaries(self, deps_graph, remotes=None):
:param deps_graph: Dependency graph to intall packages for
:param remotes:
"""
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
installer = BinaryInstaller(app, self.conan_api.config.global_conf,
self.conan_api.local.editable_packages)
install_graph = InstallGraph(deps_graph)
Expand All @@ -35,7 +35,7 @@ def install_system_requires(self, graph, only_info=False):
:param only_info: Only allow reporting and checking, but never install
:param graph: Dependency graph to intall packages for
"""
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
installer = BinaryInstaller(app, self.conan_api.config.global_conf,
self.conan_api.local.editable_packages)
installer.install_system_requires(graph, only_info)
Expand All @@ -46,7 +46,7 @@ def install_sources(self, graph, remotes):
:param remotes:
:param graph: Dependency graph to install packages for
"""
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
installer = BinaryInstaller(app, self.conan_api.config.global_conf,
self.conan_api.local.editable_packages)
installer.install_sources(graph, remotes)
Expand Down
15 changes: 7 additions & 8 deletions conan/api/subapi/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from conan.api.model import PackagesList
from conan.api.output import ConanOutput, TimedOutput
from conan.internal.api.list.query_parse import filter_package_configs
from conan.internal.conan_app import ConanApp
from conan.internal.conan_app import ConanBasicApp
from conan.internal.paths import CONANINFO
from conan.internal.errors import NotFoundException
from conan.errors import ConanException
Expand All @@ -26,7 +26,7 @@ def __init__(self, conan_api):

def latest_recipe_revision(self, ref: RecipeReference, remote=None):
assert ref.revision is None, "latest_recipe_revision: ref already have a revision"
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
if remote:
ret = app.remote_manager.get_latest_recipe_reference(ref, remote=remote)
else:
Expand All @@ -36,7 +36,7 @@ def latest_recipe_revision(self, ref: RecipeReference, remote=None):

def recipe_revisions(self, ref: RecipeReference, remote=None):
assert ref.revision is None, "recipe_revisions: ref already have a revision"
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
if remote:
results = app.remote_manager.get_recipe_revisions_references(ref, remote=remote)
else:
Expand All @@ -50,7 +50,7 @@ def latest_package_revision(self, pref: PkgReference, remote=None):
# is used as an "exists" check too in other places, lets respect the None return
assert pref.revision is None, "latest_package_revision: ref already have a revision"
assert pref.package_id is not None, "package_id must be defined"
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
if remote:
ret = app.remote_manager.get_latest_package_reference(pref, remote=remote)
else:
Expand All @@ -60,7 +60,7 @@ def latest_package_revision(self, pref: PkgReference, remote=None):
def package_revisions(self, pref: PkgReference, remote=None):
assert pref.ref.revision is not None, "package_revisions requires a recipe revision, " \
"check latest first if needed"
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
if remote:
results = app.remote_manager.get_package_revisions_references(pref, remote=remote)
else:
Expand All @@ -71,12 +71,11 @@ def packages_configurations(self, ref: RecipeReference,
remote=None) -> Dict[PkgReference, dict]:
assert ref.revision is not None, "packages: ref should have a revision. " \
"Check latest if needed."
app = ConanBasicApp(self.conan_api)
if not remote:
app = ConanApp(self.conan_api)
prefs = app.cache.get_package_references(ref)
packages = _get_cache_packages_binary_info(app.cache, prefs)
else:
app = ConanApp(self.conan_api)
if ref.revision == "latest":
ref.revision = None
ref = app.remote_manager.get_latest_recipe_reference(ref, remote=remote)
Expand Down Expand Up @@ -143,7 +142,7 @@ def select(self, pattern, package_query=None, remote=None, lru=None, profile=Non
select_bundle = PackagesList()
# Avoid doing a ``search`` of recipes if it is an exact ref and it will be used later
search_ref = pattern.search_ref
app = ConanApp(self.conan_api)
app = ConanBasicApp(self.conan_api)
limit_time = timelimit(lru) if lru else None
out = ConanOutput()
remote_name = "local cache" if not remote else remote.name
Expand Down
20 changes: 11 additions & 9 deletions conan/api/subapi/remotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class RemotesAPI:
def __init__(self, conan_api):
# This method is private, the subapi is not instantiated by users
self.conan_api = conan_api
self._remotes_file = HomePaths(self.conan_api.cache_folder).remotes_path
self._home_folder = conan_api.home_folder
self._remotes_file = HomePaths(self._home_folder).remotes_path

def list(self, pattern=None, only_enabled=True):
"""
Expand Down Expand Up @@ -107,7 +108,7 @@ def add(self, remote: Remote, force=False, index=None):
:param index: if not defined, the new remote will be last one. Pass an integer to insert
the remote in that position instead of the last one
"""
add_local_recipes_index_remote(self.conan_api, remote)
add_local_recipes_index_remote(self._home_folder, remote)
remotes = _load(self._remotes_file)
if remote.remote_type != LOCAL_RECIPES_INDEX:
_validate_url(remote.url)
Expand Down Expand Up @@ -142,9 +143,9 @@ def remove(self, pattern):
removed = _filter(remotes, pattern, only_enabled=False)
remotes = [r for r in remotes if r not in removed]
_save(self._remotes_file, remotes)
localdb = LocalDB(self.conan_api.cache_folder)
localdb = LocalDB(self._home_folder)
for remote in removed:
remove_local_recipes_index_remote(self.conan_api, remote)
remove_local_recipes_index_remote(self._home_folder, remote)
localdb.clean(remote_url=remote.url)
return removed

Expand Down Expand Up @@ -201,7 +202,7 @@ def rename(self, remote_name: str, new_name: str):

def user_info(self, remote: Remote):
# TODO: Review
localdb = LocalDB(self.conan_api.cache_folder)
localdb = LocalDB(self._home_folder)
user_info = {}
user, token, _ = localdb.get_login(remote.url)
user_info["name"] = remote.name
Expand All @@ -226,29 +227,30 @@ def user_logout(self, remote: Remote):
:param remote: The ``Remote`` object to logout
"""
localdb = LocalDB(self.conan_api.cache_folder)
localdb = LocalDB(self._home_folder)
# The localdb only stores url + username + token, not remote name, so use URL as key
localdb.clean(remote_url=remote.url)

def user_set(self, remote: Remote, username):
# TODO: Review
localdb = LocalDB(self.conan_api.cache_folder)
localdb = LocalDB(self._home_folder)
if username == "":
username = None
localdb.store(username, token=None, refresh_token=None, remote_url=remote.url)

def user_auth(self, remote: Remote, with_user=False, force=False):
# TODO: Review
localdb = LocalDB(self._home_folder)
app = ConanApp(self.conan_api)
if with_user:
user, token, _ = app.localdb.get_login(remote.url)
user, token, _ = localdb.get_login(remote.url)
if not user:
var_name = f"CONAN_LOGIN_USERNAME_{remote.name.replace('-', '_').upper()}"
user = os.getenv(var_name, None) or os.getenv("CONAN_LOGIN_USERNAME", None)
if not user:
return
app.remote_manager.check_credentials(remote, force)
user, token, _ = app.localdb.get_login(remote.url)
user, token, _ = localdb.get_login(remote.url)
return user


Expand Down
29 changes: 19 additions & 10 deletions conan/internal/conan_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from conans.client.graph.proxy import ConanProxy
from conans.client.graph.python_requires import PyRequireLoader
from conans.client.graph.range_resolver import RangeResolver
from conans.client.hook_manager import HookManager
from conans.client.loader import ConanFileLoader, load_python_file
from conans.client.remote_manager import RemoteManager
from conans.client.rest.auth_manager import ConanApiAuthManager
Expand Down Expand Up @@ -36,29 +35,39 @@ def __init__(self, requester, cmd_wrapper, global_conf, cache, home_folder):
self.home_folder = home_folder


class ConanApp:
class ConanBasicApp:
def __init__(self, conan_api):
""" Needs:
- Global configuration
- Cache home folder
"""
global_conf = conan_api.config.global_conf
self.global_conf = global_conf
cache_folder = conan_api.home_folder
self.cache_folder = cache_folder
self.cache = PkgCache(self.cache_folder, global_conf)

home_paths = HomePaths(self.cache_folder)

# Wraps an http_requester to inject proxies, certs, etc
self.requester = ConanRequester(global_conf, cache_folder)
# To handle remote connections
# Wraps RestApiClient to add authentication support (same interface)
self.localdb = LocalDB(cache_folder)
auth_manager = ConanApiAuthManager(self.requester, cache_folder, self.localdb, global_conf)
localdb = LocalDB(cache_folder)
auth_manager = ConanApiAuthManager(self.requester, cache_folder, localdb, global_conf)
# Handle remote connections
self.remote_manager = RemoteManager(self.cache, auth_manager, cache_folder)


class ConanApp(ConanBasicApp):
def __init__(self, conan_api):
""" Needs:
- LocalAPI to read editable packages
"""
super().__init__(conan_api)
self.proxy = ConanProxy(self, conan_api.local.editable_packages)
self.range_resolver = RangeResolver(self, global_conf, conan_api.local.editable_packages)
self.range_resolver = RangeResolver(self, self.global_conf, conan_api.local.editable_packages)

self.pyreq_loader = PyRequireLoader(self, global_conf)
cmd_wrap = CmdWrapper(home_paths.wrapper_path)
conanfile_helpers = ConanFileHelpers(self.requester, cmd_wrap, global_conf, self.cache,
self.pyreq_loader = PyRequireLoader(self, self.global_conf)
cmd_wrap = CmdWrapper(HomePaths(self.cache_folder).wrapper_path)
conanfile_helpers = ConanFileHelpers(self.requester, cmd_wrap, self.global_conf, self.cache,
self.cache_folder)
self.loader = ConanFileLoader(self.pyreq_loader, conanfile_helpers)
25 changes: 8 additions & 17 deletions conan/test/utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from requests.exceptions import HTTPError
from webtest.app import TestApp

from conan.api.subapi.remotes import _save
from conan.cli.exit_codes import SUCCESS, ERROR_GENERAL
from conan.internal.cache.cache import PackageLayout, RecipeLayout, PkgCache
from conan.internal.cache.home_paths import HomePaths
Expand Down Expand Up @@ -459,7 +460,7 @@ def __init__(self, cache_folder=None, current_folder=None, servers=None, inputs=
save(self.cache.settings_path, "os: [Linux, Windows]")
else:
text = default_profiles[platform.system()]
save(self.cache.default_profile_path, text)
save(os.path.join(self.cache_folder, "profiles", "default"), text)
# Using internal env variable to add another custom commands folder
self._custom_commands_folder = custom_commands_folder

Expand All @@ -483,14 +484,6 @@ def __init__(self, cache_folder):
PkgCache.__init__(self, cache_folder, global_conf)
HomePaths.__init__(self, cache_folder)

@property
def plugins_path(self): # Temporary to not break tests
return os.path.join(self._home, "extensions", "plugins")

@property
def default_profile_path(self):
return os.path.join(self._home, "profiles", "default")

return MyCache(self.cache_folder)

@property
Expand All @@ -503,17 +496,15 @@ def storage_folder(self):
return self.cache.store

def update_servers(self):
api = ConanAPI(cache_folder=self.cache_folder)
for r in api.remotes.list():
api.remotes.remove(r.name)

remotes = []
for name, server in self.servers.items():
if isinstance(server, ArtifactoryServer):
api.remotes.add(Remote(name, server.repo_api_url))
remotes.append(Remote(name, server.repo_api_url))
elif isinstance(server, TestServer):
api.remotes.add(Remote(name, server.fake_url))
remotes.append(Remote(name, server.fake_url))
else:
api.remotes.add(Remote(name, server))
remotes.append(Remote(name, server))
_save(HomePaths(self.cache_folder).remotes_path, remotes)

@contextmanager
def chdir(self, newdir):
Expand Down Expand Up @@ -563,7 +554,7 @@ def _run_cli(self, command_line, assert_error=False):
try:
if self._custom_commands_folder:
with environment_update({_CONAN_INTERNAL_CUSTOM_COMMANDS_PATH:
self._custom_commands_folder}):
self._custom_commands_folder}):
command.run(args)
else:
command.run(args)
Expand Down
8 changes: 4 additions & 4 deletions conans/client/rest_client_local_recipe_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
from conans.util.files import load, save, rmdir, copytree_compat


def add_local_recipes_index_remote(conan_api, remote):
def add_local_recipes_index_remote(home_folder, remote):
if remote.remote_type != LOCAL_RECIPES_INDEX:
return
local_recipes_index_path = HomePaths(conan_api.cache_folder).local_recipes_index_path
local_recipes_index_path = HomePaths(home_folder).local_recipes_index_path
repo_folder = os.path.join(local_recipes_index_path, remote.name)

output = ConanOutput()
Expand All @@ -43,9 +43,9 @@ def post_export(conanfile):
save(trim_hook, hook_content)


def remove_local_recipes_index_remote(conan_api, remote):
def remove_local_recipes_index_remote(home_folder, remote):
if remote.remote_type == LOCAL_RECIPES_INDEX:
local_recipes_index_path = HomePaths(conan_api.cache_folder).local_recipes_index_path
local_recipes_index_path = HomePaths(home_folder).local_recipes_index_path
local_recipes_index_path = os.path.join(local_recipes_index_path, remote.name)
ConanOutput().info(f"Removing temporary files for '{remote.name}' "
f"local-recipes-index remote")
Expand Down
4 changes: 2 additions & 2 deletions test/integration/command/install/install_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
@pytest.fixture()
def client():
c = TestClient(default_server_user=True)
save(c.cache.settings_path, "os: [Windows, Macos, Linux, FreeBSD]\nos_build: [Windows, Macos]")
save(c.cache.default_profile_path, "[settings]\nos=Windows")
c.save_home({"settings.yml": "os: [Windows, Macos, Linux, FreeBSD]\nos_build: [Windows, Macos]",
"profiles/default": "[settings]\nos=Windows"})
return c


Expand Down
6 changes: 3 additions & 3 deletions test/integration/command/install/test_install_transitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
from conans.model.recipe_ref import RecipeReference
from conan.internal.paths import CONANFILE_TXT, CONANINFO
from conan.test.utils.tools import TestClient, GenConanfile
from conans.util.files import save, load
from conans.util.files import load


@pytest.fixture()
def client():
c = TestClient()
save(c.cache.settings_path, "os: [Windows, Macos, Linux, FreeBSD]\nos_build: [Windows, Macos]\narch_build: [x86_64]")
save(c.cache.default_profile_path, "[settings]\nos=Windows")
c.save_home({"settings.yaml": "os: [Windows, Macos, Linux, FreeBSD]\nos_build: [Windows, Macos]\narch_build: [x86_64]",
"profiles/default": "[settings]\nos=Windows"})

def base_conanfile(name):
return GenConanfile(name, "0.1").with_option("language", [0, 1])\
Expand Down
Loading

0 comments on commit 2c76ac3

Please sign in to comment.