I am writing a cli application that does a lot of writing to stdout and also takes in prompts. I am wondering what is the reason the print functions are blacklisted and what else I should be using instead?
Prefer pure functions, try to return the result instead of printing it:
# bad
def f() -> None:
print('oh hi mark')
# good
def f() -> str:
return 'oh hi mark'
Read about pure functions and side-effects for more context.
Of course, you can't have an application without side-effects. So, your main function which is the entry point for the CLI does the printing. Use noqa to make this exception.
def main():
print(command()) # noqa: WPS
AFAIK, if side-effects are ok for you, I'd recommend using logging instead. For example, see how it is done in DepHell:
In this way, you can easily configure output format (enable and disable the colored output, use JSON, etc), set verbosity level, provide additional context etc.
Thanks for the very detailed response!
Of course, you can't have an application without side-effects. So, your main function which is the entry point for the CLI does the printing. Use noqa to make this exception.
I admit I'm probably not the norm, but my application is split into atomic "plugins" where each deals with one aspect of the application via subcommands (think git). Due to the "main" entrypoint not knowing which plugins are enabled ahead of time, and since some will produce output, some won't, some will alter other's output, etc. It seems more feasible to have these side-effects in each plugin and let them do any output they need to.
AFAIK, if side-effects are ok for you, I'd recommend using logging instead.
I am already using logging, but I didn't think it made sense to have prompts, where user input is required, to operate under the log, among other uses like executing commands who's sole job is to print stuff out to stdout.
See the logging docs also where it says print() should be used for
Display console output for ordinary usage of a command line script or program
If my application seems like a special case, then I suppose I'll just use noqa throughout where needed.
Probably, in this case, dependency injection would work best for you:
class PushCommand:
stream = sys.stdout
silent = False
def echo(self, *text: str, sep: str = ' ', end: str = '\n') -> None:
if silent:
return
print(*text, sep=sep, end=end, file=self.stream) # noqa: WPS
def __call__(self, *args):
self.echo('oh hi mark')
This is an intermediate solution when you still have side-effects but can disable them, tweak verbosity, and overload the echo function in the tests.
In general, the rule is to reduce points where side-effects happen as much as you can. Just see what works best in your case.
@orsinium that was a very helpful example to get me to re-think how I'm using side-effects, thank you!
I think my question has been answered, so I'll close.
Most helpful comment
@orsinium that was a very helpful example to get me to re-think how I'm using side-effects, thank you!
I think my question has been answered, so I'll close.