Skip to content

ClientSession.timeout has an incorrect typing #6917

Closed
@DevilXD

Description

Describe the bug

The aiohttp.ClientSession.timeout attribute has a type of Union[object, aiohttp.ClientTimeout], however the code logic will never actually assign a bare object type to the self._timeout attribute, making this typing quite over-inclusive. Trying to use this attribute in typed code results in having to use cast(aiohttp.ClientTimeout, session.timeout), which is far from ideal considering one can just fix the typing in the library.

I ran into this while using Python 3.8.10, but the exact same explanation above applies to the current master branch (and the version I'm using of course), as shown by the snippets below.

3.8 branch __init__ parameter:

timeout: Union[object, ClientTimeout] = sentinel,

3.8 branch self._timeout assignment:

aiohttp/aiohttp/client.py

Lines 261 to 290 in 6243204

if timeout is sentinel:
self._timeout = DEFAULT_TIMEOUT
if read_timeout is not sentinel:
warnings.warn(
"read_timeout is deprecated, " "use timeout argument instead",
DeprecationWarning,
stacklevel=2,
)
self._timeout = attr.evolve(self._timeout, total=read_timeout)
if conn_timeout is not None:
self._timeout = attr.evolve(self._timeout, connect=conn_timeout)
warnings.warn(
"conn_timeout is deprecated, " "use timeout argument instead",
DeprecationWarning,
stacklevel=2,
)
else:
self._timeout = timeout # type: ignore[assignment]
if read_timeout is not sentinel:
raise ValueError(
"read_timeout and timeout parameters "
"conflict, please setup "
"timeout.read"
)
if conn_timeout is not None:
raise ValueError(
"conn_timeout and timeout parameters "
"conflict, please setup "
"timeout.connect"
)

Note the # type: ignore comment on L278 there - it's because the timeout is sentinel check does not narrow down the timeout type. The correct way to go about this would be to use a cast there instead of ignoring the issue like that.

3.8 branch timeout attribute declaration:

aiohttp/aiohttp/client.py

Lines 1029 to 1032 in 6243204

@property
def timeout(self) -> Union[object, ClientTimeout]:
"""Timeout for the session."""
return self._timeout

Master branch __init__ parameter:

timeout: Union[_SENTINEL, ClientTimeout, None] = sentinel,

Master branch self._timeout assignment:

aiohttp/aiohttp/client.py

Lines 260 to 263 in 52fa599

if timeout is sentinel or timeout is None:
self._timeout = DEFAULT_TIMEOUT
else:
self._timeout = timeout

Due to a different handling of the sentinel value via an Enum member, no cast is needed here.

Master branch timeout attribute declaration:

aiohttp/aiohttp/client.py

Lines 1008 to 1011 in 52fa599

@property
def timeout(self) -> Union[object, ClientTimeout]:
"""Timeout for the session."""
return self._timeout

The attribute type is still over-inclusive here though.

The solution would be quite simple:

    @property
    def timeout(self) -> ClientTimeout:
        """Timeout for the session."""
        return self._timeout

Please let me know if you'd welcome a PR for this. I'd like to get this backported back to 3.8 (that I'm using) if possible, but if not, just fixing it in the master branch so that it's correct going forward would be good enough for me.

To Reproduce

Utilize some kind of a type checker like MyPy.

import asyncio
import aiohttp

async def main:
    session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10))
    # read back the total time attribute
    total_time = session.timeout.total  # "object" type of "Union[object, ClientTimeout]" has no attribute "total"
    print(total_time)

asyncio.run(main())

Expected behavior

The attribute having only the aiohttp.ClientTimeout type and not requiring cast usage when accessing the attribute during library usage in user code.

Logs/tracebacks

Not applicable

Python Version

Python 3.8.10

aiohttp Version

Version: 3.8.1

multidict Version

Version: 6.0.2

yarl Version

Version: 1.7.2

OS

Windows

Related component

Client

Additional context

Related issues and PRs:

#4191
#4193

Code of Conduct

  • I agree to follow the aio-libs Code of Conduct

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions