Skip to content

Commit

Permalink
warn if private imports from custom commands
Browse files Browse the repository at this point in the history
  • Loading branch information
memsharded committed Jan 29, 2025
1 parent 0098b06 commit 747fff5
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 0 deletions.
26 changes: 26 additions & 0 deletions conan/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from conan.internal.cache.home_paths import HomePaths
from conan import __version__
from conan.errors import ConanException, ConanInvalidConfiguration, ConanMigrationError
from conans.util.files import load

_CONAN_INTERNAL_CUSTOM_COMMANDS_PATH = "_CONAN_INTERNAL_CUSTOM_COMMANDS_PATH"

Expand All @@ -37,6 +38,29 @@ def __init__(self, conan_api):
self._groups = defaultdict(list)
self._commands = {}

@staticmethod
def _warning_custom_commands_imports(custom_commands_path):
# Protect against usage of internal API
forbidden = ["from conan.internal", "from conan.tools", "from conan.test", "from conans"]
fails = {}
for root, _, fs in os.walk(custom_commands_path):
pyfiles = [os.path.join(root, f) for f in fs if f.endswith(".py")]
for f in pyfiles:
text = load(f)
for forbid in forbidden:
if forbid in text:
fails.setdefault(forbid, []).append(f)
if fails:
ConanOutput().warning("*" * 80)
for forbid, files in fails.items():
ConanOutput().warning(f"Forbidden internal import'{forbid}' in:")
for f in files:
ConanOutput().warning(f" {f}")
ConanOutput().warning("")
ConanOutput().warning("These imports will change in future releases")
ConanOutput().warning("Please, use only public documented API in docs.conan.io")
ConanOutput().warning("*" * 80)

def add_commands(self):
if Cli._builtin_commands is None:
conan_cmd_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "commands")
Expand All @@ -61,6 +85,8 @@ def add_commands(self):
if not os.path.isdir(custom_commands_path):
return

self._warning_custom_commands_imports(custom_commands_path)

sys.path.append(custom_commands_path)
for module in pkgutil.iter_modules([custom_commands_path]):
module_name = module[1]
Expand Down
23 changes: 23 additions & 0 deletions test/integration/command_v2/custom_commands_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,26 @@ def mycache(conan_api, parser, *args, **kwargs):
c.run("mycache")
# Does not break unexpectedly, caching is working
assert "WARN: Cache invalidated, as expected: Invalid URL 'broken" in c.out


class TestCustomCommandsImports:

def test_import_warning(self):
mycommand = textwrap.dedent("""
from conan.cli.command import conan_command
from conan.internal.paths import DATA_YML
from conans.util.files import load
from conan.api.output import ConanOutput
@conan_command()
def mycache(conan_api, parser, *args, **kwargs):
\""" this is a command with subcommands \"""
ConanOutput().info(f"DATA_YML: {DATA_YML}")
""")

c = TestClient()
c.save_home({"extensions/commands/cmd_mycache.py": mycommand})
c.run("mycache")
assert "WARN: Forbidden internal import'from conan.internal' in:" in c.out
assert "WARN: Forbidden internal import'from conans' in:" in c.out
assert "DATA_YML: conandata.yml"

0 comments on commit 747fff5

Please sign in to comment.