[MERGE] Add a get_login method to UIFactory and test get_password

Martin Pool mbp at canonical.com
Tue Feb 20 04:19:58 GMT 2007


On 19 Feb 2007, Vincent Ladeuil <v.ladeuil+lp at free.fr> wrote:
> I needed to write tests involving querying user for login and
> password.
> 
> I rewrote part of the tests in the process so that they can be
> run from a dumb terminal (I get tired of
> test_text_factory_prompts_and_clears failures).

That sounds reasonable.  Does anything call these already or are they
new?

> Unicode experts comments welcome.
> 

> One point I couldn't decide was: should the login and passwords
> be encoded ?

I'm not sure.  I think your position is a reasonable place to start.

> I have no idea of what can be encountered as valid characters for
> login and passwords on all supported client systems and on all
> supported servers (restricting to ASCII seems clearly wrong,
> forcing utf-8 too, using unicode a joke ;). 
> 
> I thought that the safest was to not encode anything so that the
> servers receive exactly what the users typed.

> === modified file bzrlib/tests/__init__.py
> --- bzrlib/tests/__init__.py
> +++ bzrlib/tests/__init__.py
> @@ -530,6 +530,22 @@
>              return setattr(self._cstring, name, val)
>  
>  
> +class FakeStdin(StringIOWrapper):
> +    """Simulated stdin for tests only.
> +
> +    We pretend to be the real stdin by redirecting the fileno method so that
> +    getpas.getpass can succeed changing the echo mode of the real stdin. That
> +    allows tests to can user inputs without having to implement a
> +    full-fledged stdin.
> +    """

s/getpas/

Thanks for documenting this, it would have been a bit confusing.

Exposing it to the real stdin is a bit ugly but perhaps pragmatic.  
Would it work to instead open /dev/null or nul:?

> +
> +    fileno = sys.stdin.fileno
> +
> +    def __init__(self, string, encoding='ascii'):
> +        StringIOWrapper.__init__(self, string.encode(encoding))
> +        self.encoding = encoding
> +
> +
>  class TestCase(unittest.TestCase):
>      """Base class for bzr unit tests.
>      
> 
 
> === modified file bzrlib/ui/__init__.py
> --- bzrlib/ui/__init__.py
> +++ bzrlib/ui/__init__.py
> @@ -56,6 +56,21 @@
>          """See UIFactory.nested_progress_bar()."""
>          raise NotImplementedError(self.progress_bar)
>  
> +    def get_login(self, prompt='', **kwargs):
> +        """Prompt the user for a login (generally on a remote host).
> +
> +        :param prompt: The prompt to present the user
> +        :param kwargs: Arguments which will be expanded into the prompt.
> +                       This lets front ends display different things if
> +                       they so choose.
> +

Rather than passing the prompt and kwargs to these methods why not let
the caller combine them?

> +        :return: The user string, return None if the user canceled the
> +                 request. Note that we do not touch the encoding, users may
> +                 have whatever they see fit and the password should be
> +                 transported as is. 
> +        """
> +        raise NotImplementedError(self.progress_bar)

Should be self.get_login.

> +
>      def get_password(self, prompt='', **kwargs):
>          """Prompt the user for a password.
>  
> @@ -63,11 +78,14 @@
>          :param kwargs: Arguments which will be expanded into the prompt.
>                         This lets front ends display different things if
>                         they so choose.
> -        :return: The password string, return None if the user 
> -                 canceled the request.
> +
> +        :return: The password string, return None if the user canceled the
> +                 request. Note that we do not touch the encoding, users may
> +                 have whatever they see fit and the password should be
> +                 transported as is.
>          """
>          raise NotImplementedError(self.get_password)
> -        
> +
>      def nested_progress_bar(self):
>          """Return a nested progress bar.
>  
> @@ -104,7 +122,7 @@
>          self.clear_term()
>          # FIXME: make a regexp and handle case variations as well.
>          while True:
> -            self.prompt(prompt)
> +            self.prompt(prompt + "? [y/n]: ")
>              line = self.stdin.readline()
>              if line in ('y\n', 'yes\n'):
>                  return True
> @@ -126,6 +144,9 @@
>          """See UIFactory.nested_progress_bar()."""
>          return progress.DummyProgress()
>  
> +    def get_login(self, prompt='', **kwargs):
> +        return None
> +
>      def get_password(self, prompt='', **kwargs):
>          return None
>  
> 
> === modified file bzrlib/ui/text.py
> --- bzrlib/ui/text.py
> +++ bzrlib/ui/text.py
> @@ -27,6 +27,7 @@
>  
>  from bzrlib import (
>      progress,
> +    osutils,
>      )
>  """)
>  
> @@ -63,7 +64,7 @@
>  
>      def prompt(self, prompt):
>          """Emit prompt on the CLI."""
> -        self.stdout.write(prompt + "? [y/n]:")
> +        self.stdout.write(prompt)
>          
>      @deprecated_method(zero_eight)
>      def progress_bar(self):
> @@ -72,6 +73,23 @@
>          # bar depending on what we think of the terminal
>          return progress.ProgressBar()
>  
> +    def get_login(self, prompt='', **kwargs):
> +        """Prompt the user for a login (generally on a remote host).
> +
> +        :param prompt: The prompt to present the user
> +        :param kwargs: Arguments which will be expanded into the prompt.
> +                       This lets front ends display different things if
> +                       they so choose.
> +        :return: The user string, return None if the user 
> +                 canceled the request.
> +        """
> +        prompt += ': '
> +        prompt = (prompt % kwargs).encode(sys.stdout.encoding, 'replace')
> +        self.prompt(prompt)
> +        login = self.stdin.readline()
> +        login = login.rstrip('\n')
> +        return login
> +
>      def get_password(self, prompt='', **kwargs):
>          """Prompt the user for a password.
>  
> @@ -82,8 +100,8 @@
>          :return: The password string, return None if the user 
>                   canceled the request.
>          """
> +        prompt += ': '
>          prompt = (prompt % kwargs).encode(sys.stdout.encoding, 'replace')
> -        prompt += ': '
>          # There's currently no way to say 'i decline to enter a password'
>          # as opposed to 'my password is empty' -- does it matter?
>          return getpass.getpass(prompt)
> 
> === modified directory  // last-changed:v.ladeuil+lp at free.fr-20070219210827-qcz
> ... 46is44zdpfjxr
> # revision id: v.ladeuil+lp at free.fr-20070219210827-qcz46is44zdpfjxr
> # sha1: 53d122db5a837569f66e3151387997145bf67446
> # inventory sha1: 7201b85efd3244af16f0abdd4455a5bb7989f717
> # parent ids:
> #   pqm at pqm.ubuntu.com-20070217025822-306d98c244b53b08
> # base id: pqm at pqm.ubuntu.com-20070217025822-306d98c244b53b08
> # properties:
> #   branch-nick: fakestdin
> 

-- 
Martin



More information about the bazaar mailing list