Skip to content

Commit b57e1d7

Browse files
committed
Update Profile Detection
1 parent 5020ad0 commit b57e1d7

File tree

5 files changed

+126
-155
lines changed

5 files changed

+126
-155
lines changed

cppython/plugins/conan/plugin.py

Lines changed: 89 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import requests
1313
from conan.api.conan_api import ConanAPI
1414
from conan.api.model import ListPattern
15+
from conan.internal.model.profile import Profile
1516

1617
from cppython.core.plugin_schema.generator import SyncConsumer
1718
from cppython.core.plugin_schema.provider import Provider, ProviderPluginGroupData, SupportedProviderFeatures
@@ -108,45 +109,27 @@ def _install_dependencies(self, *, update: bool = False) -> None:
108109
all_remotes = conan_api.remotes.list()
109110
logger.debug('Available remotes: %s', [remote.name for remote in all_remotes])
110111

111-
# Get default profiles, handle case when no default profile exists
112-
try:
113-
profile_host_path = conan_api.profiles.get_default_host()
114-
profile_build_path = conan_api.profiles.get_default_build()
115-
116-
# Ensure we have valid profile paths
117-
if profile_host_path is None:
118-
# Create a minimal default profile if none exists
119-
profile_host = conan_api.profiles.get_profile([])
120-
else:
121-
profile_host = conan_api.profiles.get_profile([profile_host_path])
122-
123-
if profile_build_path is None:
124-
# Create a minimal default profile if none exists
125-
profile_build = conan_api.profiles.get_profile([])
126-
else:
127-
profile_build = conan_api.profiles.get_profile([profile_build_path])
128-
129-
except Exception:
130-
# If profile operations fail, create minimal default profiles
131-
profile_host = conan_api.profiles.get_profile([])
132-
profile_build = conan_api.profiles.get_profile([])
133-
134-
logger.debug('Using profiles: host=%s, build=%s', profile_host, profile_build)
135-
136-
# Build dependency graph
112+
# Get profiles with fallback to auto-detection
113+
profile_host, profile_build = self._get_profiles(conan_api)
114+
115+
path = str(conanfile_path)
116+
remotes = all_remotes
117+
update_flag = None if not update else True
118+
check_updates_flag = update
119+
137120
deps_graph = conan_api.graph.load_graph_consumer(
138-
path=str(conanfile_path),
121+
path=path,
139122
name=None,
140123
version=None,
141124
user=None,
142125
channel=None,
143-
profile_host=profile_host,
144-
profile_build=profile_build,
145126
lockfile=None,
146-
remotes=all_remotes,
147-
update=None if not update else True,
148-
check_updates=update,
127+
remotes=remotes,
128+
update=update_flag,
129+
check_updates=check_updates_flag,
149130
is_build_require=False,
131+
profile_host=profile_host,
132+
profile_build=profile_build,
150133
)
151134

152135
logger.debug('Dependency graph loaded with %d nodes', len(deps_graph.nodes))
@@ -266,43 +249,26 @@ def publish(self) -> None:
266249
remotes=all_remotes, # Use all remotes for dependency resolution during export
267250
)
268251

269-
# Step 2: Get default profiles, handle case when no default profile exists
270-
try:
271-
profile_host_path = conan_api.profiles.get_default_host()
272-
profile_build_path = conan_api.profiles.get_default_build()
252+
# Step 2: Get profiles with fallback to auto-detection
253+
profile_host, profile_build = self._get_profiles(conan_api)
273254

274-
# Ensure we have valid profile paths
275-
if profile_host_path is None:
276-
# Create a minimal default profile if none exists
277-
profile_host = conan_api.profiles.get_profile([])
278-
else:
279-
profile_host = conan_api.profiles.get_profile([profile_host_path])
280-
281-
if profile_build_path is None:
282-
# Create a minimal default profile if none exists
283-
profile_build = conan_api.profiles.get_profile([])
284-
else:
285-
profile_build = conan_api.profiles.get_profile([profile_build_path])
255+
# Step 3: Build dependency graph for the package - prepare parameters
256+
path = str(conanfile_path)
257+
remotes = all_remotes # Use all remotes for dependency resolution
286258

287-
except Exception:
288-
# If profile operations fail, create minimal default profiles
289-
profile_host = conan_api.profiles.get_profile([])
290-
profile_build = conan_api.profiles.get_profile([])
291-
292-
# Step 3: Build dependency graph for the package
293259
deps_graph = conan_api.graph.load_graph_consumer(
294-
path=str(conanfile_path),
260+
path=path,
295261
name=None,
296262
version=None,
297263
user=None,
298264
channel=None,
299-
profile_host=profile_host,
300-
profile_build=profile_build,
301265
lockfile=None,
302-
remotes=all_remotes, # Use all remotes for dependency resolution
266+
remotes=remotes,
303267
update=None,
304268
check_updates=False,
305269
is_build_require=False,
270+
profile_host=profile_host,
271+
profile_build=profile_build,
306272
)
307273

308274
# Step 4: Analyze binaries and install/build them if needed
@@ -339,3 +305,68 @@ def publish(self) -> None:
339305
)
340306
else:
341307
raise ProviderInstallationError('conan', 'No packages found to upload')
308+
309+
def _apply_profile_processing(self, profiles: list[Profile], conan_api: ConanAPI, cache_settings: Any) -> None:
310+
"""Apply profile plugin and settings processing to a list of profiles.
311+
312+
Args:
313+
profiles: List of profiles to process
314+
conan_api: The Conan API instance
315+
cache_settings: The settings configuration
316+
"""
317+
logger = logging.getLogger('cppython.conan')
318+
319+
# Apply profile plugin processing
320+
try:
321+
profile_plugin = conan_api.profiles._load_profile_plugin()
322+
if profile_plugin is not None:
323+
for profile in profiles:
324+
try:
325+
profile_plugin(profile)
326+
except Exception as plugin_error:
327+
logger.warning('Profile plugin failed for profile: %s', str(plugin_error))
328+
except (AttributeError, Exception):
329+
logger.debug('Profile plugin not available or failed to load')
330+
331+
# Process settings to initialize processed_settings
332+
for profile in profiles:
333+
try:
334+
profile.process_settings(cache_settings)
335+
except (AttributeError, Exception) as settings_error:
336+
logger.debug('Settings processing failed for profile: %s', str(settings_error))
337+
338+
def _get_profiles(self, conan_api: ConanAPI) -> tuple[Profile, Profile]:
339+
"""Get Conan profiles with fallback to auto-detection.
340+
341+
Args:
342+
conan_api: The Conan API instance
343+
344+
Returns:
345+
A tuple of (profile_host, profile_build) objects
346+
"""
347+
logger = logging.getLogger('cppython.conan')
348+
349+
try:
350+
# Gather default profile paths, these can raise exceptions if not available
351+
profile_host_path = conan_api.profiles.get_default_host()
352+
profile_build_path = conan_api.profiles.get_default_build()
353+
354+
# Load the actual profile objects, can raise if data is invalid
355+
profile_host = conan_api.profiles.get_profile([profile_host_path])
356+
profile_build = conan_api.profiles.get_profile([profile_build_path])
357+
358+
logger.debug('Using existing default profiles')
359+
return profile_host, profile_build
360+
361+
except Exception as e:
362+
logger.warning('Default profiles not available, using auto-detection. Conan message: %s', str(e))
363+
364+
# Create auto-detected profiles
365+
profiles = [conan_api.profiles.detect(), conan_api.profiles.detect()]
366+
cache_settings = conan_api.config.settings_yml
367+
368+
# Apply profile plugin processing to both profiles
369+
self._apply_profile_processing(profiles, conan_api, cache_settings)
370+
371+
logger.debug('Auto-detected profiles with plugin processing applied')
372+
return profiles[0], profiles[1]

tests/fixtures/conan.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def fixture_conan_mock_api(mocker: MockerFixture) -> Mock:
4343
mock_api.profiles.get_default_host = mocker.Mock(return_value=None)
4444
mock_api.profiles.get_default_build = mocker.Mock(return_value=None)
4545
mock_api.profiles.get_profile = mocker.Mock(return_value=mock_profile)
46+
mock_api.profiles.detect = mocker.Mock(return_value=mock_profile)
4647

4748
return mock_api
4849

@@ -90,6 +91,7 @@ def fixture_conan_mock_api_publish(mocker: MockerFixture) -> Mock:
9091
mock_api.profiles.get_default_host = mocker.Mock(return_value='/path/to/default/host')
9192
mock_api.profiles.get_default_build = mocker.Mock(return_value='/path/to/default/build')
9293
mock_api.profiles.get_profile = mocker.Mock(return_value=mock_profile)
94+
mock_api.profiles.detect = mocker.Mock(return_value=mock_profile)
9395

9496
return mock_api
9597

tests/integration/plugins/conan/test_provider.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Integration tests for the provider"""
22

3+
from pathlib import Path
34
from typing import Any
45

56
import pytest
@@ -8,7 +9,24 @@
89
from cppython.test.pytest.contracts import ProviderIntegrationTestContract
910

1011

11-
class TestCPPythonProvider(ProviderIntegrationTestContract[ConanProvider]):
12+
@pytest.fixture(autouse=True)
13+
def clean_conan_cache(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
14+
"""Sets CONAN_HOME to a temporary directory for each test.
15+
16+
This ensures all tests run with a clean Conan cache.
17+
18+
Args:
19+
tmp_path: Pytest temporary directory fixture
20+
monkeypatch: Pytest monkeypatch fixture for environment variable manipulation
21+
"""
22+
conan_home = tmp_path / 'conan_home'
23+
conan_home.mkdir()
24+
25+
# Set CONAN_HOME to the temporary directory
26+
monkeypatch.setenv('CONAN_HOME', str(conan_home))
27+
28+
29+
class TestConanProvider(ProviderIntegrationTestContract[ConanProvider]):
1230
"""The tests for the conan provider"""
1331

1432
@staticmethod

0 commit comments

Comments
 (0)