Repository: conan-io/conan

Issue:
[question] verbose mode hides authentication
### What is your question?

Dear Conan Folks!

I have a question I use conan build and conan export-pkg.
I use conan build like this:
```
conan build . -of kdp --deployer=full_deploy --update -pr:b=default -pr:h=kdp -verror  -c user.build:verbose=False -c tools.compilation:verbosity=quiet
```  

But if my remote repositories needs authentication then it just hangs and leaves no opportunity for user unless I use it like:
```
conan build . -of kdp --deployer=full_deploy --update -pr:b=default -pr:h=kdp  -vdebug -c user.build:verbose=True -c tools.compilation:verbosity=verbose
```
Is there a setting to show repository authentication with or without verbose mode on?

Thanks in advance for your help!
Br,
Maitrey

### Have you read the CONTRIBUTING guide?

- [x] I've read the CONTRIBUTING guide


Discussion:
Hi @maitrey 

Thanks for your feedback.

Quick question, what does the ``-c user.build:verbose=False`` does? Maybe this could be related?

Could you please try to isolate the origin? Is it because of the ``-verror``/``-vdebug``, or because of the ``-c tools.compilation:verbosity=quiet`` change?
If I remove -verror from the command:
```
conan build . -of kdp --deployer=full_deploy --update -pr:b=default -pr:h=kdp -c user.build:verbose=False -c tools.compilation:verbosity=quiet
```
it prints all the information with respect to host profile, build profile, dependency graph computation, installing packages, finalizing install, build.

If I remove -c user.build:verbose=False and run the command:
```
conan build . -of kdp --deployer=full_deploy --update -pr:b=default -pr:h=kdp -c user.build:verbose=False
```  
it prints all the information with respect to host profile, build profile, depdendency graph computation, installing packages, finalizing install, build.

If I remove -c user.build:verbose=False and run the command:
```
conan build . -of kdp --deployer=full_deploy --update -pr:b=default -pr:h=kdp -verror -c tools.compilation:verbosity=quiet
```
it prints only the build related overview
In all the above use-cases, 
I have to use to -verror to see only the build related information.
And in other cases I must use -vdebug when I would like to see all the information with respect to host profile, build profile, dependency graph computation, installing packages, finalizing install, build.



but this is not clear:

> But if my remote repositories needs authentication then it just hangs

The ``hanging`` part is what is not clear.

Also the ``-c user.build:verbose=False``, that is just a user conf, not something that Conan is aware. It could be left out, or if it is relevant, we would need to know how, what is the recipe code that reads it? What does it do?

It would be great to have something that we can reproduce from our side.
Sorry for not being clear.
By hanging what I mean is , it does not prompt for password incase if the access to artifactory token/password is expired or user does not have access to artifactory repositories. This can be seen only by using -vdebug.
I am using the user  configuration in order to skip build and only have the set up for a package:
setup_only = self.conf.get("user.build:setup", default=False)
Because a lot of users use matlab and they may directly do something with matlab like altering a model , fixing a test etc.
Thanks for the details!
I manage to understand now, and indeed ,the logging levels were hiding the user/password message, fixing it in https://github.com/conan-io/conan/pull/18642 for next 2.19.



Patch:
diff --git a/conan/api/input.py b/conan/api/input.py
index 3fae3b4fa21..258ded10dfa 100644
--- a/conan/api/input.py
+++ b/conan/api/input.py
@@ -33,11 +33,11 @@ def request_login(self, remote_name, username=None):
         :param username If username is specified it only request password"""
         self._raise_if_non_interactive()
         if not username:
-            self._out.write("Remote '%s' username: " % remote_name)
+            self._out.login_msg(f"Remote '{remote_name}' username: ")
             username = self.get_username()
 
-        self._out.write("Please enter a password for user '%s' on remote '%s': "
-                        % (username, remote_name))
+        self._out.login_msg("Please enter a password for "
+                            f"user '{username}' on remote '{remote_name}': ")
         try:
             pwd = self.get_password()
         except ConanException:
diff --git a/conan/api/output.py b/conan/api/output.py
index 535586c00cf..a8e56548d72 100644
--- a/conan/api/output.py
+++ b/conan/api/output.py
@@ -19,7 +19,7 @@
 LEVEL_TRACE = 10  # -vvv Fine-grained messages with very low-level implementation details
 
 
-class Color(object):
+class Color:
     """ Wrapper around colorama colors that are undefined in importing
     """
     RED = Fore.RED  # @UndefinedVariable
@@ -181,6 +181,11 @@ def box(self, msg):
         self.writeln(f'*{msg: ^48}*', fg=color)
         self.writeln(f"**************************************************\n", fg=color)
 
+    def login_msg(self, msg, newline=False):
+        # unconditional to the error level, this has to show always
+        self._write_message(msg, newline=newline)
+        return self
+
     def _write_message(self, msg, fg=None, bg=None, newline=True):
         if isinstance(msg, dict):
             # For traces we can receive a dict already, we try to transform then into more natural
@@ -189,20 +194,19 @@ def _write_message(self, msg, fg=None, bg=None, newline=True):
             msg = "=> {}".format(msg)
             # msg = json.dumps(msg, sort_keys=True, default=json_encoder)
 
-        ret = ""
         if self._scope:
             if self._color:
-                ret = "{}{}{}:{} ".format(fg or '', bg or '', self.scope, Style.RESET_ALL)
+                ret = f"{fg or ''}{bg or ''}{self._scope}: {msg}{Style.RESET_ALL}"
             else:
-                ret = "{}: ".format(self._scope)
-
-        if self._color:
-            ret += "{}{}{}{}".format(fg or '', bg or '', msg, Style.RESET_ALL)
+                ret = f"{self._scope}: {msg}"
         else:
-            ret += "{}".format(msg)
+            if self._color:
+                ret = f"{fg or ''}{bg or ''}{msg}{Style.RESET_ALL}"
+            else:
+                ret = msg
 
         if newline:
-            ret = "%s\n" % ret
+            ret += "\n"
 
         with self.lock:
             self.stream.write(ret)
@@ -260,7 +264,8 @@ def _warn_tag_matches(warn_tag, patterns):
 
     def warning(self, msg, warn_tag=None):
         _treat_as_error = self._warn_tag_matches(warn_tag, self._warnings_as_errors)
-        if self._conan_output_level <= LEVEL_WARNING or (_treat_as_error and self._conan_output_level <= LEVEL_ERROR):
+        if (self._conan_output_level <= LEVEL_WARNING or
+                (_treat_as_error and self._conan_output_level <= LEVEL_ERROR)):
             if self._warn_tag_matches(warn_tag, self._silent_warn_tags):
                 return self
             warn_tag_msg = "" if warn_tag is None else f"{warn_tag}: "


Test patch:
diff --git a/test/integration/remote/auth_test.py b/test/integration/remote/auth_test.py
index 25ae03cb382..42d0b3eb7a3 100644
--- a/test/integration/remote/auth_test.py
+++ b/test/integration/remote/auth_test.py
@@ -1,35 +1,25 @@
 import copy
 import os
 import textwrap
-import unittest
-import pytest
 
+import pytest
 from requests.models import Response
 
-from conan.internal.api.remotes.localdb import LocalDB
 from conan.api.model import RecipeReference
-from conan.internal.paths import CONANFILE
+from conan.internal.api.remotes.localdb import LocalDB
+from conan.internal.util.files import save
 from conan.test.assets.genconanfile import GenConanfile
+from conan.test.utils.env import environment_update
 from conan.test.utils.test_files import temp_folder
 from conan.test.utils.tools import TestClient
 from conan.test.utils.tools import TestRequester
 from conan.test.utils.tools import TestServer
-from conan.test.utils.env import environment_update
-from conan.internal.util.files import save
-
-conan_content = """
-from conan import ConanFile
-
-class OpenSSLConan(ConanFile):
-    name = "openssl"
-    version = "2.0.1"
-    files = '*'
-"""
 
 
-class AuthorizeTest(unittest.TestCase):
+class TestAuthorize:
 
-    def setUp(self):
+    @pytest.fixture(autouse=True)
+    def setup(self):
         self.servers = {}
         self.ref = RecipeReference.loads("openssl/2.0.1@lasote/testing")
         # Create a default remote. R/W is not authorized for ref,
@@ -46,25 +36,25 @@ def test_retries(self):
         self.conan = TestClient(servers=self.servers, inputs=["bad", "1",
                                                               "bad2", "2",
                                                               "nacho@gmail.com", "nachopass"])
-        save(os.path.join(self.conan.current_folder, CONANFILE), conan_content)
+        self.conan.save({"conanfile.py": GenConanfile("openssl", "2.0.1")})
         self.conan.run("export . --user=lasote --channel=testing")
-        errors = self.conan.run("upload %s -r default --only-recipe" % str(self.ref))
+        errors = self.conan.run("upload %s -r default --only-recipe -v=quiet" % str(self.ref))
         # Check that return was  ok
-        self.assertFalse(errors)
+        assert not errors
         # Check that upload was granted
         rev = self.test_server.server_store.get_last_revision(self.ref).revision
         ref = copy.copy(self.ref)
         ref.revision = rev
-        self.assertTrue(os.path.exists(self.test_server.server_store.export(ref)))
-        self.assertIn("Please enter a password for user 'bad'", self.conan.out)
-        self.assertIn("Please enter a password for user 'bad2'", self.conan.out)
-        self.assertIn("Please enter a password for user 'nacho@gmail.com'", self.conan.out)
+        assert os.path.exists(self.test_server.server_store.export(ref))
+        assert "Please enter a password for user 'bad'" in self.conan.out
+        assert "Please enter a password for user 'bad2'" in self.conan.out
+        assert "Please enter a password for user 'nacho@gmail.com'" in self.conan.out
 
     def test_auth_with_env(self):
 
         def _upload_with_credentials(credentials, assert_error=False):
             cli = TestClient(servers=self.servers)
-            save(os.path.join(cli.current_folder, CONANFILE), conan_content)
+            cli.save({"conanfile.py": GenConanfile("openssl", "2.0.1")})
             cli.run("export . --user=lasote --channel=testing")
             with environment_update(credentials):
                 cli.run("upload %s -r default --only-recipe" % str(self.ref),
@@ -74,42 +64,42 @@ def _upload_with_credentials(credentials, assert_error=False):
         # Try with remote name in credentials
         client = _upload_with_credentials({"CONAN_PASSWORD_DEFAULT": "pepepass",
                                            "CONAN_LOGIN_USERNAME_DEFAULT": "pepe"})
-        self.assertIn("Got username 'pepe' from environment", client.out)
-        self.assertIn("Got password '******' from environment", client.out)
+        assert "Got username 'pepe' from environment" in client.out
+        assert "Got password '******' from environment" in client.out
 
         # Try with generic password and login
         client = _upload_with_credentials({"CONAN_PASSWORD": "pepepass",
                                            "CONAN_LOGIN_USERNAME_DEFAULT": "pepe"})
-        self.assertIn("Got username 'pepe' from environment", client.out)
-        self.assertIn("Got password '******' from environment", client.out)
+        assert "Got username 'pepe' from environment" in client.out
+        assert "Got password '******' from environment" in client.out
 
         # Try with generic password and generic login
         client = _upload_with_credentials({"CONAN_PASSWORD": "pepepass",
                                            "CONAN_LOGIN_USERNAME": "pepe"})
-        self.assertIn("Got username 'pepe' from environment", client.out)
-        self.assertIn("Got password '******' from environment", client.out)
+        assert "Got username 'pepe' from environment" in client.out
+        assert "Got password '******' from environment" in client.out
 
         # Bad pass raise
         client = _upload_with_credentials({"CONAN_PASSWORD": "bad",
                                            "CONAN_LOGIN_USERNAME": "pepe"}, assert_error=True)
-        self.assertIn("Authentication error in remote 'default'", client.out)
+        assert "Authentication error in remote 'default'" in client.out
 
     def test_max_retries(self):
         """Bad login 3 times"""
         client = TestClient(servers=self.servers, inputs=["baduser", "badpass",
                                                           "baduser", "badpass2",
                                                           "baduser3", "badpass3"])
-        save(os.path.join(client.current_folder, CONANFILE), conan_content)
+        client.save({"conanfile.py": GenConanfile("openssl", "2.0.1")})
         client.run("export . --user=lasote --channel=testing")
         errors = client.run("upload %s -r default --only-recipe" % str(self.ref), assert_error=True)
         # Check that return was not ok
-        self.assertTrue(errors)
+        assert errors
         # Check that upload was not granted
         rev = self.servers["default"].server_store.get_last_revision(self.ref)
-        self.assertIsNone(rev)
+        assert rev is None
 
         # Check that login failed all times
-        self.assertIn("Too many failed login attempts, bye!", client.out)
+        assert "Too many failed login attempts, bye!" in client.out
 
     def test_no_client_username_checks(self):
         """Checks whether client username checks are disabled."""
@@ -119,7 +109,7 @@ def test_no_client_username_checks(self):
                                                           "badpass",
                                                           "nacho@gmail.com", "nachopass"])
 
-        save(os.path.join(client.current_folder, CONANFILE), conan_content)
+        client.save({"conanfile.py": GenConanfile("openssl", "2.0.1")})
         client.run("export . --user=lasote --channel=testing")
         client.run("upload %s -r default --only-recipe" % str(self.ref))
 
@@ -127,9 +117,9 @@ def test_no_client_username_checks(self):
         rev = self.test_server.server_store.get_last_revision(self.ref).revision
         ref = copy.copy(self.ref)
         ref.revision = rev
-        self.assertTrue(os.path.exists(self.test_server.server_store.export(ref)))
-        self.assertIn("Please enter a password for user 'some_random.special!characters' on remote 'default'",
-                      client.out)
+        assert os.path.exists(self.test_server.server_store.export(ref))
+        assert ("Please enter a password for user 'some_random.special!characters' "
+                "on remote 'default'") in client.out
 
     def test_authorize_disabled_remote(self):
         tc = TestClient(servers=self.servers)
@@ -139,7 +129,8 @@ def test_authorize_disabled_remote(self):
         # This used to fail when the authentication was not possible for disabled remotes
         tc.run("remote disable default")
         tc.run("remote login default pepe -p pepepass")
-        self.assertIn("Changed user of remote 'default' from 'None' (anonymous) to 'pepe' (authenticated)", tc.out)
+        assert ("Changed user of remote 'default' "
+                "from 'None' (anonymous) to 'pepe' (authenticated)") in tc.out
 
 
 class TestAuthenticationTest: