nebius/SWE-bench-extra huggingface dataset:
Repository: jmoiron/humanize

Issue:
humanize.apnumber(0) should return zero
`humanize.appnumber` returns 0 for 0, while (I think) it should return 'zero':

```pycon
>>> import humanize
>>> humanize.apnumber(0)
'0'
>>> humanize.apnumber(1)
'one'
>>> humanize.apnumber(2)
'two'
>>>
```

Comments:

Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 225fdc9..7fa46ec 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -2,6 +2,6 @@ Fixes #
 
 Changes proposed in this pull request:
 
- * 
- * 
- * 
+* 
+* 
+* 
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 50a676f..3883da1 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
 repos:
   - repo: https://github.com/asottile/pyupgrade
-    rev: v2.0.1
+    rev: v2.1.0
     hooks:
       - id: pyupgrade
         args: ["--py3-plus"]
@@ -18,7 +18,7 @@ repos:
         additional_dependencies: [flake8-2020, flake8-implicit-str-concat]
 
   - repo: https://github.com/asottile/seed-isort-config
-    rev: v1.9.4
+    rev: v2.1.0
     hooks:
       - id: seed-isort-config
 
diff --git a/src/humanize/number.py b/src/humanize/number.py
index ffe257f..f57588e 100644
--- a/src/humanize/number.py
+++ b/src/humanize/number.py
@@ -106,16 +106,23 @@ def intword(value, format="%.1f"):
 
 
 def apnumber(value):
-    """For numbers 1-9, returns the number spelled out. Otherwise, returns the
-    number. This follows Associated Press style.  This always returns a string
-    unless the value was not int-able, unlike the Django filter."""
+    """Converts an integer to Associated Press style.
+
+    Args:
+        value (int, float, string): Integer to convert.
+
+    Returns:
+        str: For numbers 0-9, the number spelled out. Otherwise, the number. This always
+        returns a string unless the value was not int-able, unlike the Django filter.
+    """
     try:
         value = int(value)
     except (TypeError, ValueError):
         return value
-    if not 0 < value < 10:
+    if not 0 <= value < 10:
         return str(value)
     return (
+        _("zero"),
         _("one"),
         _("two"),
         _("three"),
@@ -125,7 +132,7 @@ def apnumber(value):
         _("seven"),
         _("eight"),
         _("nine"),
-    )[value - 1]
+    )[value]
 
 
 def fractional(value):


Test patch:
diff --git a/tests/test_number.py b/tests/test_number.py
index 4b827d3..cdb0d7c 100644
--- a/tests/test_number.py
+++ b/tests/test_number.py
@@ -86,6 +86,7 @@ def test_intword(test_args, expected):
 @pytest.mark.parametrize(
     "test_input, expected",
     [
+        (0, "zero"),
         (1, "one"),
         (2, "two"),
         (4, "four"),
Repository: jmoiron/humanize

Issue:
precisedelta with minimum unit seconds fails when seconds is a very small value
### What did you do?

Calculated a small delta. 
```python
import humanize
from datetime import timedelta
duration_seconds = 1e-06       # A small value, or 0
delta = timedelta(seconds=duration_seconds)
output = humanize.precisedelta(delta, minimum_unit="minutes")
print(output)
```

### What did you expect to happen?

Output would be 0 seconds. 

Alternatively, a better error message or a warning in the documentation would be helpful. 

### What actually happened?

```
>       tail = texts[-1]
E       IndexError: list index out of range
```

### What versions are you using?

* OS: ubuntu
* Python: 3.6.12
* Humanize: 2.6.0




Comments:

Patch:
diff --git a/src/humanize/time.py b/src/humanize/time.py
index 319d7e4..e40c482 100644
--- a/src/humanize/time.py
+++ b/src/humanize/time.py
@@ -426,6 +426,20 @@ def precisedelta(value, minimum_unit="seconds", suppress=(), format="%0.2f"):
     >>> precisedelta(delta, suppress=['seconds', 'milliseconds', 'microseconds'])
     '1.50 minutes'
 
+    ```
+
+    If the delta is too small to be represented with the minimum unit,
+    a value of zero will be returned:
+
+    ```pycon
+    >>> delta = dt.timedelta(seconds=1)
+    >>> precisedelta(delta, minimum_unit="minutes")
+    '0.02 minutes'
+
+    >>> delta = dt.timedelta(seconds=0.1)
+    >>> precisedelta(delta, minimum_unit="minutes")
+    '0 minutes'
+
     ```
     """
     date, delta = date_and_delta(value)
@@ -501,7 +515,7 @@ def precisedelta(value, minimum_unit="seconds", suppress=(), format="%0.2f"):
     texts = []
     for unit, fmt in zip(reversed(Unit), fmts):
         singular_txt, plural_txt, value = fmt
-        if value > 0:
+        if value > 0 or (not texts and unit == min_unit):
             fmt_txt = ngettext(singular_txt, plural_txt, value)
             if unit == min_unit and math.modf(value)[0] > 0:
                 fmt_txt = fmt_txt.replace("%d", format)


Test patch:
diff --git a/tests/test_time.py b/tests/test_time.py
index 90c0130..f0311ce 100644
--- a/tests/test_time.py
+++ b/tests/test_time.py
@@ -496,6 +496,11 @@ def test_precisedelta_one_unit_enough(val, min_unit, expected):
             "microseconds",
             "1 year, 5 days and 2 seconds",
         ),
+        (
+            dt.timedelta(seconds=0.01),
+            "minutes",
+            "0 minutes",
+        ),
     ],
 )
 def test_precisedelta_multiple_units(val, min_unit, expected):
Repository: jmwri/ioccontainer

Issue:
Inject should override value if None
When using the `@inject` decorator, the provided parameter should be overridden if it is `None`. At the moment the provider parameter takes precedence 100% of the time.

Comments:

Patch:
diff --git a/ioccontainer/inject.py b/ioccontainer/inject.py
index 162f6e1..98ba622 100644
--- a/ioccontainer/inject.py
+++ b/ioccontainer/inject.py
@@ -50,7 +50,11 @@ def inject_decorator(container: 'Container'):
             service = container.get(cls)
 
             if _is_positional_argument(position, parameter, new_args):
-                new_args.append(service)
+                if len(new_args) >= position + 1:
+                    new_args[position] = service
+                else:
+                    new_args.append(service)
+
             elif _is_keyword_argument(parameter):
                 kwargs[parameter.name] = service
             else:
@@ -96,7 +100,9 @@ def _default_parameter_provided(parameter: inspect.Parameter) -> bool:
 
 def _argument_provided(position: int, parameter: inspect.Parameter,
                        args: typing.List, kwargs: typing.Dict) -> bool:
-    return position < len(args) or parameter.name in kwargs.keys()
+    if position < len(args) and args[position] is not None:
+        return True
+    return kwargs.get(parameter.name) is not None
 
 
 def _is_positional_argument(
@@ -106,7 +112,9 @@ def _is_positional_argument(
                         inspect.Parameter.POSITIONAL_OR_KEYWORD)
     if parameter.kind not in positional_types:
         return False
-    return position == len(args)
+    if position == len(args):
+        return True
+    return position + 1 == len(args) and args[position] is None
 
 
 def _is_keyword_argument(parameter: inspect.Parameter) -> bool:


Test patch:
diff --git a/tests/test_container.py b/tests/test_container.py
index d76d2e1..f338f75 100644
--- a/tests/test_container.py
+++ b/tests/test_container.py
@@ -148,3 +148,29 @@ def test_injection_to_constructor():
     my_class = MyClass('my_test_string')
     assert my_class.some_str is 'my_test_string'
     assert my_class.get_val() is 1
+
+
+@provider('str_service')
+def provide_str():
+    return 'string service'
+
+
+def test_param_overriding():
+    @inject(string='str_service')
+    def my_fn(string):
+        return string
+
+    assert my_fn() == 'string service'
+    assert my_fn('overridden') == 'overridden'
+    assert my_fn(None) == 'string service'
+
+
+def test_multiple_param_overriding():
+    @inject(s1='str_service', s2='str_service')
+    def my_fn(s1, s2):
+        return s1, s2
+
+    assert my_fn() == ('string service', 'string service')
+    assert my_fn('overridden') == ('overridden', 'string service')
+    assert my_fn(None) == ('string service', 'string service')
+    assert my_fn('overridden', None) == ('overridden', 'string service')
Repository: joanvila/aioredlock

Issue:
A function to return number of active locks
Would be a nice to have a feature to do that.

Comments:

Patch:
diff --git a/aioredlock/algorithm.py b/aioredlock/algorithm.py
index bf192ab..51707b7 100644
--- a/aioredlock/algorithm.py
+++ b/aioredlock/algorithm.py
@@ -238,3 +238,18 @@ class Aioredlock:
         self._watchdogs.clear()
 
         await self.redis.clear_connections()
+
+    async def get_active_locks(self):
+        """
+        Return all stored locks that are valid.
+
+        .. note::
+            This function is only really useful in learning if there are no
+            active locks. It is possible that by the time the a lock is
+            returned from this function that it is no longer active.
+        """
+        ret = []
+        for lock in self._locks.values():
+            if lock.valid is True and await lock.is_locked():
+                ret.append(lock)
+        return ret
diff --git a/aioredlock/lock.py b/aioredlock/lock.py
index a44b84c..58f5b09 100644
--- a/aioredlock/lock.py
+++ b/aioredlock/lock.py
@@ -21,3 +21,6 @@ class Lock:
 
     async def release(self):
         await self.lock_manager.unlock(self)
+
+    async def is_locked(self):
+        return await self.lock_manager.is_locked(self)


Test patch:
diff --git a/tests/ut/conftest.py b/tests/ut/conftest.py
index 7acbe39..7004ad1 100644
--- a/tests/ut/conftest.py
+++ b/tests/ut/conftest.py
@@ -12,8 +12,19 @@ async def dummy_sleep(seconds):
 
 
 @pytest.fixture
-def locked_lock():
-    return Lock(None, "resource_name", 1, -1, True)
+def locked_lock(lock_manager_redis_patched):
+    lock_manager, _ = lock_manager_redis_patched
+    lock = Lock(lock_manager, "resource_name", 1, -1, True)
+    lock_manager._locks[lock.resource] = lock
+    return lock
+
+
+@pytest.fixture
+def unlocked_lock(lock_manager_redis_patched):
+    lock_manager, _ = lock_manager_redis_patched
+    lock = Lock(lock_manager, "other_resource_name", 1, -1, False)
+    lock_manager._locks[lock.resource] = lock
+    return lock
 
 
 @pytest.fixture
diff --git a/tests/ut/test_algorithm.py b/tests/ut/test_algorithm.py
index f4ef3ed..e2655c0 100644
--- a/tests/ut/test_algorithm.py
+++ b/tests/ut/test_algorithm.py
@@ -344,3 +344,13 @@ class TestAioredlock:
 
             await lock_manager.unlock(lock)
             assert lock.valid is False
+
+    @pytest.mark.asyncio
+    async def test_get_active_locks(self, lock_manager_redis_patched, locked_lock, unlocked_lock):
+        lock_manager, redis = lock_manager_redis_patched
+        redis.is_locked.return_value = True
+
+        locks = await lock_manager.get_active_locks()
+
+        assert locked_lock in locks
+        assert unlocked_lock not in locks
Repository: johnsyweb/python_sparse_list

Issue:
Deleting an empty slice from a sparse list fails
```
sl = sparse_list.SparseList(xrange(5), None)
del sl[3:3]
```

throws `IndexError: range object index out of range` whereas it should just leave the list untouched.

Comments:

Patch:
diff --git a/sparse_list.py b/sparse_list.py
index c7f6b56..e759c96 100644
--- a/sparse_list.py
+++ b/sparse_list.py
@@ -79,6 +79,9 @@ class SparseList(object):
         else:
             indices = (item, )
 
+        if not indices:
+            return
+
         offset = 0
 
         for k in sorted(self.elements.keys()):


Test patch:
diff --git a/test_sparse_list.py b/test_sparse_list.py
index d45461f..ff82f80 100755
--- a/test_sparse_list.py
+++ b/test_sparse_list.py
@@ -176,6 +176,11 @@ class TestSparseList(unittest.TestCase):
         del sl[::2]
         self.assertEqual([1, 3, 5], sl)
 
+    def test_empty_removal(self):
+        sl = sparse_list.SparseList(xrange(5), None)
+        del sl[3:3]
+        self.assertEqual([0, 1, 2, 3, 4], sl)
+
     def test_append(self):
         sl = sparse_list.SparseList(1, 0)
         sl.append(1)
Repository: joke2k/django-environ

Issue:
Support for SOME_VAR=(int, None)?
Is there a way to tell environ that `SOME_VAR=` in an env file is fine, and should lead to SOME_VAR being assigned the pythonic `None` when using the `SOME_VAR=(int, None)` format?

Comments:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 47d9673..c46d3ff 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -16,6 +16,8 @@ Added
 - Added option to override existing variables with ``read_env``
   `#103 <https://github.com/joke2k/django-environ/issues/103>`_,
   `#249 <https://github.com/joke2k/django-environ/issues/249>`_.
+- Added support for empty var with None default value
+  `#209 <https://github.com/joke2k/django-environ/issues/209>`_.
 
 
 Fixed
diff --git a/environ/environ.py b/environ/environ.py
index b678096..7cb2040 100644
--- a/environ/environ.py
+++ b/environ/environ.py
@@ -375,6 +375,8 @@ class Env:
                     not isinstance(default, NoValue):
                 cast = type(default)
 
+        value = None if default is None and value == '' else value
+
         if value != default or (parse_default and value):
             value = self.parse_value(value, cast)
 


Test patch:
diff --git a/tests/test_env.py b/tests/test_env.py
index acd8dc4..55147fb 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -86,6 +86,7 @@ class TestEnv:
 
     def test_int_with_none_default(self):
         assert self.env('NOT_PRESENT_VAR', cast=int, default=None) is None
+        assert self.env('EMPTY_INT_VAR', cast=int, default=None) is None
 
     @pytest.mark.parametrize(
         'value,variable',
diff --git a/tests/test_env.txt b/tests/test_env.txt
index befeed7..a179a48 100644
--- a/tests/test_env.txt
+++ b/tests/test_env.txt
@@ -29,6 +29,7 @@ FLOAT_STRANGE_VAR2=123.420.333,3
 FLOAT_NEGATIVE_VAR=-1.0
 PROXIED_VAR=$STR_VAR
 EMPTY_LIST=
+EMPTY_INT_VAR=
 INT_VAR=42
 STR_LIST_WITH_SPACES= foo,  bar
 STR_VAR=bar
Repository: joke2k/faker

Issue:
BBAN for en_GB too short
* Faker version: v2.0.3
* OS: linux

Numeric part of the en_GB BBAN needs to be 14 digits long, it currently only returns 13, failing further validation.

### Steps to reproduce

Invoke `fake.iban()` or `fake.bban()` with the en_GB locale, an IBAN or BBAN with 1 digit missing is returned.

### Expected behavior

GB ibans should be 22 chars long: https://www.xe.com/ibancalculator/sample/?ibancountry=united kingdom



Comments:

Patch:
diff --git a/faker/providers/bank/en_GB/__init__.py b/faker/providers/bank/en_GB/__init__.py
index 9486de78..75daaf2e 100644
--- a/faker/providers/bank/en_GB/__init__.py
+++ b/faker/providers/bank/en_GB/__init__.py
@@ -2,5 +2,5 @@ from .. import Provider as BankProvider
 
 
 class Provider(BankProvider):
-    bban_format = '????#############'
+    bban_format = '????##############'
     country_code = 'GB'


Test patch:
diff --git a/tests/providers/test_bank.py b/tests/providers/test_bank.py
index e3fc353e..c3333af1 100644
--- a/tests/providers/test_bank.py
+++ b/tests/providers/test_bank.py
@@ -45,3 +45,18 @@ class TestPlPL(unittest.TestCase):
     def test_iban(self):
         iban = self.factory.iban()
         assert re.match(r"PL\d{26}", iban)
+
+
+class TestEnGB(unittest.TestCase):
+    """Tests the bank provider for en_GB locale"""
+
+    def setUp(self):
+        self.factory = Faker('en_GB')
+
+    def test_bban(self):
+        bban = self.factory.bban()
+        assert re.match(r"[A-Z]{4}\d{14}", bban)
+
+    def test_iban(self):
+        iban = self.factory.iban()
+        assert re.match(r"GB\d{2}[A-Z]{4}\d{14}", iban)
Repository: joke2k/faker

Issue:
Why does faker.misc.uuid has `cast_to=str` by default? 
# I'm willing to open PR
should this issue be accepted

* Faker version: latest
* OS: N/A

`faker.misc.uuid` has a arg `cast_to` with `str` as default.  So the returned value is always string.
https://github.com/joke2k/faker/blob/9f48a5c6a251b2b8d9f84f65472f946b85b9a923/faker/providers/misc/__init__.py#L95

I want to have the `uuid.UUID` object returned. 

Calling with `cast_to=UUID` again raises a Error on UUID lib (of course it would). Since it's returned `UUID(UUID([...]))`from faker with this

![image](https://user-images.githubusercontent.com/3301060/80141445-f1113900-857f-11ea-85a2-2252c74acd00.png)

So I would have to cast it like this `UUID(str(UUID([...])))` to be funcional.

### Steps to reproduce

1. `faker.uuid(cast_to=str)`
1. `faker.uuid(cast_to=uuid.UUID)`

### Expected behavior
I should be able to call  `faker.uuid(cast_to=None)` and have the pure `UUID` object returned.

### Actual behavior
Faker forces object `UUID` to be cast.



Comments:

Patch:
diff --git a/faker/providers/misc/__init__.py b/faker/providers/misc/__init__.py
index c4906eff..39fb692f 100644
--- a/faker/providers/misc/__init__.py
+++ b/faker/providers/misc/__init__.py
@@ -87,12 +87,20 @@ class Provider(BaseProvider):
         return res.hexdigest()
 
     def uuid4(self, cast_to=str):
-        """Generate a random UUID4 object and cast it to another type using a callable ``cast_to``.
+        """Generate a random UUID4 object and cast it to another type if specified using a callable ``cast_to``.
 
         By default, ``cast_to`` is set to ``str``.
+
+        May be called with ``cast_to=None`` to return a full-fledged ``UUID``.
+
+        :sample:
+        :sample: cast_to=None
         """
         # Based on http://stackoverflow.com/q/41186818
-        return cast_to(uuid.UUID(int=self.generator.random.getrandbits(128), version=4))
+        generated_uuid = uuid.UUID(int=self.generator.random.getrandbits(128), version=4)
+        if cast_to is not None:
+            generated_uuid = cast_to(generated_uuid)
+        return generated_uuid
 
     def password(
             self,


Test patch:
diff --git a/tests/providers/test_misc.py b/tests/providers/test_misc.py
index 16194c2a..f616db65 100644
--- a/tests/providers/test_misc.py
+++ b/tests/providers/test_misc.py
@@ -17,7 +17,7 @@ class TestMisc(unittest.TestCase):
         self.fake = Faker()
         Faker.seed(0)
 
-    def test_uuid4(self):
+    def test_uuid4_str(self):
         uuid4 = self.fake.uuid4()
         assert uuid4
         assert isinstance(uuid4, str)
@@ -28,7 +28,7 @@ class TestMisc(unittest.TestCase):
         assert isinstance(uuid4, int)
 
     def test_uuid4_uuid_object(self):
-        uuid4 = self.fake.uuid4(cast_to=lambda x: x)
+        uuid4 = self.fake.uuid4(cast_to=None)
         assert uuid4
         assert isinstance(uuid4, uuid.UUID)
Repository: joke2k/faker

Issue:
Random sentence of length 1 fails
* Faker version: 5.5.0
* OS: Ubuntu 20.04.1

Creating a random sentence of length 1 fails because it attempts to titleize the first word of the sentence, but `random_choices`  is returning a tuple which is immutable. 

This also occurs sometimes with 2 or 3 length sentences, although I presume that under the hood they are randomly selecting sentences of length 1.

This works as expected in Faker version 5.4.1.

### Steps to reproduce

```
>>> import faker
>>> thing = faker.Faker()
>>> print(thing.sentence(1))
```

### Expected behavior

A random titleized word is printed with a fullstop

### Actual behavior

```
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "~/.../venv/lib/python3.6/site-packages/faker/providers/lorem/__init__.py", line 86, in sentence
    words[0] = words[0].title()
TypeError: 'tuple' object does not support item assignment
```


Comments:

Patch:
diff --git a/faker/providers/lorem/__init__.py b/faker/providers/lorem/__init__.py
index cc17fa47..42c4030f 100644
--- a/faker/providers/lorem/__init__.py
+++ b/faker/providers/lorem/__init__.py
@@ -22,7 +22,7 @@ class Provider(BaseProvider):
     sentence_punctuation = '.'
 
     def words(self, nb=3, ext_word_list=None, unique=False):
-        """Generate a list of words.
+        """Generate a tuple of words.
 
         The ``nb`` argument controls the number of words in the resulting list,
         and if ``ext_word_list`` is provided, words from that list will be used
@@ -82,7 +82,7 @@ class Provider(BaseProvider):
         if variable_nb_words:
             nb_words = self.randomize_nb_elements(nb_words, min=1)
 
-        words = self.words(nb=nb_words, ext_word_list=ext_word_list)
+        words = list(self.words(nb=nb_words, ext_word_list=ext_word_list))
         words[0] = words[0].title()
 
         return self.word_connector.join(words) + self.sentence_punctuation


Test patch:
diff --git a/tests/providers/test_lorem.py b/tests/providers/test_lorem.py
index 7ba59859..be45e8b5 100644
--- a/tests/providers/test_lorem.py
+++ b/tests/providers/test_lorem.py
@@ -88,6 +88,10 @@ class TestLoremProvider:
                 words = sentence.lower().replace('.', '').split()
                 assert all(isinstance(word, str) and word in self.custom_word_list for word in words)
 
+    def test_sentence_single_word(self, faker):
+        word = faker.sentence(1)
+        assert str.isupper(word[0])
+
     def test_paragraph_no_sentences(self, faker, num_samples):
         for _ in range(num_samples):
             assert faker.paragraph(0) == ''
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.


Comments:

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()