0

I have a Flask app running that kept producing a weird bug in production. For a route, I check a user's role and their ID against the requested resource before returning a result.

Locally, I had the following line which works on the dev server as well as passes tests:

# user_id is a param passed to the Flask route
if current_user.usertype_id == 1 or current_user.id is user_id:
    # do something
else:
    abort(401)

In production, this would throw a 401 error every time, even with a logged in user. So, I changed it to check for equality only:

# user_id is a param passed to the Flask route
if current_user.usertype_id == 1 or current_user.id == user_id:
    # do something
else:
    abort(401)

...and that solved the unauthorized error.

I understand the difference between is and ==, so my question is why doesn't this throw an error locally? Nothing has changed, so I would have expected tests to catch this, but nothing failed in the unit tests or when I was testing manually in the browser.

Brian
  • 4,274
  • 2
  • 27
  • 55
  • 4
    Because you should *never expect anything in particular with `is`*, the runtime is free to optimize certain immutable objects and not recreate them where you might expect. Using `is` here *was always wrong semantically* it just *happened* to behave in a certain way, but that's not something you should have ever relied on. – juanpa.arrivillaga Aug 19 '21 at 15:38
  • 1
    what datatype are `current_user.id` and `user_id`? – David Culbreth Aug 19 '21 at 15:38
  • 1
    Does this answer your question? [Is there a difference between "==" and "is"?](https://stackoverflow.com/questions/132988/is-there-a-difference-between-and-is) – David Culbreth Aug 19 '21 at 15:39
  • 4
    For example, in your local testing, maybe all your ID's were small integers, which the CPython runtime caches (as an optimization/implementation detail, again, not something you can rely on) – juanpa.arrivillaga Aug 19 '21 at 15:41

1 Answers1

5

The reason why you are experiencing different behavior with is is that user IDs are small in local, but large in production.

To fix it, compare integers by value with ==.

It's just an implementation detail that for small integers, is also returns True:

assert int('5') is int('5')
assert not (int('555') is int('555'))
pts
  • 80,836
  • 20
  • 110
  • 183
  • Yep, didn't know that. On prod, it was for users with IDs in the 100's, but not my account when I changed my user type while I was trying to debug. Thank you. – Brian Aug 19 '21 at 16:23