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

fix: update pydecimal algorithm to ensure left part is not generated with a leading 0 #1994

Merged
merged 2 commits into from
Feb 16, 2024
Merged

fix: update pydecimal algorithm to ensure left part is not generated with a leading 0 #1994

merged 2 commits into from
Feb 16, 2024

Conversation

alexei
Copy link
Contributor

@alexei alexei commented Feb 14, 2024

What does this change

This changes the algorithm of pydecimal to ensure the integer part if of the specified length.

What was wrong

As suggested in #1992, it is possible for pydecimal to generate a value with an integer part that's shorter than the specified length.

How this fixes it

The original algorithm generated a list of digits and joined it into an integer. However, with that approach it is possible that one or more leading digits are 0, thus ending up with a shorter value.

For example,

from faker import Faker
fake = Faker()
fake.seed_instance(15)
fake.pydecimal(left_digits=8, right_digits=3)

Generates 08023002 for left number, thus resulting in Decimal("8023002.531"). Since 8023002 is only 7 digits, it does not satisfy the requirement that the resulting value has 8 left digits.

Fixes #1992

@alexei
Copy link
Contributor Author

alexei commented Feb 14, 2024

I fear the tests I added are only relevant to the original algorithm. Let me know how to go about this.

@fcurella
Copy link
Collaborator

The issues with hardcoding a specific seed in tests is that different versions of Python may return a different value. What we usually do is set the seed to 0, and then run the same test for some number of iteration, and hope for the best 🤞🏼

@alexei
Copy link
Contributor Author

alexei commented Feb 14, 2024

I did try that initially and I gave up after a couple of million iterations. @stefan6419846 suggested to try to find a seed and that did the trick. I also noticed there were other tests that seeded the generator so I figured it was harmless.

However, as I pointed out in my previous comment, the tests are only relevant with the previous technique where digits are generated individually. With this change an integer is generated as a whole, so that bug would no longer exist. I would say it's not worth keeping the tests. Besides, the number of digits is already checked in an earlier test. These two are just to reproduce the bug I reported. What do you think?

@stefan6419846
Copy link
Contributor

My initial proposal with a fixed seed is mostly helpful for doing local debugging on your setup as once you have found a suitable seed, digging through the implementation and analyzing where the issue stems from makes the most sense. This does not necessarily need to hold true as mentioned in the previous comments in this PR for actual unit/integration tests.

@alexei
Copy link
Contributor Author

alexei commented Feb 15, 2024

There are other tests doing the same:

def test_min_value_zero_doesnt_return_negative(self):
Faker.seed("1")
result = self.fake.pydecimal(left_digits=3, right_digits=2, min_value=0, max_value=999)
self.assertGreater(result, 0)
def test_min_value_one_hundred_doesnt_return_negative(self):
Faker.seed("1")
result = self.fake.pydecimal(left_digits=3, right_digits=2, min_value=100, max_value=999)
self.assertGreater(result, 100)
def test_min_value_minus_one_doesnt_return_positive(self):
Faker.seed("5")
result = self.fake.pydecimal(left_digits=3, right_digits=2, min_value=-999, max_value=0)
self.assertLess(result, 0)
def test_min_value_minus_one_hundred_doesnt_return_positive(self):
Faker.seed("5")
result = self.fake.pydecimal(left_digits=3, right_digits=2, min_value=-999, max_value=-100)
self.assertLess(result, -100)
def test_min_value_10_pow_1000_return_greater_number(self):
Faker.seed("2")
result = self.fake.pydecimal(min_value=10**1000)
self.assertGreater(result, 10**1000)

In any case, this is focusing on the wrong question. I think I should drop the tests, as they're not necessary. If you think the tests should stay, then please either guide me so I can rewrite them in a way that fits the codebase, or change them yourselves.

@fcurella
Copy link
Collaborator

fcurella commented Feb 15, 2024 via email

@fcurella
Copy link
Collaborator

ok, I've looked into this and now I have a better understadning of what you're saying. I'd say let's drop the tests

@alexei
Copy link
Contributor Author

alexei commented Feb 15, 2024

@fcurella thanks for taking a look; I dropped the tests

Copy link
Collaborator

@fcurella fcurella left a comment

Choose a reason for hiding this comment

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

Thank you!

@fcurella fcurella merged commit 695f500 into joke2k:master Feb 16, 2024
28 checks passed
@alexei alexei deleted the fix-decimal_with_leading_zero branch February 29, 2024 13:45
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.

pydecimal(left_digits=14) produced value with 13 left digits
3 participants