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

Stricter type checking for Python integers and floats #1554

Merged
merged 3 commits into from
Jun 2, 2023

Conversation

heinrich5991
Copy link
Contributor

The error messages mirror the ones used in Python itself. E.g.:

>>> chr(1.0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer
>>> class A:
...     def __index__(self):
...             return 1.0
...
>>> chr(A())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __index__ returned non-int (type float)

and

>>> math.sqrt("1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must be real number, not str
>>> class A:
...     def __float__(self):
...             return "1"
...
>>> math.sqrt(A())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: A.__float__ returned non-float (type str)

Fixes #1544.

Also add some tests checking the stricter type checking plus a few floating point roundtrip tests.

@heinrich5991 heinrich5991 requested a review from a team as a code owner May 23, 2023 21:37
@heinrich5991 heinrich5991 requested review from jhugman and removed request for a team May 23, 2023 21:37
@heinrich5991
Copy link
Contributor Author

This is a breaking change and should probably get a CHANGELOG entry, if it were accepted.

@jhugman jhugman requested a review from a team May 23, 2023 22:02
Copy link
Contributor

@bendk bendk left a comment

Choose a reason for hiding this comment

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

I love the deletion of all the coerce() calls, but I'm not sure about how to implement the check.

return value

@classmethod
def write(cls, value, buf):
cls.writeChecked(value, buf)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not understanding the need for a separate writeChecked method. What if this stayed as just write()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
cls.writeChecked(value, buf)
cls.writeChecked(cls.check(value), buf)

It's missing a cls.check call around the value. Seems like I'm missing some tests…

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added that cls.check call.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ahh, I understand now. What about write_unchecked instead? That follows the Rust meaning of "unchecked" and also follows PEP 8.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What about write_unchecked instead?

write_unchecked/writeUnchecked sounds good.

and also follows PEP 8.

I also prefer that naming style. I tried to fit in though; most of the file is using camelCase, e.g. all of RustBuffer's methods: allocWithBuilder, consumeWithStream, … Should I still change it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed it to writeUnchecked for now. If you want to have it as write_unchecked instead, please say so.

Copy link
Member

Choose a reason for hiding this comment

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

I agree what you have now is more consistent with the existing style, even though pep-8 does recommend otherwise.

@heinrich5991 heinrich5991 force-pushed the pr_less_coercing branch 2 times, most recently from 6fbd1e9 to 39ee669 Compare May 24, 2023 16:16
Copy link
Member

@mhammond mhammond left a comment

Choose a reason for hiding this comment

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

This seems fine to me

try:
value = value.__index__()
except Exception:
raise TypeError("'{}' object cannot be interpreted as an integer".format(type(value).__name__))
Copy link
Member

Choose a reason for hiding this comment

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

As an aside, I'd probably be in favour of declaring we only work with 3.6 and up (even 3.7 TBH, seeing 3.6 is getting no security updates) which would mean f-strings could be used.

Copy link
Member

Choose a reason for hiding this comment

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

side-note: Glean supports 3.6 and that's also the minimum for mozilla-central.

return value

@classmethod
def write(cls, value, buf):
cls.writeChecked(value, buf)
Copy link
Member

Choose a reason for hiding this comment

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

I agree what you have now is more consistent with the existing style, even though pep-8 does recommend otherwise.

@heinrich5991
Copy link
Contributor Author

What's still missing to get this merged? @bendk

@badboy
Copy link
Member

badboy commented May 31, 2023

What's still missing to get this merged? @bendk

I'll give this a look tomorrow

@mhammond
Copy link
Member

TBH I think it's ready and the only reason I didn't merge it is because Ben explicitly requested changes and I didn't want to speak for whether he thought the updates were ok.

Copy link
Member

@badboy badboy left a comment

Choose a reason for hiding this comment

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

Code looks good. I'll check with Ben.

@heinrich5991 In the meantime can you add the changelog entry?

@heinrich5991
Copy link
Contributor Author

heinrich5991 commented Jun 1, 2023

@heinrich5991 In the meantime can you add the changelog entry?

Done. EDIT: Now also conflicts resolved. EDIT2: Now also rebase mistake fixed.

Kotlin and Swift don't need it either way because they're statically
typed. By moving it directly to the lowering/writing, it avoids a bunch
of no-ops when no coercing is actually needed, e.g. when recursively
coercing lists.
The error messages mirror the ones used in Python itself. E.g.:

```python
>>> chr(1.0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'float' object cannot be interpreted as an integer
>>> class A:
...     def __index__(self):
...             return 1.0
...
>>> chr(A())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __index__ returned non-int (type float)
```
and
```python
>>> math.sqrt("1")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must be real number, not str
>>> class A:
...     def __float__(self):
...             return "1"
...
>>> math.sqrt(A())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: A.__float__ returned non-float (type str)
```

Fixes mozilla#1544.

Also add some tests checking the stricter type checking plus a few
floating point roundtrip tests.
Copy link
Contributor

@bendk bendk left a comment

Choose a reason for hiding this comment

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

Looks good to me. I agree it's better to match the current naming style. We can tackle the PEP-8 update in a different PR.

@badboy badboy merged commit 154739d into mozilla:main Jun 2, 2023
@badboy
Copy link
Member

badboy commented Jun 2, 2023

Thank you, @heinrich5991!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lossy, silent floatint conversion for int* parameters in Python
4 participants