Reputation: 2019
Our project is getting kind of large, and the other day I ran into this problem, when I tried to add a simple error message to a function:
def create_report(id):
report = new_report(id)
if not report:
raise api_error('Could not find report with id %d' % (id,))
...
The problem? id
was a string and it crashed when it tried to format it as a number. I was not the original author of the function and wrongly assumed that id
would be a number. Instead it is supposed to be a string. Oops.
If this was a strongly typed language I would get an error from the compiler right away; what is the best way to go about these sorts of things? Should I be checking the type of every parameter (seems like a lot of legwork), or should I be putting everything in try:
blocks? Maybe we should write a comment in every function describing it's parameters? Or was I simply supposed to have known better?
Upvotes: 1
Views: 189
Reputation: 20126
I think the problem here is that you assert something about the value in the part of code which should get executed when something unexpected has happened. Maybe a better approach would be to change new_report(id)
to raise an exception if something goes wrong - specifying if it's a value error or the id couldn't be found. Then your code should be:
def create_report(id):
report = new_report(id)
...
def new_report(id):
try:
# find the report by id
# if couldn't find raise api_error
except ValueError:
# explain that id is the wrong type
Upvotes: 0
Reputation: 375574
Even better than the other answers' %s
, I find using %r
is best if the message is intended for developers' eyes. This will help you distinguish between subtle cases. For example, if you call this function with '12 '
for an id, a %s message won't show the trailing space. %r
uses the repr() of the value, and so will include the quotes, helping you to see the precise value.
Upvotes: 0
Reputation: 637
Try using %s and then using str(variable). This will make sure that everything gets converted to string (even list and tuples) and no TypeError takes place
def create_report(id):
report = new_report(id)
if not report:
raise api_error('Could not find report with id %s' % (str(id),))
...
Upvotes: 0
Reputation: 993
A possible strategy to make such things more robust regarding parameter types would be to write:
def create_report(id):
report = new_report(id)
if not report:
raise api_error('Could not find report with id %s' % (id,))
...
It won't always look nice, but at least it won't easily break.
But in general, it's always better to have some explicit contract for the function. The docstring is the right place for that.
Upvotes: 0
Reputation: 309
If you really want to make sure a string printing function doesn't crash, you're probably best off using %s, which converts the integrated value to a string using pythons str()-function. And seeing how it's an ID you're talking about, I'll assume it's of type int - in which case this solution should work out fine (If you're using floats, I'm not too sure how many digits str() will keep).
Edit Aw boy, I'm slow...
Upvotes: 0
Reputation: 184151
That format string should have used %s
, not because id
is a string but because it's the best choice generally. %d
requires a numeric type, but %s
will convert other types to strings if necessary. You should use %d
only when you need to change the numeric formatting.
Upvotes: 2