Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Fully type annotated.

This is a huge win compare to requests. AFAIKT requests is too flexible (read: easier to misuse) and difficult to add type annotation now. The author of requests gave a horrible type annotation example here [0].

IMO at this time when you evaluate a new Python library before adopting, "having type annotation" should be as important as "having decent unit test coverage".

[0]: https://lwn.net/Articles/643399/



The huge win against requests is that Https is fully async.. you can download 20 files in parallel without not too much effort. Throughout in python asyncio is amazing, something similar to node or perhaps better... That's the main point.


FWIW, requests3 has "Type-annotations for all public-facing APIs", asyncio, HTTP/2, connection pooling, timeouts, etc https://github.com/kennethreitz/requests3



Sorry, have you checked the source? Are these features there or only announced? Has requests added a timeout by default finally?


It looks like requests is now owned by PSF. https://github.com/psf/requests

But IDK why requests3 wasn't transferred as well, and why issues appear to be disabled on the repo now.

The docs reference a timeout arg (that appears to default to the socket default timeout) for connect and/or read https://3.python-requests.org/user/advanced/#timeouts

And the tests reference a timeout argument. If that doesn't work, I wonder how much work it would be to send a PR (instead of just talking s to Ken and not contributing any code)


>But IDK why requests3 wasn't transferred as well, and

That's thing... Who knows..


TIL requests3 beta works with httpx as a backend: https://github.com/not-kennethreitz/team/issues/21#issuecomm...

If requests3 is installed, `import requests' imports requests3


> "having type annotation" should be as important as "having decent unit test coverage".

I mean this is pretty spot on IMO. I've worked with many languages, and have concluded that having a powerful type system catches soooo many bugs before you even try to run the code.

And they're usually "stupid" bugs too, forgetting to sanitize inputs etc. Even worse is when a language tries to be "smart", so you end up with "1" + 2 = "12" and no errors at all.


Type systems and static checks in general are great. What do you think of the example type in the post above? It seems like the type system might not be expressive enough to handle existing APIs, that’s something which was hard for TypeScript too, but has slowly been getting better. Perhaps that will be the case in Python too.


Personally, I use type aliases in cases where I need to describe very dynamic types that make intuitive sense but are too wordy to pattern-match visually. So you might decompose that example like:

    FileSourceType = Union[basestring, file]
    FileSpecType = Union[
        # (filename, file_source)
        Tuple[basestring, Optional[FileSourceType]],
        # (filename, file_source, content_type)
        Tuple[basestring, Optional[FileSourceType], Optional[basestring]],
        # (filename, file_source, content_type, custom_headers)
        Tuple[basestring, Optional[FileSourceType], Optional[basestring], Optional[Headers]]
    ]

    ...

    files: Optional[
        Union[
            Mapping[basestring, FileSpecType], 
            Iterable[Tuple[basestring, FileSpecType]
        ]
    ]
You theoretically lose some "glance value" because now you have to look in two places for the type...but in practice, I think you can figure it out a lot easier than the original example. Obviously you don't want to do this in simple cases, but it can make pathological cases like the above a lot easier to chew on.


Some parts of that example seem pretty silly. URLs for example: he describes how just about anything can be passed in as a URL, and requests attempts to call __str__ or __unicode__ on it and then parse it. Considering (essentially?) all types in Python have __str__, this is a perfectly reasonable place to use the Any type. The issue isn't with the expressiveness of the typing system, but with the absurd flexibility of input handling by requests.


The right type is ‘object’, not ‘Any’.

‘object’ is the base class of all types: you can put anything into an ‘object’, and you can only do very generic operations like str() on what you get out of an ‘object’ without further checks like isinstance().

‘Any’ is an unsound escape hatch that disables type checking: you can put anything into an ‘Any’, and you can do anything with what you get out of an ‘Any’, and the type checker will make no effort to stop you from doing something wrong.

https://docs.python.org/3/library/typing.html#the-any-type

https://mypy.readthedocs.io/en/latest/dynamic_typing.html#an...


Actually the proper way to handle is to use protocols. They provide already SupportsBytes type, and you can similarly create custom SupportsStr, but IMO it is silly to do that. This approach increases chance of bugs, and it's just easy to use str type and for developer to wrap such value in str(). I believe that's why SupportsStr was not introduced.


You should check out the Literal type in Python. It provides for some of the stupider things in libraries, like return types that vary based on the value of an input parameter, as in Python’s open() method, which might return a text stream or a bytestream based on the value of the second parameter.


> And they're usually "stupid" bugs too, forgetting to sanitize inputs etc. Even worse is when a language tries to be "smart", so you end up with "1" + 2 = "12" and no errors at all.

The last one is no longer possible in Python 3 and will throw an error. That's another thing I am glad they fixed.


That's from 2015, his second complaint about interfaces vs inheritance is solved in recent versions with Protocols. Compatibility with 2.7 shouldn't be argument today either.

The complex union is as you say more a sign of a too flexible api rather than problem with type hints, the type hints just brings the root issue to the surface. I mean, why would you accept either a mapping or a list of tuples as input? Just let the user call dict(..) on the tuple-list first if they have it in such format? The documentation doesn't even mention that lists are ok for headers, only dicts: https://2.python-requests.org/en/master/api/#main-interface.

The file-tuple api with various length tuples is perhaps valid and the most convenient way to implement such options, but it's still an exceptionally unique api which requires exceptional type hints, it can be made slightly simpler which chc demonstrated above.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: