0

I am introducing the loggers into my project and I would like to ban the print statement usage in it. My intent is to force any future developers to use the loggers and to make sure I replaced all print invocations in project.

So far I managed to restrict print('foo') and print 'foo' like invocations with:

from __future__ import print_function

def print(*args, **kwargs):
    raise SyntaxError("Don't use print! Use logger instead.")

But it is still possible to use print without arguments with intent of adding newline but it won't do anything.

Is it possible to do it without interpreter modifications?

EDIT: I wasn't clear enough I guess from the comments. I just wanted to know if I can prevent the print function for being aliased

print("foo") # raises exception
print "foo" # doesn't work either
print # doesn't raise any exception, but I want it to
foo = print # this shouldn't work either like the one above, but it does
Dluzak
  • 315
  • 1
  • 11
  • What about put `args` and `kwargs` with a default value? I'm not sure it will work, but could be a workaround. EDIT: I tried and it raises the exception. – A. Wolf Oct 17 '18 at 12:18
  • I suggest adding unit tests that verify that the log output actually looks like intended. You could also write a custom pylint plugin to issue linter warnings whenever `print` is used. – Sven Marnach Oct 17 '18 at 12:22
  • Rather than not trust your developers and try to break Python, why not catch this at review time (with linters and auto-formatters, perhaps)? – Martijn Pieters Oct 17 '18 at 12:28
  • Yeah, linters and auto-formatters are perfectly valid answers for _this_ question, but completely inappropriate for the other one, which is why I don't think they are duplicates. – Sven Marnach Oct 17 '18 at 12:29
  • 1
    @SvenMarnach: right, I see your point. Wrote an answer instead. – Martijn Pieters Oct 17 '18 at 12:36
  • solving the wrong problem?... or solving a problem wrong way? why you want to restrict print command? – Shahram Banazadeh Oct 17 '18 at 12:55

1 Answers1

4

No, you can't prevent print statements from being used in code that doesn't use from __future__ import print_function. print statements are not hooked, they are compiled directly to a set of opcodes and the implementation of those opcodes just write directly to stdout or other file object (when using the >> notation).

You could go the drastic route of requiring a custom codec, but that's no better than requiring that from __future__ import print_function is used.

By the same token, if all code does use from __future__ import print_function, while you can assign a new print function to the __builtin__ module, you can't prevent someone from building their own version (named print or something else) that writes to sys.stdout, or from executing reload(__builtin__). Python is highly dynamic and flexible, I'd not try to lock this down.

The normal path to enforce coding standards is to use a linter, code review and tests. You can install hooks on most version control systems that prevent code from being checked in that doesn't pass a linter, and both pylint and flake8 support custom plugins. You can run a test that configures the logging module to direct all output to a file then raise an exception if anything is written to stdout, etc.

This is the path that Facebook uses (and it is not alone in this approach), where Python code must pass the Facebook flake8 configuration, which includes the flake8-bugbear extension, and code is autoformatted using Black to make it easy for developers to meet those requirements.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I think what the OP is worried about is that `from __future__ import print_function` still allows bare `print` statements. Without the future import, they would write a newline to stdout, but with the future import they just do nothing. I think this is mostly a non-issue, but this is how I read the question. – Sven Marnach Oct 17 '18 at 12:44
  • I assume that it treats in that case `print` as a function object (which I overriden), just like in case of `a = print` but without a variable to assign to. So my goal is basically to prevent `print` function from being 'assignable'. It's really small project so I'm not entirely convinced if setting up pylint and writing custom plugin would be worth the hustle. Reviews are fine as long as everyone remember about this convention and I will soon leave this project. The implementation works for most cases, except the one corner case, so I wondered if it's doable. – Dluzak Oct 17 '18 at 13:09
  • @SvenMarnach, yes, that is exactly what I intended. – Dluzak Oct 17 '18 at 13:29
  • @SvenMarnach: right, that wasn't clear. The moment you use `from __future__ import print_function` the `print` statement is *gone*. Bare `print` entries in the code are just references to an object named `print`. – Martijn Pieters Oct 17 '18 at 13:36
  • @Dluzak: you can't prevent rebinding of `print` either, no. – Martijn Pieters Oct 17 '18 at 13:37
  • @MartijnPieters thanks, for the answer, I hoped it's possible with such flexible language like Python. If you could embed the that it's impossible to prevent rebinding of the function into the answer, I would accept it. – Dluzak Oct 18 '18 at 15:09
  • @Dluzak: it's not just rebinding, you can define your own version, or just `reload(__builtin__)` and get the original back. – Martijn Pieters Oct 18 '18 at 16:08