Skip to content

Commit

Permalink
Docs improvements (#216)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ousret authored Feb 9, 2025
2 parents f224adc + 81df6b8 commit 955b7a4
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 36 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@
u"Niquests Documentation",
author,
"Niquests",
"One line description of project.",
"“Safest, Fastest, Easiest, and Most advanced” Python HTTP Client. Production Ready! Drop-in replacement for Requests. HTTP/1.1, HTTP/2, and HTTP/3 supported. With WebSocket, and SSE! Be free of Requests bondage now.",
"Miscellaneous",
)
]
Expand Down
2 changes: 2 additions & 0 deletions docs/dev/migrate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ for more information. (The goal would simply to execute the following piece of c
from sys import modules
import niquests
import requests
import urllib3
# the mock utility 'response' only works with 'requests'
modules["requests"] = niquests
modules["requests.adapters"] = niquests.adapters
modules["requests.exceptions"] = niquests.exceptions
modules["requests.compat"] = requests.compat
modules["requests.packages.urllib3"] = urllib3
.. warning:: This code sample is only to be executed in a development environment, it permit to fool the third-party dependencies that have a strong tie on Requests.
Expand Down
11 changes: 8 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
Niquests: HTTP for Humans™
==========================

Release v\ |version|. (:ref:`Installation <install>`)
Release v\ |version| (:ref:`Installation <install>`)


.. image:: https://static.pepy.tech/badge/niquests/month
:target: https://pepy.tech/project/niquests
.. image:: https://img.shields.io/pypi/dm/niquests.svg
:target: https://pypistats.org/packages/niquests
:alt: Niquests Downloads Per Month Badge

.. image:: https://img.shields.io/pypi/l/niquests.svg
Expand All @@ -24,6 +24,8 @@ Release v\ |version|. (:ref:`Installation <install>`)
**Niquests** is an elegant and simple HTTP library for Python, built for human beings. It
is designed to be a drop-in replacement for **Requests** that is no longer under feature freeze.

Supports HTTP/1.1, HTTP/2 and HTTP/3 out-of-the-box without breaking a sweat!

-------------------

**Behold, the power of Niquests**::
Expand Down Expand Up @@ -76,6 +78,8 @@ Niquests is ready for today's web.
- Automatic honoring of `.netrc`
- Basic & Digest Authentication
- Familiar `dict`–like Cookies
- Network settings fine-tuning
- HTTP/2 with prior knowledge
- Object-oriented headers
- Multi-part File Uploads
- Post-Quantum Security
Expand All @@ -94,6 +98,7 @@ Niquests is ready for today's web.
- Trailers!
- DNSSEC!
- Async!
- SSE!

Niquests officially supports Python 3.7+, and runs great on PyPy.

Expand Down
6 changes: 3 additions & 3 deletions docs/user/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ You may find bellow a plausible example::
print(req.upload_progress)

with niquests.Session() as s:
s.post("https://pie.dev/post", data=b"foo"*16800*1024, hooks={"on_upload": [track]})
s.post("https://httpbingo.org/post", data=b"foo"*16800*1024, hooks={"on_upload": [track]})

.. note:: Niquests recommend the excellent tqdm library to create progress bars with ease.

Expand Down Expand Up @@ -1493,7 +1493,7 @@ Here is a simple example::

session = niquests.Session()

response = session.get("https://pie.dev/get")
response = session.get("https://httpbingo.org/get")

print(response.conn_info.resolution_latency) # output the DNS resolution latency
print(response.conn_info.tls_handshake_latency) # the TLS handshake completion
Expand All @@ -1516,7 +1516,7 @@ Here is a simple example::
import niquests

session = niquests.Session()
session.get("https://pie.dev/get", verify="sha256_8fff956b66667ffe5801c8432b12c367254727782d91bc695b7a53d0b512d721")
session.get("https://httpbingo.org/get", verify="sha256_8fff956b66667ffe5801c8432b12c367254727782d91bc695b7a53d0b512d721")

.. warning:: Supported fingerprinting algorithms are sha256, and sha1. The prefix is mandatory.

Expand Down
4 changes: 2 additions & 2 deletions docs/user/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ You must provide the user and pass into the DNS url as such::
from niquests import Session

with Session(resolver="doh://user:[email protected]") as s:
resp = s.get("https://pie.dev/get")
resp = s.get("https://httpbingo.org/get")

Passing a bearer token
----------------------
Expand All @@ -60,7 +60,7 @@ You must provide the token directly into the DNS url as such::
from niquests import Session

with Session(resolver="doh://[email protected]") as s:
resp = s.get("https://pie.dev/get")
resp = s.get("https://httpbingo.org/get")

netrc Authentication
~~~~~~~~~~~~~~~~~~~~
Expand Down
121 changes: 94 additions & 27 deletions docs/user/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ and returned. To check that a request is successful, use

.. note:: Since Niquests 3.2, ``r.raise_for_status()`` is chainable as it returns self if everything went fine.

.. tip:: Niquests support using ``orjson`` instead of the ``json`` standard library. To leverage that feature, install ``orjson`` or ``niquests[speedups]``. This can dramatically increase performance.

Raw Response Content
--------------------

Expand Down Expand Up @@ -411,7 +413,7 @@ But, since our ``status_code`` for ``r`` was ``200``, when we call
``raise_for_status()`` we get::

>>> r.raise_for_status()
None
<Response HTTP/2 [200]>

All is well.

Expand Down Expand Up @@ -560,7 +562,7 @@ Timeouts

You can tell Niquests to stop waiting for a response after a given number of
seconds with the ``timeout`` parameter. Nearly all production code should use
this parameter in nearly all niquests. By default GET, HEAD, OPTIONS ships with a
this parameter in nearly all requests. By default GET, HEAD, OPTIONS ships with a
30 seconds timeout delay and 120 seconds for the rest::

>>> niquests.get('https://github.com/', timeout=0.001)
Expand All @@ -569,14 +571,35 @@ this parameter in nearly all niquests. By default GET, HEAD, OPTIONS ships with
niquests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)


.. admonition:: Note
.. note::

``timeout`` is not a time limit on the entire response download;
rather, an exception is raised if the server has not issued a
response for ``timeout`` seconds (more precisely, if no bytes have been
received on the underlying socket for ``timeout`` seconds). If no timeout is specified explicitly, requests
use the default according to your HTTP verb. Either 30 seconds or 120 seconds.

.. warning::

We know that users are surprised by the ``timeout`` behaviors. You should know
that Niquests is bound to some legacy behaviors that existed well prior us.
Let's say that you set up ``timeout=1`` to a specific host. Now let's say on
purpose that the host is down. Then we should expect the request to fail
exactly 1s after. That is correct. But! Beware that if the host has more than
1 DNS records (either A or AAAA), they all will be tested with set timeout limit!
So if ``example.tld`` has two IPs associated, then you should expect 2s max delay.
And so on, so forth...

.. tip::

Set ``happy_eyeballs=True`` when constructing your ``Session`` to try all endpoints simultaneously.
This will help you circumvent most of the connectivity issues.

.. warning::

Unfortunately, due to a Python restriction, we cannot ensure that ``timeout`` is respected if your system DNS is
unresponsive. This only applies in synchronous mode (i.e. not async).
To circumvent that issue, you should use a more modern DNS resolver solution. See ``resolver=...`` parameter.

Errors and Exceptions
---------------------
Expand Down Expand Up @@ -606,7 +629,7 @@ The underlying package may or may not be installed on your environment.
If it is not present, no HTTP/3 or QUIC support will be present.

If you uninstall the qh3 package it disable the support for HTTP/3 without breaking anything.
On the overhand, installing it manually (require compilation/non native wheel) will bring its support.
On the overhand, installing it manually (may require compilation toolchain) will bring its support.

Find a quick way to know if your environment is capable of emitting HTTP/3 requests by::

Expand Down Expand Up @@ -645,7 +668,10 @@ Any ``Response`` returned by get, post, put, etc... will be a lazy instance of `
and actually leverage its perks, you will have to issue multiple concurrent request before
actually trying to access any ``Response`` methods or attributes.

**Example A)** Emitting concurrent requests and loading them via `Session.gather()`::
Gather responses
~~~~~~~~~~~~~~~~

Emitting concurrent requests and loading them via `Session.gather()`::

from niquests import Session
from time import time
Expand All @@ -656,19 +682,22 @@ Any ``Response`` returned by get, post, put, etc... will be a lazy instance of `
responses = []

responses.append(
s.get("https://pie.dev/delay/3")
s.get("https://httpbingo.org/delay/3")
)

responses.append(
s.get("https://pie.dev/delay/1")
s.get("https://httpbingo.org/delay/1")
)

s.gather()

print(f"waited {time() - before} second(s)") # will print 3s


**Example B)** Emitting concurrent requests and loading them via direct access::
Direct Access
~~~~~~~~~~~~~

Emitting concurrent requests and loading them via direct access::

from niquests import Session
from time import time
Expand All @@ -679,11 +708,11 @@ Any ``Response`` returned by get, post, put, etc... will be a lazy instance of `
responses = []

responses.append(
s.get("https://pie.dev/delay/3")
s.get("https://httpbingo.org/delay/3")
)

responses.append(
s.get("https://pie.dev/delay/1")
s.get("https://httpbingo.org/delay/1")
)

# internally call gather with self (Response)
Expand Down Expand Up @@ -731,13 +760,12 @@ Here is a basic example::
tasks = []

for _ in range(10):
tasks.append(asyncio.create_task(fetch("https://pie.dev/delay/1")))
tasks.append(asyncio.create_task(fetch("https://httpbingo.org/delay/1")))

responses = await asyncio.gather(*tasks)

print(responses)


if __name__ == "__main__":
asyncio.run(main())

Expand Down Expand Up @@ -772,7 +800,7 @@ Look at this basic sample::
tasks = []

for _ in range(10):
tasks.append(asyncio.create_task(fetch("https://pie.dev/delay/1")))
tasks.append(asyncio.create_task(fetch("https://httpbingo.org/delay/1")))

responses_responses = await asyncio.gather(*tasks)
responses = [item for sublist in responses_responses for item in sublist]
Expand All @@ -796,12 +824,11 @@ Delaying the content consumption in an async context can be easily achieved usin
async def main() -> None:

async with niquests.AsyncSession() as s:
r = await s.get("https://pie.dev/get", stream=True)
r = await s.get("https://httpbingo.org/get", stream=True)

async for chunk in await r.iter_content(16):
print(chunk)


if __name__ == "__main__":

asyncio.run(main())
Expand All @@ -814,7 +841,7 @@ Or using the ``iter_line`` method as such::
async def main() -> None:

async with niquests.AsyncSession() as s:
r = await s.get("https://pie.dev/get", stream=True)
r = await s.get("https://httpbingo.org/get", stream=True)

async for chunk in r.iter_line():
print(chunk)
Expand All @@ -830,7 +857,7 @@ Or simply by doing::
async def main() -> None:

async with niquests.AsyncSession() as s:
r = await s.get("https://pie.dev/get", stream=True)
r = await s.get("https://httpbingo.org/get", stream=True)
payload = await r.json()

if __name__ == "__main__":
Expand All @@ -855,17 +882,16 @@ Here is a basic example of how you would do it::
import niquests
import asyncio


async def main() -> None:

responses = []

async with niquests.AsyncSession(multiplexed=True) as s:
responses.append(
await s.get("https://pie.dev/get", stream=True)
await s.get("https://httpbingo.org/get", stream=True)
)
responses.append(
await s.get("https://pie.dev/get", stream=True)
await s.get("https://httpbingo.org/get", stream=True)
)

print(responses)
Expand Down Expand Up @@ -898,9 +924,36 @@ To do so, you are invited to set the following parameters within a Session const
- **pool_connections** means the number of host target (or pool of connections if you prefer).
- **pool_maxsize** means the maximum of concurrent connexion per host target/pool.

.. warning:: Due to the multiplexed aspect of both HTTP/2, and HTTP/3 you can issue, usually, more than 200 requests per connection without ever needing to create another one.
.. tip:: Due to the multiplexed aspect of both HTTP/2, and HTTP/3 you can issue, usually, more than 200 requests per connection without ever needing to create another one.

.. note:: This setting is most useful for multi-threading/tasks application.

Pool Connections
~~~~~~~~~~~~~~~~

.. note:: This setting is most useful for multi-threading application.
Setting ``pool_connections=2`` will keep the connection to ``host-b.tld`` and ``host-c.tld``.
``host-a.tld`` will be silently discarded.

.. code:: python
import niquests
with niquests.Session(pool_connections=2) as s:
s.get("https://host-a.tld/some")
s.get("https://host-b.tld/some")
s.get("https://host-c.tld/some")
.. attention::

Unfortunately, due to backward compatibility purposes, those settings applies PER SCHEME.
``pool_connections=2`` will allow up to 2 HTTP (unencrypted) and 2 HTTPS (encrypted)
connections. Meaning that you can still get 4 hosts being kept alive.

Pool Maxsize
~~~~~~~~~~~~

Setting ``pool_maxsize=2`` will allow up to 2 connection to ``host-a.tld``.
This settings is only useful in a concurrent environment. Either async or threaded.

DNS Resolution
--------------
Expand All @@ -921,9 +974,9 @@ Here is a basic example that leverage Google public DNS over HTTPS::
from niquests import Session

with Session(resolver="doh+google://") as s:
resp = s.get("https://pie.dev/get")
resp = s.get("https://httpbingo.org/get")

Here, the domain name (**pie.dev**) will be resolved using the provided DNS url.
Here, the domain name (**httpbingo.org**) will be resolved using the provided DNS url.

.. note:: By default, Niquests still use the good old, often insecure, system DNS.

Expand All @@ -935,7 +988,7 @@ You may specify a list of resolvers to be tested in order::
from niquests import Session

with Session(resolver=["doh+google://", "doh://cloudflare-dns.com"]) as s:
resp = s.get("https://pie.dev/get")
resp = s.get("https://httpbingo.org/get")

The second entry ``doh://cloudflare-dns.com`` will only be tested if ``doh+google://`` failed to provide a usable answer.

Expand Down Expand Up @@ -974,10 +1027,24 @@ Simply add ``verify=false`` into your DNS url to pursue::
from niquests import Session

with Session(resolver="doh+google://default/?verify=false") as s:
resp = s.get("https://pie.dev/get")
resp = s.get("https://httpbingo.org/get")


.. warning:: Doing a ``s.get("https://httpbingo.org/get", verify=False)`` does not impact the resolver.

Timeouts
~~~~~~~~

You may set a specific timeout for domain name resolution by appending ``?timeout=1`` to the resolver configuration.

.. code:: python
from niquests import Session
with Session(resolver="doh+google://default/?timeout=1") as s:
resp = s.get("https://httpbingo.org/get")
.. warning:: Doing a ``s.get("https://pie.dev/get", verify=False)`` does not impact the resolver.
This will prevent any resolution that last longer to a second.

Speedups
--------
Expand Down

0 comments on commit 955b7a4

Please sign in to comment.