Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dice: invalid dice rolls now gives a more friendly error #1386

Merged
merged 2 commits into from
Dec 17, 2018

Conversation

Hanicef
Copy link
Contributor

@Hanicef Hanicef commented Sep 25, 2018

Another side note is that i used SyntaxError rather than the all-encompassing Exception, as some exceptions should be handled different. As far as I know, though, eval_equation() only throws SyntaxError.
Fixes #1189

@dgw dgw added the Tweak label Sep 26, 2018
@dgw dgw added this to the 6.6.0 milestone Sep 26, 2018
@dgw
Copy link
Member

dgw commented Sep 26, 2018

Catching specific exceptions: Yay! But in some cases eval_equation() could throw sopel.tools.calculation.ExpressionEvaluator.Error instead of SyntaxError. If you follow the code far enough into Python itself, a sufficiently invalid expression could also generate TypeError or ValueError depending on the specific Python version used. (Not likely, because AFAIK this last case triggers only on a null byte, but for completeness…)

Aside from nitpicking which exception types should be caught, there's also the matter of error wording. The phrase "a weird dice" is grammatically incorrect ("dice" is plural), and "How do I throw it?" would be wrong unless the user gives only one die (the singular form of "dice"). What about one of these alternatives, as a start? Just to avoid Sopel referencing the number of dice.

  • I don't know how to roll that.
  • Are you sure I can roll that?
  • Hmm, that didn't work. If you want me to roll dice, I'm looking for XdY.

Something along those lines. I'm not necessarily perfectly happy with any of those, but I would accept one of them if we can't come up with something better.

@Hanicef
Copy link
Contributor Author

Hanicef commented Sep 26, 2018

Yeah, you're right with the grammar. I'll have to think of something more clever.
On the topic of the exceptions, though, I honestly can't see TypeError occuring. Quoting from the Python documentation (https://docs.python.org/3/library/exceptions.html#TypeError):

Raised when an operation or function is applied to an object of inappropriate type. The associated value is a string giving details about the type mismatch.

Since there is no way to send types other than str, which is the type expected, it's theoretically impossible for this exception to be raised, assuming all underlying functions follow this practice.
I will, however, catch ValueError, as that is explicitly thrown by eval_equation on equations that would be ridiculously huge.

@dgw
Copy link
Member

dgw commented Sep 26, 2018

Catching TypeError is relevant because of the behavior of the built-in compile() in Python < 3.5:

Changed in version 3.5: Previously, TypeError was raised when null bytes were encountered in source.
https://docs.python.org/3/library/functions.html#compile

So, while in Python 3.5+ those conditions raise ValueError, it can raise TypeError in older versions of Python. Since Sopel supports 2.7+ & 3.4+, we have to account for that change.

@Hanicef
Copy link
Contributor Author

Hanicef commented Sep 26, 2018

Yeah, you're right about that. I added it to the exception catching, but since IRC does not allow NUL in strings, this should still not happen (See RFC-1459).
On the other hand, SyntaxError is not guranteed to occur due to invalid dice, but can also occur due to an invalid expression, so I made the error message more generic.

EDIT: Why the assertion error in test_tools.py? Do you know what I did wrong?

@dgw
Copy link
Member

dgw commented Sep 26, 2018

The test failure's not your fault. Looks like one of the DuckDuckGo examples changed in the last several hours. I'll fix it and rerun the tests before merging to get a check-mark on this PR when it's ready. (Or maybe it will fix itself, who knows. The search module tests are annoyingly flaky.)

I think I like the new error messages. Just don't quite understand where "as well as the algorithms" comes from for SyntaxError. Love that you removed the old "Showing the actual error" comment; meant to mention that and forgot, so well done.

It's probably worth handling sopel.tools.calculation.ExpressionEvaluator.Error as well, since that's explicitly thrown if evaluating takes too long, or if the expression contains unimplemented operators. (You can import it as from sopel.tools.calculation.ExpressionEvaluator import Error as ExpressionEvaluatorError, or similar, so it looks a bit neater. I don't really mind giving it a more descriptive name, and in fact it should probably get renamed to something more specific in a future version.)

@Hanicef
Copy link
Contributor Author

Hanicef commented Sep 26, 2018

What I mean with "as well as the algorithms" is that 3 + 4 is technically a valid dice roll, despite not being a dice. What I realized was that dice are converted to integers, and then passed on to eval_equation, so the exception for SyntaxError isn't strictly related to dice being invalid, but the algorithm being invalid. Consequently, the algorithm is invalid if the dice is invalid, hence the description.

Copy link
Member

@dgw dgw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a digression on the ways in which the existing structure of sopel.tools.calculation has managed to disappoint me to the line notes.

tl;dr: this PR is 👍 as-is.

bot.reply("You roll %s: %s = very big" % (
trigger.group(2), pretty_str))
return
except (SyntaxError, eval_equation.Error):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reference to sopel.tools.calculation.ExpressionEvaluator.Error is weird done this way, but it's not really possible to do it more conventionally due to the nested classes. Trust me, I tried! 😹

Frustratingly, doing e.g. from sopel.tools.calculation.ExpressionEvaluator import Error as ExpressionEvaluationError doesn't work. Nor does from sopel.tools.calculation import ExpressionEvaluator.Error as ExpressionEvaluationError. That's invalid syntax.

I'm not up for refactoring sopel.tools.calculation any time soon, just to "fix" this one except statement to look more conventional, so the module and this PR will do as-is. Maybe a future Sopel release (major version) will eventually make it so the calculation tools actually provides a proper ExpressionEvaluationError that can be imported on its own for catching exceptions… but that's definitely not happening in the point release that will include this PR.

@dgw dgw merged commit 4bf71cf into sopel-irc:master Dec 17, 2018
dgw added a commit that referenced this pull request Oct 28, 2023
Python 3.5 changed `compile()` (which `eval_equation()` uses, far down
the call stack) to raise `ValueError` in the case that used to raise a
`TypeError`, so that block isn't necessary any more.

See https://docs.python.org/3.8/library/functions.html#compile and
#1386 (comment)

Input that doesn't contain any dice expressions could still be processed
and wind up at the wrong error handler; added an early-return for that.
(An expression without dice rolls should be evaluated using `.calc`. And
yes, I'm aware that this could be someone's spacebar heater. Tough!)

The tests also now specifically cover an expression with no number of
dice (to verify that the default of 1 die works), and cases where the
input contains one or more valid dice expression(s) but has invalid
syntax somewhere else in the string.
dgw added a commit that referenced this pull request Oct 29, 2023
Python 3.5 changed `compile()` (which `eval_equation()` uses, far down
the call stack) to raise `ValueError` in the case that used to raise a
`TypeError`, so that block isn't necessary any more.

See https://docs.python.org/3.8/library/functions.html#compile and
#1386 (comment)

Input that doesn't contain any dice expressions could still be processed
and wind up at the wrong error handler; added an early-return for that.
(An expression without dice rolls should be evaluated using `.calc`. And
yes, I'm aware that this could be someone's spacebar heater. Tough!)

The tests also now specifically cover an expression with no number of
dice (to verify that the default of 1 die works), and cases where the
input contains one or more valid dice expression(s) but has invalid
syntax somewhere else in the string.
dgw added a commit that referenced this pull request Oct 31, 2023
Python 3.5 changed `compile()` (which `eval_equation()` uses, far down
the call stack) to raise `ValueError` in the case that used to raise a
`TypeError`, so that block isn't necessary any more.

See https://docs.python.org/3.8/library/functions.html#compile and
#1386 (comment)

Input that doesn't contain any dice expressions could still be processed
and wind up at the wrong error handler; added an early-return for that.
(An expression without dice rolls should be evaluated using `.calc`. And
yes, I'm aware that this could be someone's spacebar heater. Tough!)

The tests also now specifically cover an expression with no number of
dice (to verify that the default of 1 die works), and cases where the
input contains one or more valid dice expression(s) but has invalid
syntax somewhere else in the string.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants