Repository: joke2k/faker

Issue:
pytuple can occassionally return fewer than expected elements
* Faker version: 6.1.1
* OS: Mac OSX 10.15.5

Sporadically using pytuple may result in the tuple having less than the requested elements (`nb_elements`) even when `variable_nb_elements` is set to False.

This happens because pytuple relies on pyset, returning `tuple(self.pyset(...))`. Because it delegates to a set rather than a list, any duplicate numbers generated will result in the set, and following that the tuple, having fewer numbers than expected.

Suggest that the appropriate fix might be to use `pylist` instead of `pyset`

### Steps to reproduce

1. Specify `nb_elements = 3` in a call to pytuple, `variable_nb_elements= False`
2. Repeat until the tuple 'randomly' contains less than 3 elements  

```python
import faker

fake = faker.Faker()

for x in range(10000):
    random_tuple = fake.pytuple(nb_elements=3, variable_nb_elements=False, value_types=[int])
    assert len(random_tuple) == 3, f"Tuple {random_tuple} not len 3 at iteration {x}"
```

### Expected behavior

When calling pytuple with `nb_elements = 3` and `variable_nb_elements = False` the tuple should always contain 3 elements, even if there are duplicate values.

### Actual behavior

Sporadically the tuple contains less than 3 elements.


Discussion:
fcurella: Thank you!

Patch:
diff --git a/faker/providers/python/__init__.py b/faker/providers/python/__init__.py
index 2a0a4cc1..e6b2391f 100644
--- a/faker/providers/python/__init__.py
+++ b/faker/providers/python/__init__.py
@@ -123,7 +123,7 @@ class Provider(BaseProvider):
 
     def pytuple(self, nb_elements=10, variable_nb_elements=True, value_types=None, *allowed_types):
         return tuple(
-            self.pyset(
+            self._pyiterable(
                 nb_elements,
                 variable_nb_elements,
                 value_types,


Test patch:
diff --git a/tests/providers/test_python.py b/tests/providers/test_python.py
index e42bc53f..263c0505 100644
--- a/tests/providers/test_python.py
+++ b/tests/providers/test_python.py
@@ -195,6 +195,21 @@ class TestPython(unittest.TestCase):
         with self.assertRaises(AssertionError):
             self.factory.pystr(min_chars=5, max_chars=5)
 
+    def test_pytuple(self):
+        with warnings.catch_warnings(record=True) as w:
+            some_tuple = Faker().pytuple()
+            assert len(w) == 0
+        assert some_tuple
+        assert isinstance(some_tuple, tuple)
+
+    def test_pytuple_size(self):
+        def mock_pyint(self, *args, **kwargs):
+            return 1
+
+        with patch('faker.providers.python.Provider.pyint', mock_pyint):
+            some_tuple = Faker().pytuple(nb_elements=3, variable_nb_elements=False, value_types=[int])
+            assert some_tuple == (1, 1, 1)
+
     def test_pylist(self):
         with warnings.catch_warnings(record=True) as w:
             some_list = self.factory.pylist()