27

I recently ran into Ellipsis (...) that is used in function parameters in aiohttp code and then in that function's body:

def make_mocked_request(method, path, headers=None, *,
                        match_info=sentinel,
                        version=HttpVersion(1, 1), closing=False,
                        app=None,
                        writer=sentinel,
                        protocol=sentinel,
                        transport=sentinel,
                        payload=sentinel,
                        sslcontext=None,
                        client_max_size=1024**2,
                        loop=...):
    """Creates mocked web.Request testing purposes.

    Useful in unit tests, when spinning full web server is overkill or
    specific conditions and errors are hard to trigger.

    """

    task = mock.Mock()
    if loop is ...:
        loop = mock.Mock()
        loop.create_future.return_value = ()

Can you explain this new python 3 feature?

Jan Pokorný
  • 1,418
  • 15
  • 22
Boris Burkov
  • 13,420
  • 17
  • 74
  • 109
  • 1
    Possible duplicate of [What does the Python Ellipsis object do?](https://stackoverflow.com/questions/772124/what-does-the-python-ellipsis-object-do) – Andrew F Jan 28 '19 at 16:13

2 Answers2

35

Ellipsis is a Built-in constant in Python. In Python 3 it has a literal syntax ... so it can be used like any other literal. This was accepted by Guido for Python 3 because some folks thought it would be cute.

The code you have found (use as function argument default) is apparently one such "cute" usage. Later in that code you'll see:

if loop is ...:
    loop = mock.Mock()
    loop.create_future.return_value = ()

It's just being used as a sentinel, here, and may as well be object() or anything else - there is nothing specific to Ellipsis. Perhaps the usual sentinel, None, has some other specific meaning in this context, although I can not see any evidence of that in the commit (it seems like None will have worked just as well).

Another use-case for an ellipsis literal sometimes seen in the wild is a placeholder for code not written yet, similar to pass statement:

class Todo:
    ...

For the more typical use-cases, which are involving the extended slice syntax, see What does the Python Ellipsis object do?

wim
  • 338,267
  • 99
  • 616
  • 750
  • 2
    Thanks for response, @wim! So I assume that the use of ellipsis here is a "cute" way to check that the argument wasn't passed on this function's input (and thus equals to its default value, which is Ellipsis). – Boris Burkov Jan 28 '19 at 16:28
  • 1
    Yes, that's it. Sorry it couldn't be something more interesting.. :) – wim Jan 28 '19 at 16:32
  • the secrets module has this doc string: ` (function) token_hex: (nbytes: int | None = ...) -> str Return a random text string, in hexadecimal. The string has *nbytes* random bytes, each byte converted to two hex digits. If *nbytes* is None or not supplied, a reasonable default is used.` Does ellipses have a useful meaning here? – Zaffer Jul 18 '21 at 00:58
  • @Zaffer I'm not sure where you are seeing that but it's [not the standard docstring](https://github.com/python/cpython/blob/v3.9.6/Lib/secrets.py#L48-L59) – wim Jul 18 '21 at 17:32
  • 1
    @Zaffer I suspect it is VS Code hinting that there is more information to read; a traditional English use of [ellipses](https://www.merriam-webster.com/dictionary/ellipsis) – Yolo Perdiem Oct 11 '21 at 13:47
4

Building on https://stackoverflow.com/a/54406084/1988486, using ... vs. None has some validity when dealing with type annotations. Consider the following:

def foo(map: dict = None):
    if not map:
        map = {}

    return map

Granted, this doesn't do much, but this is causing type hints to conflict in a modern editor such as VS Code. This is due to the fact that you're claiming that map is a dict and yet the default is a NoneType (not a dict).

The Ellipsis is its own type, so VS Code will not complain, but you can interact with it in similar ways because it is a sentinel value.

def food(map: dict = ...):
    if map is ...:
        map = {}

    return map

VS Code will not raise any concerns about the type hinting and the various types this variable can become in the course of running the code.

I can't say for sure why the aiohttp code uses it since they don't appear to be using type annotations, but it's at least a viable reason to do so.

BradLucky
  • 88
  • 1
  • 7