Repository: joke2k/faker

Issue:
Empty range bug with pyfloat
* Faker version: 2.0.3
* OS: macOS 10.14.6 (Mojave)

Calling `pyfloat` with `min_value` or `max_value` can result in both being set to the same thing, and causing errors. May have been introduced by #995.

### Steps to reproduce
```
for _ in range(1000):
    faker.pyfloat(max_value=9999)
```

You may have to try this a few times, or increase the range

### Expected behavior

Faker should handle this gracefully

### Actual behavior

`ValueError: empty range for randrange() (9999,9999, 0)`



Discussion:

Resolving patch:
diff --git a/faker/providers/python/__init__.py b/faker/providers/python/__init__.py
--- a/faker/providers/python/__init__.py
+++ b/faker/providers/python/__init__.py
@@ -34,7 +34,6 @@ def pystr(self, min_chars=None, max_chars=20):
 
     def pyfloat(self, left_digits=None, right_digits=None, positive=False,
                 min_value=None, max_value=None):
-
         if left_digits is not None and left_digits < 0:
             raise ValueError(
                 'A float number cannot have less than 0 digits in its '
@@ -48,6 +47,8 @@ def pyfloat(self, left_digits=None, right_digits=None, positive=False,
                 'A float number cannot have less than 0 digits in total')
         if None not in (min_value, max_value) and min_value > max_value:
             raise ValueError('Min value cannot be greater than max value')
+        if None not in (min_value, max_value) and min_value == max_value:
+            raise ValueError('Min and max value cannot be the same')
 
         left_digits = left_digits if left_digits is not None else (
             self.random_int(1, sys.float_info.dig))
@@ -55,12 +56,7 @@ def pyfloat(self, left_digits=None, right_digits=None, positive=False,
             self.random_int(0, sys.float_info.dig - left_digits))
         sign = ''
         if (min_value is not None) or (max_value is not None):
-            if min_value is None:
-                min_value = max_value - self.random_int()
-            if max_value is None:
-                max_value = min_value + self.random_int()
-
-            left_number = self.random_int(min_value, max_value - 1)
+            left_number = self._safe_random_int(min_value, max_value)
         else:
             sign = '+' if positive else self.random_element(('+', '-'))
             left_number = self.random_number(left_digits)
@@ -71,6 +67,19 @@ def pyfloat(self, left_digits=None, right_digits=None, positive=False,
             self.random_number(right_digits),
         ))
 
+    def _safe_random_int(self, min_value, max_value):
+        orig_min_value = min_value
+        orig_max_value = max_value
+
+        if min_value is None:
+            min_value = max_value - self.random_int()
+        if max_value is None:
+            max_value = min_value + self.random_int()
+        if min_value == max_value:
+            return self._safe_random_int(orig_min_value, orig_max_value)
+        else:
+            return self.random_int(min_value, max_value - 1)
+
     def pyint(self, min_value=0, max_value=9999, step=1):
         return self.generator.random_int(min_value, max_value, step=step)
 


Test patch:
diff --git a/tests/test_factory.py b/tests/test_factory.py
--- a/tests/test_factory.py
+++ b/tests/test_factory.py
@@ -487,6 +487,18 @@ def test_negative_pyfloat(self):
         assert any(factory.pyfloat(left_digits=0, positive=False) < 0 for _ in range(100))
         assert any(factory.pydecimal(left_digits=0, positive=False) < 0 for _ in range(100))
 
+    def test_pyfloat_empty_range_error(self):
+        # tests for https://github.com/joke2k/faker/issues/1048
+        factory = Faker()
+        factory.seed_instance(8038)
+        assert factory.pyfloat(max_value=9999) < 9999
+
+    def test_pyfloat_same_min_max(self):
+        # tests for https://github.com/joke2k/faker/issues/1048
+        factory = Faker()
+        with pytest.raises(ValueError):
+            assert factory.pyfloat(min_value=9999, max_value=9999)
+
     def test_us_ssn_valid(self):
         from faker.providers.ssn.en_US import Provider