40

While writing a library for a large project I'm working on at work, an issue came up which required a token to be sent to an email address, and then passed back into the code where it can then be used for further use.

My colleague says to just read from STDIN (using Python: code = input("Enter code: ")) and then have a user pass it in, however to me this seems like bad practice as the library might (in this case definitely will) be used in a background task on a server.

I was wondering whether or not this was considered an anti-pattern or not.

Paradoxis
  • 1,361

5 Answers5

78

As a general guideline, libraries should be totally disconnected from the environment. That means that they shouldn't perform operations on standard streams, on specific files, or have any expectation about the environment or the context they are used.

Of course, there are exceptions to this rule, but there must be a very good reason for it. In the case of using stdin, i can't find any reason (unless your library actually provides routines for reading from stdin, like std::cin from C++). Also, taking the I/O streams from a parameter rather than having them hardcoded adds so much flexibility that it's not worth not doing it.

Paul92
  • 2,621
  • 17
  • 19
16

I would consider this not necessarily an anti-pattern, just a poorly designed library. It should be trivial to ask for a string as a method parameter, where the input could be passed directly in.

If that doesn't fit this usage, then a method parameter can be a stream, with STDIN passed into the method.

If that doesn't fit this usage, then the library is not flexible enough.

Bryan B
  • 2,814
4

Maybe consider having the ability in your library to set a callback to a user-provided function that will read input from wherever, and then return the appropriate value back to whatever part of the library is using that function.

1

If it does read from stdin, it means it would like to take program-level ownership of stdin. It likely is not compatible with any other library that reads from stdin, less specific protocol for how they share use. In at least my own personal glossary, this would make the library a framework, which is an expensive tradeoff.

But in this case, the library should probably just take an input file descriptor.

djechlin
  • 2,212
0

The answer by @Paul92 is good general discussion, but I would like to offer a possible clean(ish) solution to this:

A a library, this code needs to be adaptable to any runtime environment, so you can't really ask STDIN for some crucial bit of data. For one, users of your library might not have stdin available for a number of reasons. Instead you might want to use some form of strategy pattern to customise how the token is to be retrieved.

In Python, probably the best option is to pass in the token fetching strategy as a function parameter. Something like that:

def stdin_prompt():
    return input("Enter code: ")

def my_library_function(arg1, arg2, ... argn, token_provider = stdin_prompt):
    ...
    token = token_provider()
    ...
    return stuff

# somewhere in the user code
stuff = my_library_function(a1, a2, ... an, lambda: "123456")

Think of it like this. The token that you require, is an argument to the library function. Since the value for the token might not be statically known at the call site, you can not really ask for the value as an argument. Instead, the caller has to provide a function that is going to be responsible for providing the token when called.

All the responsibility for providing the exact mechanics of the token are now externalised from the library function. The consumer of the function is now responsible for acquiring the token by whatever means are available at runtime. It may ask STDIN, but it might also act as a mail gateway, wait for the message to pop in to the inbox, read it, extract the token and completely automate the process. It might be a GUI dialog or a web based form. Anything really -- all the options are now in the hands of the library consumer.