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

bpo-46301: cover uncomparable values in Enum._convert_ #30472

Merged
merged 2 commits into from
Jan 8, 2022

Conversation

sobolevn
Copy link
Member

@sobolevn sobolevn commented Jan 7, 2022

@@ -4477,6 +4485,19 @@ def test_convert(self):
and name not in dir(IntEnum)],
[], msg='Names other than CONVERT_TEST_* found.')

def test_convert_uncomparable(self):
uncomp = enum.Enum._convert_(
Copy link
Member Author

Choose a reason for hiding this comment

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

Moreover, _convert_ was not tested on plain Enum

Copy link
Member

@ethanfurman ethanfurman Jan 7, 2022

Choose a reason for hiding this comment

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

_convert_ was not intended to be called on plain Enum -- it's purpose it to convert existing constants in a module to enums, and existing constants will already be str or int (or possibly something else, like complex).

Copy link
Member

@ethanfurman ethanfurman Jan 7, 2022

Choose a reason for hiding this comment

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

Change the UNCOMPARABLE_* values as suggested above, and also add a test using a complex type.

@sobolevn
Copy link
Member Author

sobolevn commented Jan 7, 2022

@AlexWaygood can you please add "skip news" to this PR? I can't add labels myself 🙂

@@ -4440,6 +4440,14 @@ def test__all__(self):
CONVERT_STRING_TEST_NAME_E = 5
CONVERT_STRING_TEST_NAME_F = 5

# We also need values that cannot be compared:
Copy link
Member

Choose a reason for hiding this comment

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

All we need for this test is for UNCOMPARABLE_* to be different types of values:

UNCOMPABLE_A = 5
UNCOMPABLE_C = (9, 4)
UNCOMPABLE_B = 'hello'

@@ -4477,6 +4485,19 @@ def test_convert(self):
and name not in dir(IntEnum)],
[], msg='Names other than CONVERT_TEST_* found.')

def test_convert_uncomparable(self):
uncomp = enum.Enum._convert_(
Copy link
Member

@ethanfurman ethanfurman Jan 7, 2022

Choose a reason for hiding this comment

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

Change the UNCOMPARABLE_* values as suggested above, and also add a test using a complex type.

@bedevere-bot
Copy link

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

And if you don't make the requested changes, you will be put in the comfy chair!

@sobolevn
Copy link
Member Author

sobolevn commented Jan 8, 2022

Thanks! I've added one more case with complex and simplified UNCOMPARABLE_ defs.

I have made the requested changes; please review again

@bedevere-bot
Copy link

Thanks for making the requested changes!

@ethanfurman: please review the changes made to this pull request.

@sobolevn
Copy link
Member Author

sobolevn commented Jan 8, 2022

Thank you! 👍
Do we need to backport this?

@pablogsal
Copy link
Member

Seems that this PR has broken the refleak buildbots:

https://buildbot.python.org/all/#/builders/320/builds/269/
https://buildbot.python.org/all/#/builders/384/builds/255

According to our buildbot policy, if this is not fixed in 24h we will need to revert

@sobolevn
Copy link
Member Author

sobolevn commented Jan 9, 2022

@pablogsal thanks for the info! I would love to help to get this fixed, but I am not familiar with how refleak works. And since this is a test-only PR I am bit surprised that this happened 🤔

test_enum leaked [220, 220, 220] references, sum=660
test_enum leaked [56, 56, 56] memory blocks, sum=168
.test test_unittest failed -- multiple errors occurred; run in verbose mode for details
0:53:31 load avg: 0.00 Re-running failed tests in verbose mode
0:53:31 load avg: 0.00 Re-running test_enum in verbose mode
test_enum leaked [220, 220, 220] references, sum=660
test_enum leaked [56, 56, 56] memory blocks, sum=168

From https://buildbot.python.org/all/#/builders/384/builds/255 🤔

@sobolevn
Copy link
Member Author

sobolevn commented Jan 9, 2022

I was able to reproduce this locally on macos with ./python.exe -m test -R : -v test_enum

test_enum leaked [220, 220, 220, 220] references, sum=880
test_enum leaked [56, 56, 56, 56] memory blocks, sum=224
test_enum failed (reference leak) in 39.1 sec

== Tests result: FAILURE ==

1 test failed:
    test_enum

Total duration: 39.1 sec
Tests result: FAILURE

@sobolevn
Copy link
Member Author

sobolevn commented Jan 9, 2022

PYTHONDUMPREFSFILE=ex.txt ./python.exe -m test -R : -v test_enum produces this:
ex.txt

@pablogsal
Copy link
Member

I am not at my computer so I cannot test, but the problem that happens in these cases where only tests are added is that the new tests are either modifying the global state or adding some sort of cache.

@sobolevn
Copy link
Member Author

sobolevn commented Jan 9, 2022

Ok, looks like I've found the problem:

body['__module__'] = module

When I comment this line out - tests pass, with it - we have a refleak.

@pablogsal
Copy link
Member

Ok, looks like I've found the problem:

Yeah, this is an example of "modifying the global state". Tests should be idempotent, and they should leave everything as it was before

@sobolevn
Copy link
Member Author

sobolevn commented Jan 9, 2022

I am not at my computer so I cannot test, but the problem that happens in these cases where only tests are added is that the new tests are either modifying the global state or adding some sort of cache.

Yes, you are right! These tests do modify the global state. They add a new class to the current module.

Yeah, this is an example of "modifying the global state". Tests should be idempotent, and they should leave everything as it was before

But this line is from enum.py, not from tests 🤔

Either way, I will open a new PR with test-with-buildbots later today. I hope that I will be able to fix it!

@ethanfurman
Copy link
Member

The line body['__module__'] = module is not modifying global state: body is a dict that will be used to create a new enum class.

The clue was good, though -- the global constants that are converted into enums need to be reset to their original values, either before or after the test runs.

ethanfurman added a commit that referenced this pull request Jan 10, 2022
@ethanfurman
Copy link
Member

ethanfurman commented Jan 10, 2022

@sobolevn add

    global COMPLEX_A, COMPLEX_B, COMPLEX_C

to the top of test_convert_complex, and

    COMPLEX_A = 2j
    COMPLEX_B = 3j
    COMPLEX_C = 1j

to the bottom. Make similar changes to test_convert_uncomparable.

@sobolevn
Copy link
Member Author

Oh, sorry, I've missed your comment in the morning 🙂
I went with .swap_item technique. This ensures that we don't polute real modules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
skip news tests Tests in the Lib/test dir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants