redc 0.1.0.dev5__tar.gz → 0.1.1.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redc-0.1.1.dev0/.github/workflows/before-all.sh +30 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/PKG-INFO +6 -4
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/README.md +5 -3
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/pyproject.toml +2 -2
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/__init__.py +3 -2
 - redc-0.1.1.dev0/redc/callbacks.py +81 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/client.py +120 -69
 - redc-0.1.1.dev0/redc/exceptions/__init__.py +22 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/ext/redc.cpp +62 -26
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/ext/redc.h +22 -15
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/response.py +6 -11
 - redc-0.1.0.dev5/wheelhouse/redc-0.1.0.dev5-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl → redc-0.1.1.dev0/wheelhouse/redc-0.1.1.dev0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +0 -0
 - redc-0.1.0.dev5/wheelhouse/redc-0.1.0.dev5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl → redc-0.1.1.dev0/wheelhouse/redc-0.1.1.dev0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +0 -0
 - redc-0.1.0.dev5/wheelhouse/redc-0.1.0.dev5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl → redc-0.1.1.dev0/wheelhouse/redc-0.1.1.dev0-cp312-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +0 -0
 - redc-0.1.0.dev5/wheelhouse/redc-0.1.0.dev5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl → redc-0.1.1.dev0/wheelhouse/redc-0.1.1.dev0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +0 -0
 - redc-0.1.0.dev5/redc/callbacks.py +0 -40
 - redc-0.1.0.dev5/redc/exceptions/__init__.py +0 -2
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/.clang-format +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/.github/workflows/build_wheels.yml +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/.gitignore +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/CMake/PreventInSourceBuild.cmake +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/CMakeLists.txt +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/LICENSE +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/assets/images/redc-logo.png +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/callback.py +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/codes.py +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/ext/utils/concurrentqueue.h +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/ext/utils/curl_utils.h +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/utils/__init__.py +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/utils/headers.py +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/utils/http.py +0 -0
 - {redc-0.1.0.dev5 → redc-0.1.1.dev0}/redc/utils/json_encoder.py +0 -0
 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env bash
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            CURL_VERSION="8.11.1"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # deps
         
     | 
| 
      
 6 
     | 
    
         
            +
            yum install wget gcc make libpsl-devel libidn-devel zlib-devel libnghttp2-devel perl-IPC-Cmd -y
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            # openssl from source
         
     | 
| 
      
 9 
     | 
    
         
            +
            git clone --depth 1 https://github.com/openssl/openssl
         
     | 
| 
      
 10 
     | 
    
         
            +
            cd openssl
         
     | 
| 
      
 11 
     | 
    
         
            +
            ./Configure
         
     | 
| 
      
 12 
     | 
    
         
            +
            make -j100
         
     | 
| 
      
 13 
     | 
    
         
            +
            make install
         
     | 
| 
      
 14 
     | 
    
         
            +
            ldconfig
         
     | 
| 
      
 15 
     | 
    
         
            +
            cd .. && rm -rf openssl
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            # curl from source
         
     | 
| 
      
 18 
     | 
    
         
            +
            wget https://curl.se/download/curl-$CURL_VERSION.tar.gz
         
     | 
| 
      
 19 
     | 
    
         
            +
            tar -xzvf curl-$CURL_VERSION.tar.gz
         
     | 
| 
      
 20 
     | 
    
         
            +
            rm curl-$CURL_VERSION.tar.gz
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            cd curl-$CURL_VERSION
         
     | 
| 
      
 23 
     | 
    
         
            +
            ./configure --with-openssl --enable-cookies --with-zlib --enable-threaded-resolver --enable-ipv6 --enable-proxy --with-ca-fallback --with-ca-bundle=/etc/ssl/certs/ca-certificates.crt
         
     | 
| 
      
 24 
     | 
    
         
            +
            make -j100
         
     | 
| 
      
 25 
     | 
    
         
            +
            make install
         
     | 
| 
      
 26 
     | 
    
         
            +
            ldconfig
         
     | 
| 
      
 27 
     | 
    
         
            +
            curl --version
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            cd ..
         
     | 
| 
      
 30 
     | 
    
         
            +
            rm -rf curl-$CURL_VERSION
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.1
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: redc
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 0.1. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 0.1.1.dev0
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: RedC is a high-performance, asynchronous HTTP client library for Python, built on top of the powerful curl library
         
     | 
| 
       5 
5 
     | 
    
         
             
            Keywords: asyncio,http,client,http-client,curl,libcurl
         
     | 
| 
       6 
6 
     | 
    
         
             
            Author-Email: AYMEN Mohammed <let.me.code.safe@gmail.com>
         
     | 
| 
         @@ -11,10 +11,10 @@ Requires-Python: >=3.9 
     | 
|
| 
       11 
11 
     | 
    
         
             
            Description-Content-Type: text/markdown
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
            <div align="center">
         
     | 
| 
       14 
     | 
    
         
            -
              <img src="/assets/images/redc-logo.png">
         
     | 
| 
      
 14 
     | 
    
         
            +
              <img src="https://raw.githubusercontent.com/AYMENJD/redc/refs/heads/main/assets/images/redc-logo.png">
         
     | 
| 
       15 
15 
     | 
    
         
             
            </div>
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
            [](https://pypi.org/project/RedC) [](https://pepy.tech/project/redc)
         
     | 
| 
      
 17 
     | 
    
         
            +
            [](https://pypi.org/project/RedC) [](https://curl.se/ch/8.11.1.html) [](https://pepy.tech/project/redc)
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            **RedC** is a **high-performance**, asynchronous **HTTP** client library for **Python**, built on top of the powerful **curl** library. It provides a simple and intuitive interface for making HTTP requests and handling responses
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
         @@ -43,6 +43,7 @@ async def main(): 
     | 
|
| 
       43 
43 
     | 
    
         
             
                async with Client(base_url="https://jsonplaceholder.typicode.com") as client:
         
     | 
| 
       44 
44 
     | 
    
         
             
                    # Make a GET request
         
     | 
| 
       45 
45 
     | 
    
         
             
                    response = await client.get("/posts/1")
         
     | 
| 
      
 46 
     | 
    
         
            +
                    response.raise_for_status()
         
     | 
| 
       46 
47 
     | 
    
         
             
                    print(response.status_code)  # 200
         
     | 
| 
       47 
48 
     | 
    
         
             
                    print(response.json())  # {'userId': 1, 'id': 1, 'title': '...', 'body': '...'}
         
     | 
| 
       48 
49 
     | 
    
         | 
| 
         @@ -51,6 +52,7 @@ async def main(): 
     | 
|
| 
       51 
52 
     | 
    
         
             
                        "/posts",
         
     | 
| 
       52 
53 
     | 
    
         
             
                        json={"title": "foo", "body": "bar", "userId": 1},
         
     | 
| 
       53 
54 
     | 
    
         
             
                    )
         
     | 
| 
      
 55 
     | 
    
         
            +
                    response.raise_for_status()
         
     | 
| 
       54 
56 
     | 
    
         
             
                    print(response.status_code)  # 201
         
     | 
| 
       55 
57 
     | 
    
         
             
                    print(response.json())  # {'id': 101, ...}
         
     | 
| 
       56 
58 
     | 
    
         | 
| 
         @@ -59,4 +61,4 @@ asyncio.run(main()) 
     | 
|
| 
       59 
61 
     | 
    
         | 
| 
       60 
62 
     | 
    
         
             
            ## License
         
     | 
| 
       61 
63 
     | 
    
         | 
| 
       62 
     | 
    
         
            -
            MIT [LICENSE](LICENSE)
         
     | 
| 
      
 64 
     | 
    
         
            +
            MIT [LICENSE](https://github.com/AYMENJD/redc/blob/main/LICENSE)
         
     | 
| 
         @@ -1,8 +1,8 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            <div align="center">
         
     | 
| 
       2 
     | 
    
         
            -
              <img src="/assets/images/redc-logo.png">
         
     | 
| 
      
 2 
     | 
    
         
            +
              <img src="https://raw.githubusercontent.com/AYMENJD/redc/refs/heads/main/assets/images/redc-logo.png">
         
     | 
| 
       3 
3 
     | 
    
         
             
            </div>
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            [](https://pypi.org/project/RedC) [](https://pepy.tech/project/redc)
         
     | 
| 
      
 5 
     | 
    
         
            +
            [](https://pypi.org/project/RedC) [](https://curl.se/ch/8.11.1.html) [](https://pepy.tech/project/redc)
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            **RedC** is a **high-performance**, asynchronous **HTTP** client library for **Python**, built on top of the powerful **curl** library. It provides a simple and intuitive interface for making HTTP requests and handling responses
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
         @@ -31,6 +31,7 @@ async def main(): 
     | 
|
| 
       31 
31 
     | 
    
         
             
                async with Client(base_url="https://jsonplaceholder.typicode.com") as client:
         
     | 
| 
       32 
32 
     | 
    
         
             
                    # Make a GET request
         
     | 
| 
       33 
33 
     | 
    
         
             
                    response = await client.get("/posts/1")
         
     | 
| 
      
 34 
     | 
    
         
            +
                    response.raise_for_status()
         
     | 
| 
       34 
35 
     | 
    
         
             
                    print(response.status_code)  # 200
         
     | 
| 
       35 
36 
     | 
    
         
             
                    print(response.json())  # {'userId': 1, 'id': 1, 'title': '...', 'body': '...'}
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
         @@ -39,6 +40,7 @@ async def main(): 
     | 
|
| 
       39 
40 
     | 
    
         
             
                        "/posts",
         
     | 
| 
       40 
41 
     | 
    
         
             
                        json={"title": "foo", "body": "bar", "userId": 1},
         
     | 
| 
       41 
42 
     | 
    
         
             
                    )
         
     | 
| 
      
 43 
     | 
    
         
            +
                    response.raise_for_status()
         
     | 
| 
       42 
44 
     | 
    
         
             
                    print(response.status_code)  # 201
         
     | 
| 
       43 
45 
     | 
    
         
             
                    print(response.json())  # {'id': 101, ...}
         
     | 
| 
       44 
46 
     | 
    
         | 
| 
         @@ -47,4 +49,4 @@ asyncio.run(main()) 
     | 
|
| 
       47 
49 
     | 
    
         | 
| 
       48 
50 
     | 
    
         
             
            ## License
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
            MIT [LICENSE](LICENSE)
         
     | 
| 
      
 52 
     | 
    
         
            +
            MIT [LICENSE](https://github.com/AYMENJD/redc/blob/main/LICENSE)
         
     | 
| 
         @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build" 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            [project]
         
     | 
| 
       6 
6 
     | 
    
         
             
            name = "redc"
         
     | 
| 
       7 
     | 
    
         
            -
            version = "0.1. 
     | 
| 
      
 7 
     | 
    
         
            +
            version = "0.1.1.dev0"
         
     | 
| 
       8 
8 
     | 
    
         
             
            description = "RedC is a high-performance, asynchronous HTTP client library for Python, built on top of the powerful curl library"
         
     | 
| 
       9 
9 
     | 
    
         
             
            readme = "README.md"
         
     | 
| 
       10 
10 
     | 
    
         
             
            authors = [{ name = "AYMEN Mohammed", email = "let.me.code.safe@gmail.com" }]
         
     | 
| 
         @@ -27,5 +27,5 @@ skip = "*musllinux*" 
     | 
|
| 
       27 
27 
     | 
    
         
             
            archs = ["x86_64"]
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
            [tool.cibuildwheel.linux]
         
     | 
| 
       30 
     | 
    
         
            -
            before-all = " 
     | 
| 
      
 30 
     | 
    
         
            +
            before-all = ".github/workflows/before-all.sh"
         
     | 
| 
       31 
31 
     | 
    
         
             
            manylinux-x86_64-image = "manylinux2014"
         
     | 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            from .callbacks import StreamCallback
         
     | 
| 
      
 1 
     | 
    
         
            +
            from .callbacks import StreamCallback, ProgressCallback
         
     | 
| 
       2 
2 
     | 
    
         
             
            from .client import Client
         
     | 
| 
       3 
3 
     | 
    
         
             
            from .codes import HTTPStatus
         
     | 
| 
       4 
4 
     | 
    
         
             
            from .exceptions import HTTPError
         
     | 
| 
         @@ -11,10 +11,11 @@ __all__ = [ 
     | 
|
| 
       11 
11 
     | 
    
         
             
                "HTTPError",
         
     | 
| 
       12 
12 
     | 
    
         
             
                "HTTPStatus",
         
     | 
| 
       13 
13 
     | 
    
         
             
                "StreamCallback",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "ProgressCallback",
         
     | 
| 
       14 
15 
     | 
    
         
             
                "utils",
         
     | 
| 
       15 
16 
     | 
    
         
             
            ]
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
            __version__ = "0.1. 
     | 
| 
      
 18 
     | 
    
         
            +
            __version__ = "0.1.1.dev0"
         
     | 
| 
       18 
19 
     | 
    
         
             
            __copyright__ = "Copyright (c) 2025 RedC, AYMENJD"
         
     | 
| 
       19 
20 
     | 
    
         
             
            __license__ = "MIT License"
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
         @@ -0,0 +1,81 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import inspect
         
     | 
| 
      
 2 
     | 
    
         
            +
            from typing import Callable
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class StreamCallback:
         
     | 
| 
      
 6 
     | 
    
         
            +
                """A class for creating a stream callback"""
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def __init__(self, callback: Callable[[bytes, int], None]):
         
     | 
| 
      
 9 
     | 
    
         
            +
                    """A callback handler for streaming data
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    Example:
         
     | 
| 
      
 12 
     | 
    
         
            +
                        .. code-block:: python
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                            >>> def callback(data: bytes, data_size: int):
         
     | 
| 
      
 15 
     | 
    
         
            +
                            ...     print(f"Received {len(data)}")
         
     | 
| 
      
 16 
     | 
    
         
            +
                            >>> stream_callback = StreamCallback(callback)
         
     | 
| 
      
 17 
     | 
    
         
            +
                            >>> client.get("https://example.com/", stream_callback=stream_callback)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    Parameters:
         
     | 
| 
      
 20 
     | 
    
         
            +
                        callback (``Callable[[bytes, int], None]``):
         
     | 
| 
      
 21 
     | 
    
         
            +
                            A function that accepts two arguments: data (``bytes``) and data_size (``int``)
         
     | 
| 
      
 22 
     | 
    
         
            +
                            The function cannot be asynchronous
         
     | 
| 
      
 23 
     | 
    
         
            +
                    """
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    self.callback = callback
         
     | 
| 
      
 26 
     | 
    
         
            +
                    self._validate_callback()
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def _validate_callback(self):
         
     | 
| 
      
 29 
     | 
    
         
            +
                    if inspect.iscoroutinefunction(self.callback):
         
     | 
| 
      
 30 
     | 
    
         
            +
                        raise TypeError("Callback function cannot be asynchronous")
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    signature = inspect.signature(self.callback)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    parameters = signature.parameters
         
     | 
| 
      
 35 
     | 
    
         
            +
                    num_parameters = len(parameters)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    if num_parameters != 2:
         
     | 
| 
      
 38 
     | 
    
         
            +
                        raise TypeError(
         
     | 
| 
      
 39 
     | 
    
         
            +
                            f"Callback function must accept two arguments only callback(data: bytes, data_size: int) but it accepts {num_parameters}."
         
     | 
| 
      
 40 
     | 
    
         
            +
                        )
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            class ProgressCallback:
         
     | 
| 
      
 44 
     | 
    
         
            +
                """A class for creating a progress callback"""
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def __init__(self, callback: Callable[[int, int, int, int], None]):
         
     | 
| 
      
 47 
     | 
    
         
            +
                    """A callback handler for progress updates
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    Example:
         
     | 
| 
      
 50 
     | 
    
         
            +
                        .. code-block:: python
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                            >>> def callback(dltotal: int, dlnow: int, ultotal: int, ulnow: int):
         
     | 
| 
      
 53 
     | 
    
         
            +
                            ...     print(f"Downloaded {dlnow}/{dltotal}, Uploaded {ulnow}/{ultotal}")
         
     | 
| 
      
 54 
     | 
    
         
            +
                            >>> progress_callback = ProgressCallback(callback)
         
     | 
| 
      
 55 
     | 
    
         
            +
                            >>> client.get("https://example.com/", progress_callback=progress_callback)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    Parameters:
         
     | 
| 
      
 58 
     | 
    
         
            +
                        callback (``Callable[[int, int, int, int], None]``):
         
     | 
| 
      
 59 
     | 
    
         
            +
                            A function that accepts four arguments:
         
     | 
| 
      
 60 
     | 
    
         
            +
                            - dltotal (``int``): Total bytes expected to be downloaded
         
     | 
| 
      
 61 
     | 
    
         
            +
                            - dlnow (``int``): Bytes downloaded so far
         
     | 
| 
      
 62 
     | 
    
         
            +
                            - ultotal (``int``): Total bytes expected to be uploaded
         
     | 
| 
      
 63 
     | 
    
         
            +
                            - ulnow (``int``): Bytes uploaded so far
         
     | 
| 
      
 64 
     | 
    
         
            +
                            The function cannot be asynchronous.
         
     | 
| 
      
 65 
     | 
    
         
            +
                    """
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    self.callback = callback
         
     | 
| 
      
 68 
     | 
    
         
            +
                    self._validate_callback()
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def _validate_callback(self):
         
     | 
| 
      
 71 
     | 
    
         
            +
                    if inspect.iscoroutinefunction(self.callback):
         
     | 
| 
      
 72 
     | 
    
         
            +
                        raise TypeError("Callback function cannot be asynchronous")
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    signature = inspect.signature(self.callback)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    parameters = signature.parameters
         
     | 
| 
      
 76 
     | 
    
         
            +
                    num_parameters = len(parameters)
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                    if num_parameters != 4:
         
     | 
| 
      
 79 
     | 
    
         
            +
                        raise TypeError(
         
     | 
| 
      
 80 
     | 
    
         
            +
                            f"Callback function must accept exactly four arguments (dltotal: int, dlnow: int, ultotal: int, ulnow: int) but it accepts {num_parameters}."
         
     | 
| 
      
 81 
     | 
    
         
            +
                        )
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            from urllib.parse import urlencode
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            from .callbacks import StreamCallback
         
     | 
| 
      
 3 
     | 
    
         
            +
            from .callbacks import StreamCallback, ProgressCallback
         
     | 
| 
       4 
4 
     | 
    
         
             
            from .redc_ext import RedC
         
     | 
| 
       5 
5 
     | 
    
         
             
            from .response import Response
         
     | 
| 
       6 
6 
     | 
    
         
             
            from .utils import json_dumps, parse_base_url
         
     | 
| 
         @@ -13,7 +13,11 @@ class Client: 
     | 
|
| 
       13 
13 
     | 
    
         
             
                    self,
         
     | 
| 
       14 
14 
     | 
    
         
             
                    base_url: str = None,
         
     | 
| 
       15 
15 
     | 
    
         
             
                    buffer_size: int = 16384,
         
     | 
| 
      
 16 
     | 
    
         
            +
                    headers: dict = None,
         
     | 
| 
      
 17 
     | 
    
         
            +
                    timeout: tuple = (30.0, 0.0),
         
     | 
| 
      
 18 
     | 
    
         
            +
                    ca_cert_path: str = None,
         
     | 
| 
       16 
19 
     | 
    
         
             
                    force_verbose: bool = None,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    raise_for_status: bool = False,
         
     | 
| 
       17 
21 
     | 
    
         
             
                    json_encoder=json_dumps,
         
     | 
| 
       18 
22 
     | 
    
         
             
                ):
         
     | 
| 
       19 
23 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -32,26 +36,50 @@ class Client: 
     | 
|
| 
       32 
36 
     | 
    
         
             
                        buffer_size (``int``, *optional*):
         
     | 
| 
       33 
37 
     | 
    
         
             
                            The buffer size for libcurl. Must be greater than ``1024`` bytes. Default is ``16384`` (16KB)
         
     | 
| 
       34 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                        headers (``dict``, *optional*):
         
     | 
| 
      
 40 
     | 
    
         
            +
                            Headers to include in every request. Default is ``None``
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 43 
     | 
    
         
            +
                            A tuple of `(total_timeout, connect_timeout)` in seconds to include in every request. Default is ``(30.0, 0.0)``
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                        ca_cert_path (``str``, *optional*):
         
     | 
| 
      
 46 
     | 
    
         
            +
                            Path to a CA certificate bundle file for SSL/TLS verification. Default is ``None``
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       35 
48 
     | 
    
         
             
                        force_verbose (``bool``, *optional*):
         
     | 
| 
       36 
49 
     | 
    
         
             
                            Force verbose output for all requests. Default is ``None``
         
     | 
| 
       37 
50 
     | 
    
         | 
| 
      
 51 
     | 
    
         
            +
                        raise_for_status (``bool``, *optional*):
         
     | 
| 
      
 52 
     | 
    
         
            +
                            If ``True``, automatically raises an :class:`redc.HTTPError` for responses with HTTP status codes
         
     | 
| 
      
 53 
     | 
    
         
            +
                            indicating an error (i.e., 4xx or 5xx) or for CURL errors (e.g., network issues, timeouts). Default is ``False``
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
       38 
55 
     | 
    
         
             
                        json_encoder (``Callable`` , *optional*):
         
     | 
| 
       39 
     | 
    
         
            -
                            A callable for encoding JSON data. Default is  
     | 
| 
      
 56 
     | 
    
         
            +
                            A callable for encoding JSON data. Default is :class:`redc.utils.json_dumps`
         
     | 
| 
       40 
57 
     | 
    
         
             
                    """
         
     | 
| 
       41 
58 
     | 
    
         | 
| 
       42 
59 
     | 
    
         
             
                    assert isinstance(base_url, (str, type(None))), "base_url must be string"
         
     | 
| 
       43 
60 
     | 
    
         
             
                    assert isinstance(buffer_size, int), "buffer_size must be int"
         
     | 
| 
       44 
     | 
    
         
            -
                    assert  
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
      
 61 
     | 
    
         
            +
                    assert isinstance(ca_cert_path, (str, type(None))), (
         
     | 
| 
      
 62 
     | 
    
         
            +
                        "ca_cert_path must be string"
         
     | 
| 
      
 63 
     | 
    
         
            +
                    )
         
     | 
| 
      
 64 
     | 
    
         
            +
                    assert isinstance(timeout, tuple) and len(timeout) == 2, (
         
     | 
| 
      
 65 
     | 
    
         
            +
                        "timeout must be a tuple of (total_timeout, connect_timeout)"
         
     | 
| 
      
 66 
     | 
    
         
            +
                    )
         
     | 
| 
       46 
67 
     | 
    
         
             
                    assert isinstance(force_verbose, (bool, type(None))), (
         
     | 
| 
       47 
68 
     | 
    
         
             
                        "force_verbose must be bool or None"
         
     | 
| 
       48 
69 
     | 
    
         
             
                    )
         
     | 
| 
      
 70 
     | 
    
         
            +
                    assert isinstance(raise_for_status, bool), "raise_for_status must be bool"
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    assert buffer_size >= 1024, "buffer_size must be bigger than 1024 bytes"
         
     | 
| 
       49 
73 
     | 
    
         | 
| 
       50 
74 
     | 
    
         
             
                    self.force_verbose = force_verbose
         
     | 
| 
      
 75 
     | 
    
         
            +
                    self.raise_for_status = raise_for_status
         
     | 
| 
       51 
76 
     | 
    
         | 
| 
       52 
77 
     | 
    
         
             
                    self.__base_url = (
         
     | 
| 
       53 
     | 
    
         
            -
                         
     | 
| 
      
 78 
     | 
    
         
            +
                        parse_base_url(base_url) if isinstance(base_url, str) else None
         
     | 
| 
       54 
79 
     | 
    
         
             
                    )
         
     | 
| 
      
 80 
     | 
    
         
            +
                    self.__default_headers = headers if isinstance(headers, dict) else {}
         
     | 
| 
      
 81 
     | 
    
         
            +
                    self.__timeout = timeout
         
     | 
| 
      
 82 
     | 
    
         
            +
                    self.__ca_cert_path = ca_cert_path if isinstance(ca_cert_path, str) else ""
         
     | 
| 
       55 
83 
     | 
    
         
             
                    self.__json_encoder = json_encoder
         
     | 
| 
       56 
84 
     | 
    
         
             
                    self.__redc_ext = RedC(buffer_size)
         
     | 
| 
       57 
85 
     | 
    
         | 
| 
         @@ -61,6 +89,16 @@ class Client: 
     | 
|
| 
       61 
89 
     | 
    
         
             
                async def __aexit__(self, exc_type, exc_val, exc_tb):
         
     | 
| 
       62 
90 
     | 
    
         
             
                    await self.close()
         
     | 
| 
       63 
91 
     | 
    
         | 
| 
      
 92 
     | 
    
         
            +
                @property
         
     | 
| 
      
 93 
     | 
    
         
            +
                def is_running(self):
         
     | 
| 
      
 94 
     | 
    
         
            +
                    """Checks if RedC is currently running
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 97 
     | 
    
         
            +
                        ``bool``: ``True`` if RedC is running, False otherwise
         
     | 
| 
      
 98 
     | 
    
         
            +
                    """
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                    return self.__redc_ext.is_running()
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
       64 
102 
     | 
    
         
             
                async def request(
         
     | 
| 
       65 
103 
     | 
    
         
             
                    self,
         
     | 
| 
       66 
104 
     | 
    
         
             
                    method: str,
         
     | 
| 
         @@ -70,12 +108,12 @@ class Client: 
     | 
|
| 
       70 
108 
     | 
    
         
             
                    data: dict[str, str] = None,
         
     | 
| 
       71 
109 
     | 
    
         
             
                    files: dict[str, str] = None,
         
     | 
| 
       72 
110 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       73 
     | 
    
         
            -
                    timeout:  
     | 
| 
       74 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 111 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       75 
112 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       76 
113 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       77 
114 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
       78 
115 
     | 
    
         
             
                    stream_callback: StreamCallback = None,
         
     | 
| 
      
 116 
     | 
    
         
            +
                    progress_callback: ProgressCallback = None,
         
     | 
| 
       79 
117 
     | 
    
         
             
                    verbose: bool = False,
         
     | 
| 
       80 
118 
     | 
    
         
             
                ):
         
     | 
| 
       81 
119 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -108,11 +146,9 @@ class Client: 
     | 
|
| 
       108 
146 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       109 
147 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       110 
148 
     | 
    
         | 
| 
       111 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       112 
     | 
    
         
            -
                             
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       115 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 149 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 150 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 151 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       116 
152 
     | 
    
         | 
| 
       117 
153 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       118 
154 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -126,6 +162,9 @@ class Client: 
     | 
|
| 
       126 
162 
     | 
    
         
             
                        stream_callback (:class:`redc.StreamCallback`, *optional*):
         
     | 
| 
       127 
163 
     | 
    
         
             
                            Callback for streaming response data. Default is ``None``
         
     | 
| 
       128 
164 
     | 
    
         | 
| 
      
 165 
     | 
    
         
            +
                        progress_callback (:class:`redc.ProgressCallback`, *optional*):
         
     | 
| 
      
 166 
     | 
    
         
            +
                            Callback for tracking upload and download progress. Default is ``None``
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
       129 
168 
     | 
    
         
             
                        verbose (``bool``, *optional*):
         
     | 
| 
       130 
169 
     | 
    
         
             
                            Whether to enable verbose output for the request. Default is ``False``
         
     | 
| 
       131 
170 
     | 
    
         | 
| 
         @@ -139,6 +178,12 @@ class Client: 
     | 
|
| 
       139 
178 
     | 
    
         | 
| 
       140 
179 
     | 
    
         
             
                        stream_callback = stream_callback.callback
         
     | 
| 
       141 
180 
     | 
    
         | 
| 
      
 181 
     | 
    
         
            +
                    if progress_callback is not None:
         
     | 
| 
      
 182 
     | 
    
         
            +
                        if not isinstance(progress_callback, ProgressCallback):
         
     | 
| 
      
 183 
     | 
    
         
            +
                            raise TypeError("progress_callback must be of type ProgressCallback")
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                        progress_callback = progress_callback.callback
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
       142 
187 
     | 
    
         
             
                    if form is not None:
         
     | 
| 
       143 
188 
     | 
    
         
             
                        if isinstance(form, dict):
         
     | 
| 
       144 
189 
     | 
    
         
             
                            form = urlencode(form)
         
     | 
| 
         @@ -162,6 +207,8 @@ class Client: 
     | 
|
| 
       162 
207 
     | 
    
         
             
                        if not isinstance(files, dict):
         
     | 
| 
       163 
208 
     | 
    
         
             
                            raise TypeError("files must be of type dict[str, str]")
         
     | 
| 
       164 
209 
     | 
    
         | 
| 
      
 210 
     | 
    
         
            +
                    timeout, connect_timeout = timeout if timeout is not None else self.__timeout
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
       165 
212 
     | 
    
         
             
                    if timeout <= 0:
         
     | 
| 
       166 
213 
     | 
    
         
             
                        raise ValueError("timeout must be greater than 0")
         
     | 
| 
       167 
214 
     | 
    
         | 
| 
         @@ -172,9 +219,12 @@ class Client: 
     | 
|
| 
       172 
219 
     | 
    
         | 
| 
       173 
220 
     | 
    
         
             
                    if headers is not None:
         
     | 
| 
       174 
221 
     | 
    
         
             
                        if isinstance(headers, dict):
         
     | 
| 
      
 222 
     | 
    
         
            +
                            headers = {**self.__default_headers, **headers}
         
     | 
| 
       175 
223 
     | 
    
         
             
                            headers = [f"{k}: {v}" for k, v in headers.items()]
         
     | 
| 
       176 
224 
     | 
    
         
             
                        else:
         
     | 
| 
       177 
225 
     | 
    
         
             
                            raise TypeError("headers must be of type dict[str, str]")
         
     | 
| 
      
 226 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 227 
     | 
    
         
            +
                        headers = [f"{k}: {v}" for k, v in self.__default_headers.items()]
         
     | 
| 
       178 
228 
     | 
    
         | 
| 
       179 
229 
     | 
    
         
             
                    if self.__base_url:
         
     | 
| 
       180 
230 
     | 
    
         
             
                        url = f"{self.__base_url}{url.lstrip('/')}"
         
     | 
| 
         @@ -193,22 +243,25 @@ class Client: 
     | 
|
| 
       193 
243 
     | 
    
         
             
                                allow_redirect=allow_redirect,
         
     | 
| 
       194 
244 
     | 
    
         
             
                                proxy_url=proxy_url,
         
     | 
| 
       195 
245 
     | 
    
         
             
                                verify=verify,
         
     | 
| 
      
 246 
     | 
    
         
            +
                                ca_cert_path=self.__ca_cert_path,
         
     | 
| 
       196 
247 
     | 
    
         
             
                                stream_callback=stream_callback,
         
     | 
| 
      
 248 
     | 
    
         
            +
                                progress_callback=progress_callback,
         
     | 
| 
       197 
249 
     | 
    
         
             
                                verbose=self.force_verbose or verbose,
         
     | 
| 
       198 
250 
     | 
    
         
             
                            )
         
     | 
| 
       199 
     | 
    
         
            -
                        )
         
     | 
| 
      
 251 
     | 
    
         
            +
                        ),
         
     | 
| 
      
 252 
     | 
    
         
            +
                        raise_for_status=self.raise_for_status,
         
     | 
| 
       200 
253 
     | 
    
         
             
                    )
         
     | 
| 
       201 
254 
     | 
    
         | 
| 
       202 
255 
     | 
    
         
             
                async def get(
         
     | 
| 
       203 
256 
     | 
    
         
             
                    self,
         
     | 
| 
       204 
257 
     | 
    
         
             
                    url: str,
         
     | 
| 
       205 
258 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       206 
     | 
    
         
            -
                    timeout:  
     | 
| 
       207 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 259 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       208 
260 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       209 
261 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       210 
262 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
       211 
263 
     | 
    
         
             
                    stream_callback: StreamCallback = None,
         
     | 
| 
      
 264 
     | 
    
         
            +
                    progress_callback: ProgressCallback = None,
         
     | 
| 
       212 
265 
     | 
    
         
             
                    verbose: bool = False,
         
     | 
| 
       213 
266 
     | 
    
         
             
                ):
         
     | 
| 
       214 
267 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -226,11 +279,9 @@ class Client: 
     | 
|
| 
       226 
279 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       227 
280 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       228 
281 
     | 
    
         | 
| 
       229 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       230 
     | 
    
         
            -
                             
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
       232 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       233 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 282 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 283 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 284 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       234 
285 
     | 
    
         | 
| 
       235 
286 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       236 
287 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -244,22 +295,26 @@ class Client: 
     | 
|
| 
       244 
295 
     | 
    
         
             
                        stream_callback (:class:`redc.StreamCallback`, *optional*):
         
     | 
| 
       245 
296 
     | 
    
         
             
                            Callback for streaming response data. Default is ``None``
         
     | 
| 
       246 
297 
     | 
    
         | 
| 
      
 298 
     | 
    
         
            +
                        progress_callback (:class:`redc.ProgressCallback`, *optional*):
         
     | 
| 
      
 299 
     | 
    
         
            +
                            Callback for tracking upload and download progress. Default is ``None``
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
       247 
301 
     | 
    
         
             
                        verbose (``bool``, *optional*):
         
     | 
| 
       248 
302 
     | 
    
         
             
                            Whether to enable verbose output for the request. Default is ``False``
         
     | 
| 
       249 
303 
     | 
    
         | 
| 
       250 
304 
     | 
    
         
             
                    Returns:
         
     | 
| 
       251 
305 
     | 
    
         
             
                        :class:`redc.Response`
         
     | 
| 
       252 
306 
     | 
    
         
             
                    """
         
     | 
| 
      
 307 
     | 
    
         
            +
             
     | 
| 
       253 
308 
     | 
    
         
             
                    return await self.request(
         
     | 
| 
       254 
309 
     | 
    
         
             
                        method="GET",
         
     | 
| 
       255 
310 
     | 
    
         
             
                        url=url,
         
     | 
| 
       256 
311 
     | 
    
         
             
                        headers=headers,
         
     | 
| 
       257 
312 
     | 
    
         
             
                        timeout=timeout,
         
     | 
| 
       258 
     | 
    
         
            -
                        connect_timeout=connect_timeout,
         
     | 
| 
       259 
313 
     | 
    
         
             
                        allow_redirect=allow_redirect,
         
     | 
| 
       260 
314 
     | 
    
         
             
                        proxy_url=proxy_url,
         
     | 
| 
       261 
315 
     | 
    
         
             
                        verify=verify,
         
     | 
| 
       262 
316 
     | 
    
         
             
                        stream_callback=stream_callback,
         
     | 
| 
      
 317 
     | 
    
         
            +
                        progress_callback=progress_callback,
         
     | 
| 
       263 
318 
     | 
    
         
             
                        verbose=self.force_verbose or verbose,
         
     | 
| 
       264 
319 
     | 
    
         
             
                    )
         
     | 
| 
       265 
320 
     | 
    
         | 
| 
         @@ -267,8 +322,7 @@ class Client: 
     | 
|
| 
       267 
322 
     | 
    
         
             
                    self,
         
     | 
| 
       268 
323 
     | 
    
         
             
                    url: str,
         
     | 
| 
       269 
324 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       270 
     | 
    
         
            -
                    timeout:  
     | 
| 
       271 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 325 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       272 
326 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       273 
327 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       274 
328 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
         @@ -289,11 +343,9 @@ class Client: 
     | 
|
| 
       289 
343 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       290 
344 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       291 
345 
     | 
    
         | 
| 
       292 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       293 
     | 
    
         
            -
                             
     | 
| 
       294 
     | 
    
         
            -
             
     | 
| 
       295 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       296 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 346 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 347 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 348 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       297 
349 
     | 
    
         | 
| 
       298 
350 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       299 
351 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -315,7 +367,6 @@ class Client: 
     | 
|
| 
       315 
367 
     | 
    
         
             
                        url=url,
         
     | 
| 
       316 
368 
     | 
    
         
             
                        headers=headers,
         
     | 
| 
       317 
369 
     | 
    
         
             
                        timeout=timeout,
         
     | 
| 
       318 
     | 
    
         
            -
                        connect_timeout=connect_timeout,
         
     | 
| 
       319 
370 
     | 
    
         
             
                        allow_redirect=allow_redirect,
         
     | 
| 
       320 
371 
     | 
    
         
             
                        proxy_url=proxy_url,
         
     | 
| 
       321 
372 
     | 
    
         
             
                        verify=verify,
         
     | 
| 
         @@ -330,12 +381,12 @@ class Client: 
     | 
|
| 
       330 
381 
     | 
    
         
             
                    data: dict[str, str] = None,
         
     | 
| 
       331 
382 
     | 
    
         
             
                    files: dict[str, str] = None,
         
     | 
| 
       332 
383 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       333 
     | 
    
         
            -
                    timeout:  
     | 
| 
       334 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 384 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       335 
385 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       336 
386 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       337 
387 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
       338 
388 
     | 
    
         
             
                    stream_callback: StreamCallback = None,
         
     | 
| 
      
 389 
     | 
    
         
            +
                    progress_callback: ProgressCallback = None,
         
     | 
| 
       339 
390 
     | 
    
         
             
                    verbose: bool = False,
         
     | 
| 
       340 
391 
     | 
    
         
             
                ):
         
     | 
| 
       341 
392 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -369,11 +420,9 @@ class Client: 
     | 
|
| 
       369 
420 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       370 
421 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       371 
422 
     | 
    
         | 
| 
       372 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       373 
     | 
    
         
            -
                             
     | 
| 
       374 
     | 
    
         
            -
             
     | 
| 
       375 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       376 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 423 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 424 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 425 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       377 
426 
     | 
    
         | 
| 
       378 
427 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       379 
428 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -387,6 +436,9 @@ class Client: 
     | 
|
| 
       387 
436 
     | 
    
         
             
                        stream_callback (:class:`redc.StreamCallback`, *optional*):
         
     | 
| 
       388 
437 
     | 
    
         
             
                            Callback for streaming response data. Default is ``None``
         
     | 
| 
       389 
438 
     | 
    
         | 
| 
      
 439 
     | 
    
         
            +
                        progress_callback (:class:`redc.ProgressCallback`, *optional*):
         
     | 
| 
      
 440 
     | 
    
         
            +
                            Callback for tracking upload and download progress. Default is ``None``
         
     | 
| 
      
 441 
     | 
    
         
            +
             
     | 
| 
       390 
442 
     | 
    
         
             
                        verbose (``bool``, *optional*):
         
     | 
| 
       391 
443 
     | 
    
         
             
                            Whether to enable verbose output for the request. Default is ``False``
         
     | 
| 
       392 
444 
     | 
    
         | 
| 
         @@ -402,11 +454,11 @@ class Client: 
     | 
|
| 
       402 
454 
     | 
    
         
             
                        files=files,
         
     | 
| 
       403 
455 
     | 
    
         
             
                        headers=headers,
         
     | 
| 
       404 
456 
     | 
    
         
             
                        timeout=timeout,
         
     | 
| 
       405 
     | 
    
         
            -
                        connect_timeout=connect_timeout,
         
     | 
| 
       406 
457 
     | 
    
         
             
                        allow_redirect=allow_redirect,
         
     | 
| 
       407 
458 
     | 
    
         
             
                        proxy_url=proxy_url,
         
     | 
| 
       408 
459 
     | 
    
         
             
                        verify=verify,
         
     | 
| 
       409 
460 
     | 
    
         
             
                        stream_callback=stream_callback,
         
     | 
| 
      
 461 
     | 
    
         
            +
                        progress_callback=progress_callback,
         
     | 
| 
       410 
462 
     | 
    
         
             
                        verbose=self.force_verbose or verbose,
         
     | 
| 
       411 
463 
     | 
    
         
             
                    )
         
     | 
| 
       412 
464 
     | 
    
         | 
| 
         @@ -418,12 +470,12 @@ class Client: 
     | 
|
| 
       418 
470 
     | 
    
         
             
                    data: dict[str, str] = None,
         
     | 
| 
       419 
471 
     | 
    
         
             
                    files: dict[str, str] = None,
         
     | 
| 
       420 
472 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       421 
     | 
    
         
            -
                    timeout:  
     | 
| 
       422 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 473 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       423 
474 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       424 
475 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       425 
476 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
       426 
477 
     | 
    
         
             
                    stream_callback: StreamCallback = None,
         
     | 
| 
      
 478 
     | 
    
         
            +
                    progress_callback: ProgressCallback = None,
         
     | 
| 
       427 
479 
     | 
    
         
             
                    verbose: bool = False,
         
     | 
| 
       428 
480 
     | 
    
         
             
                ):
         
     | 
| 
       429 
481 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -457,11 +509,9 @@ class Client: 
     | 
|
| 
       457 
509 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       458 
510 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       459 
511 
     | 
    
         | 
| 
       460 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       461 
     | 
    
         
            -
                             
     | 
| 
       462 
     | 
    
         
            -
             
     | 
| 
       463 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       464 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 512 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 513 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 514 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       465 
515 
     | 
    
         | 
| 
       466 
516 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       467 
517 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -475,6 +525,9 @@ class Client: 
     | 
|
| 
       475 
525 
     | 
    
         
             
                        stream_callback (:class:`redc.StreamCallback`, *optional*):
         
     | 
| 
       476 
526 
     | 
    
         
             
                            Callback for streaming response data. Default is ``None``
         
     | 
| 
       477 
527 
     | 
    
         | 
| 
      
 528 
     | 
    
         
            +
                        progress_callback (:class:`redc.ProgressCallback`, *optional*):
         
     | 
| 
      
 529 
     | 
    
         
            +
                            Callback for tracking upload and download progress. Default is ``None``
         
     | 
| 
      
 530 
     | 
    
         
            +
             
     | 
| 
       478 
531 
     | 
    
         
             
                        verbose (``bool``, *optional*):
         
     | 
| 
       479 
532 
     | 
    
         
             
                            Whether to enable verbose output for the request. Default is ``False``
         
     | 
| 
       480 
533 
     | 
    
         | 
| 
         @@ -490,11 +543,11 @@ class Client: 
     | 
|
| 
       490 
543 
     | 
    
         
             
                        files=files,
         
     | 
| 
       491 
544 
     | 
    
         
             
                        headers=headers,
         
     | 
| 
       492 
545 
     | 
    
         
             
                        timeout=timeout,
         
     | 
| 
       493 
     | 
    
         
            -
                        connect_timeout=connect_timeout,
         
     | 
| 
       494 
546 
     | 
    
         
             
                        allow_redirect=allow_redirect,
         
     | 
| 
       495 
547 
     | 
    
         
             
                        proxy_url=proxy_url,
         
     | 
| 
       496 
548 
     | 
    
         
             
                        verify=verify,
         
     | 
| 
       497 
549 
     | 
    
         
             
                        stream_callback=stream_callback,
         
     | 
| 
      
 550 
     | 
    
         
            +
                        progress_callback=progress_callback,
         
     | 
| 
       498 
551 
     | 
    
         
             
                        verbose=self.force_verbose or verbose,
         
     | 
| 
       499 
552 
     | 
    
         
             
                    )
         
     | 
| 
       500 
553 
     | 
    
         | 
| 
         @@ -506,12 +559,12 @@ class Client: 
     | 
|
| 
       506 
559 
     | 
    
         
             
                    data: dict[str, str] = None,
         
     | 
| 
       507 
560 
     | 
    
         
             
                    files: dict[str, str] = None,
         
     | 
| 
       508 
561 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       509 
     | 
    
         
            -
                    timeout:  
     | 
| 
       510 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 562 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       511 
563 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       512 
564 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       513 
565 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
       514 
566 
     | 
    
         
             
                    stream_callback: StreamCallback = None,
         
     | 
| 
      
 567 
     | 
    
         
            +
                    progress_callback: ProgressCallback = None,
         
     | 
| 
       515 
568 
     | 
    
         
             
                    verbose: bool = False,
         
     | 
| 
       516 
569 
     | 
    
         
             
                ):
         
     | 
| 
       517 
570 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -545,11 +598,9 @@ class Client: 
     | 
|
| 
       545 
598 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       546 
599 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       547 
600 
     | 
    
         | 
| 
       548 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       549 
     | 
    
         
            -
                             
     | 
| 
       550 
     | 
    
         
            -
             
     | 
| 
       551 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       552 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 601 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 602 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 603 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       553 
604 
     | 
    
         | 
| 
       554 
605 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       555 
606 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -563,6 +614,9 @@ class Client: 
     | 
|
| 
       563 
614 
     | 
    
         
             
                        stream_callback (:class:`redc.StreamCallback`, *optional*):
         
     | 
| 
       564 
615 
     | 
    
         
             
                            Callback for streaming response data. Default is ``None``
         
     | 
| 
       565 
616 
     | 
    
         | 
| 
      
 617 
     | 
    
         
            +
                        progress_callback (:class:`redc.ProgressCallback`, *optional*):
         
     | 
| 
      
 618 
     | 
    
         
            +
                            Callback for tracking upload and download progress. Default is ``None``
         
     | 
| 
      
 619 
     | 
    
         
            +
             
     | 
| 
       566 
620 
     | 
    
         
             
                        verbose (``bool``, *optional*):
         
     | 
| 
       567 
621 
     | 
    
         
             
                            Whether to enable verbose output for the request. Default is ``False``
         
     | 
| 
       568 
622 
     | 
    
         | 
| 
         @@ -579,11 +633,11 @@ class Client: 
     | 
|
| 
       579 
633 
     | 
    
         
             
                        files=files,
         
     | 
| 
       580 
634 
     | 
    
         
             
                        headers=headers,
         
     | 
| 
       581 
635 
     | 
    
         
             
                        timeout=timeout,
         
     | 
| 
       582 
     | 
    
         
            -
                        connect_timeout=connect_timeout,
         
     | 
| 
       583 
636 
     | 
    
         
             
                        allow_redirect=allow_redirect,
         
     | 
| 
       584 
637 
     | 
    
         
             
                        proxy_url=proxy_url,
         
     | 
| 
       585 
638 
     | 
    
         
             
                        verify=verify,
         
     | 
| 
       586 
639 
     | 
    
         
             
                        stream_callback=stream_callback,
         
     | 
| 
      
 640 
     | 
    
         
            +
                        progress_callback=progress_callback,
         
     | 
| 
       587 
641 
     | 
    
         
             
                        verbose=self.force_verbose or verbose,
         
     | 
| 
       588 
642 
     | 
    
         
             
                    )
         
     | 
| 
       589 
643 
     | 
    
         | 
| 
         @@ -591,12 +645,12 @@ class Client: 
     | 
|
| 
       591 
645 
     | 
    
         
             
                    self,
         
     | 
| 
       592 
646 
     | 
    
         
             
                    url: str,
         
     | 
| 
       593 
647 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       594 
     | 
    
         
            -
                    timeout:  
     | 
| 
       595 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 648 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       596 
649 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       597 
650 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       598 
651 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
       599 
652 
     | 
    
         
             
                    stream_callback: StreamCallback = None,
         
     | 
| 
      
 653 
     | 
    
         
            +
                    progress_callback: ProgressCallback = None,
         
     | 
| 
       600 
654 
     | 
    
         
             
                    verbose: bool = False,
         
     | 
| 
       601 
655 
     | 
    
         
             
                ):
         
     | 
| 
       602 
656 
     | 
    
         
             
                    """
         
     | 
| 
         @@ -614,11 +668,9 @@ class Client: 
     | 
|
| 
       614 
668 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       615 
669 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       616 
670 
     | 
    
         | 
| 
       617 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       618 
     | 
    
         
            -
                             
     | 
| 
       619 
     | 
    
         
            -
             
     | 
| 
       620 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       621 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 671 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 672 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 673 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       622 
674 
     | 
    
         | 
| 
       623 
675 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       624 
676 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -632,6 +684,9 @@ class Client: 
     | 
|
| 
       632 
684 
     | 
    
         
             
                        stream_callback (:class:`redc.StreamCallback`, *optional*):
         
     | 
| 
       633 
685 
     | 
    
         
             
                            Callback for streaming response data. Default is ``None``
         
     | 
| 
       634 
686 
     | 
    
         | 
| 
      
 687 
     | 
    
         
            +
                        progress_callback (:class:`redc.ProgressCallback`, *optional*):
         
     | 
| 
      
 688 
     | 
    
         
            +
                            Callback for tracking upload and download progress. Default is ``None``
         
     | 
| 
      
 689 
     | 
    
         
            +
             
     | 
| 
       635 
690 
     | 
    
         
             
                        verbose (``bool``, *optional*):
         
     | 
| 
       636 
691 
     | 
    
         
             
                            Whether to enable verbose output for the request. Default is ``False``
         
     | 
| 
       637 
692 
     | 
    
         | 
| 
         @@ -643,11 +698,11 @@ class Client: 
     | 
|
| 
       643 
698 
     | 
    
         
             
                        url=url,
         
     | 
| 
       644 
699 
     | 
    
         
             
                        headers=headers,
         
     | 
| 
       645 
700 
     | 
    
         
             
                        timeout=timeout,
         
     | 
| 
       646 
     | 
    
         
            -
                        connect_timeout=connect_timeout,
         
     | 
| 
       647 
701 
     | 
    
         
             
                        allow_redirect=allow_redirect,
         
     | 
| 
       648 
702 
     | 
    
         
             
                        proxy_url=proxy_url,
         
     | 
| 
       649 
703 
     | 
    
         
             
                        verify=verify,
         
     | 
| 
       650 
704 
     | 
    
         
             
                        stream_callback=stream_callback,
         
     | 
| 
      
 705 
     | 
    
         
            +
                        progress_callback=progress_callback,
         
     | 
| 
       651 
706 
     | 
    
         
             
                        verbose=self.force_verbose or verbose,
         
     | 
| 
       652 
707 
     | 
    
         
             
                    )
         
     | 
| 
       653 
708 
     | 
    
         | 
| 
         @@ -655,8 +710,7 @@ class Client: 
     | 
|
| 
       655 
710 
     | 
    
         
             
                    self,
         
     | 
| 
       656 
711 
     | 
    
         
             
                    url: str,
         
     | 
| 
       657 
712 
     | 
    
         
             
                    headers: dict[str, str] = None,
         
     | 
| 
       658 
     | 
    
         
            -
                    timeout:  
     | 
| 
       659 
     | 
    
         
            -
                    connect_timeout: float = 0.0,
         
     | 
| 
      
 713 
     | 
    
         
            +
                    timeout: tuple = None,
         
     | 
| 
       660 
714 
     | 
    
         
             
                    allow_redirect: bool = True,
         
     | 
| 
       661 
715 
     | 
    
         
             
                    proxy_url: str = "",
         
     | 
| 
       662 
716 
     | 
    
         
             
                    verify: bool = True,
         
     | 
| 
         @@ -677,11 +731,9 @@ class Client: 
     | 
|
| 
       677 
731 
     | 
    
         
             
                        headers (``dict[str, str]``, *optional*):
         
     | 
| 
       678 
732 
     | 
    
         
             
                            Headers to include in the request. Default is ``None``
         
     | 
| 
       679 
733 
     | 
    
         | 
| 
       680 
     | 
    
         
            -
                        timeout (`` 
     | 
| 
       681 
     | 
    
         
            -
                             
     | 
| 
       682 
     | 
    
         
            -
             
     | 
| 
       683 
     | 
    
         
            -
                        connect_timeout (``float``, *optional*):
         
     | 
| 
       684 
     | 
    
         
            -
                            The connection timeout for the request in seconds. Default is ``0.0``
         
     | 
| 
      
 734 
     | 
    
         
            +
                        timeout (``tuple``, *optional*):
         
     | 
| 
      
 735 
     | 
    
         
            +
                            A tuple of ``(total_timeout, connect_timeout)`` in seconds to override the default timeout.
         
     | 
| 
      
 736 
     | 
    
         
            +
                            If ``None``, the default timeout specified in ``Client`` is used.
         
     | 
| 
       685 
737 
     | 
    
         | 
| 
       686 
738 
     | 
    
         
             
                        allow_redirect (``bool``, *optional*):
         
     | 
| 
       687 
739 
     | 
    
         
             
                            Whether to allow redirects. Default is ``True``
         
     | 
| 
         @@ -703,7 +755,6 @@ class Client: 
     | 
|
| 
       703 
755 
     | 
    
         
             
                        url=url,
         
     | 
| 
       704 
756 
     | 
    
         
             
                        headers=headers,
         
     | 
| 
       705 
757 
     | 
    
         
             
                        timeout=timeout,
         
     | 
| 
       706 
     | 
    
         
            -
                        connect_timeout=connect_timeout,
         
     | 
| 
       707 
758 
     | 
    
         
             
                        allow_redirect=allow_redirect,
         
     | 
| 
       708 
759 
     | 
    
         
             
                        proxy_url=proxy_url,
         
     | 
| 
       709 
760 
     | 
    
         
             
                        verify=verify,
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from ..codes import HTTPStatus
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class HTTPError(Exception):
         
     | 
| 
      
 5 
     | 
    
         
            +
                """Exception raised for HTTP and CURL-related errors"""
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def __init__(self, status_code: int, curl_error_code: int, curl_error_message: str):
         
     | 
| 
      
 8 
     | 
    
         
            +
                    self.status_code = status_code
         
     | 
| 
      
 9 
     | 
    
         
            +
                    self.curl_error_code = curl_error_code
         
     | 
| 
      
 10 
     | 
    
         
            +
                    self.curl_error_message = curl_error_message
         
     | 
| 
      
 11 
     | 
    
         
            +
                    self.is_curl_error = status_code == -1
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    if self.is_curl_error:
         
     | 
| 
      
 14 
     | 
    
         
            +
                        super().__init__(f"CURL {self.curl_error_code}: {self.curl_error_message}")
         
     | 
| 
      
 15 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 16 
     | 
    
         
            +
                        short_description = HTTPStatus.get_description(self.status_code)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                        super().__init__(
         
     | 
| 
      
 19 
     | 
    
         
            +
                            self.status_code
         
     | 
| 
      
 20 
     | 
    
         
            +
                            if not short_description
         
     | 
| 
      
 21 
     | 
    
         
            +
                            else f"{self.status_code} - {short_description}"
         
     | 
| 
      
 22 
     | 
    
         
            +
                        )
         
     | 
| 
         @@ -7,6 +7,7 @@ RedC::RedC(const long &buffer) { 
     | 
|
| 
       7 
7 
     | 
    
         
             
              {
         
     | 
| 
       8 
8 
     | 
    
         
             
                acq_gil gil;
         
     | 
| 
       9 
9 
     | 
    
         
             
                loop_ = nb::module_::import_("asyncio").attr("get_event_loop")();
         
     | 
| 
      
 10 
     | 
    
         
            +
                call_soon_threadsafe_ = loop_.attr("call_soon_threadsafe");
         
     | 
| 
       10 
11 
     | 
    
         
             
              }
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
              static CurlGlobalInit g;
         
     | 
| 
         @@ -38,24 +39,26 @@ bool RedC::is_running() { 
     | 
|
| 
       38 
39 
     | 
    
         
             
            void RedC::close() {
         
     | 
| 
       39 
40 
     | 
    
         
             
              if (running_) {
         
     | 
| 
       40 
41 
     | 
    
         
             
                running_ = false;
         
     | 
| 
       41 
     | 
    
         
            -
                curl_multi_wakeup(multi_handle_);
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
                if (worker_thread_.joinable()) {
         
     | 
| 
      
 44 
     | 
    
         
            +
                  curl_multi_wakeup(multi_handle_);
         
     | 
| 
       44 
45 
     | 
    
         
             
                  worker_thread_.join();
         
     | 
| 
       45 
46 
     | 
    
         
             
                }
         
     | 
| 
       46 
47 
     | 
    
         | 
| 
       47 
48 
     | 
    
         
             
                cleanup();
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
       48 
50 
     | 
    
         
             
                curl_multi_cleanup(multi_handle_);
         
     | 
| 
       49 
51 
     | 
    
         
             
              }
         
     | 
| 
       50 
52 
     | 
    
         
             
            }
         
     | 
| 
       51 
53 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
            py_object RedC::request(const  
     | 
| 
      
 54 
     | 
    
         
            +
            py_object RedC::request(const char *method, const char *url, const char *raw_data, const py_object &data,
         
     | 
| 
       53 
55 
     | 
    
         
             
                                    const py_object &files, const py_object &headers, const long &timeout_ms,
         
     | 
| 
       54 
     | 
    
         
            -
                                    const long &connect_timeout_ms, const bool &allow_redirect, const  
     | 
| 
       55 
     | 
    
         
            -
                                    const bool &verify, const  
     | 
| 
      
 56 
     | 
    
         
            +
                                    const long &connect_timeout_ms, const bool &allow_redirect, const char *proxy_url,
         
     | 
| 
      
 57 
     | 
    
         
            +
                                    const bool &verify, const char *ca_cert_path, const py_object &stream_callback,
         
     | 
| 
      
 58 
     | 
    
         
            +
                                    const py_object &progress_callback, const bool &verbose) {
         
     | 
| 
       56 
59 
     | 
    
         
             
              CHECK_RUNNING();
         
     | 
| 
       57 
60 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
              if (method 
     | 
| 
      
 61 
     | 
    
         
            +
              if (isNullOrEmpty(method) || isNullOrEmpty(url)) {
         
     | 
| 
       59 
62 
     | 
    
         
             
                throw std::invalid_argument("method or url must be non-empty");
         
     | 
| 
       60 
63 
     | 
    
         
             
              }
         
     | 
| 
       61 
64 
     | 
    
         | 
| 
         @@ -64,12 +67,13 @@ py_object RedC::request(const string &method, const string &url, const char *raw 
     | 
|
| 
       64 
67 
     | 
    
         
             
                throw std::runtime_error("Failed to create CURL easy handle");
         
     | 
| 
       65 
68 
     | 
    
         
             
              }
         
     | 
| 
       66 
69 
     | 
    
         | 
| 
      
 70 
     | 
    
         
            +
              bool is_nobody = (strcmp(method, "HEAD") == 0 || strcmp(method, "OPTIONS") == 0);
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
       67 
72 
     | 
    
         
             
              try {
         
     | 
| 
       68 
73 
     | 
    
         
             
                curl_easy_setopt(easy, CURLOPT_BUFFERSIZE, buffer_size_);
         
     | 
| 
       69 
     | 
    
         
            -
                curl_easy_setopt(easy, CURLOPT_URL, url 
     | 
| 
       70 
     | 
    
         
            -
                curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, method 
     | 
| 
      
 74 
     | 
    
         
            +
                curl_easy_setopt(easy, CURLOPT_URL, url);
         
     | 
| 
      
 75 
     | 
    
         
            +
                curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, method);
         
     | 
| 
       71 
76 
     | 
    
         | 
| 
       72 
     | 
    
         
            -
                curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 1L);
         
     | 
| 
       73 
77 
     | 
    
         
             
                curl_easy_setopt(easy, CURLOPT_TIMEOUT_MS, timeout_ms);
         
     | 
| 
       74 
78 
     | 
    
         | 
| 
       75 
79 
     | 
    
         
             
                curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, &RedC::header_callback);
         
     | 
| 
         @@ -82,7 +86,7 @@ py_object RedC::request(const string &method, const string &url, const char *raw 
     | 
|
| 
       82 
86 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT_MS, connect_timeout_ms);
         
     | 
| 
       83 
87 
     | 
    
         
             
                }
         
     | 
| 
       84 
88 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                if ( 
     | 
| 
      
 89 
     | 
    
         
            +
                if (is_nobody) {
         
     | 
| 
       86 
90 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_NOBODY, 1L);
         
     | 
| 
       87 
91 
     | 
    
         
             
                } else {
         
     | 
| 
       88 
92 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, &RedC::write_callback);
         
     | 
| 
         @@ -93,17 +97,19 @@ py_object RedC::request(const string &method, const string &url, const char *raw 
     | 
|
| 
       93 
97 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 30L);
         
     | 
| 
       94 
98 
     | 
    
         
             
                }
         
     | 
| 
       95 
99 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
                if (!proxy_url 
     | 
| 
       97 
     | 
    
         
            -
                  curl_easy_setopt(easy, CURLOPT_PROXY, proxy_url 
     | 
| 
      
 100 
     | 
    
         
            +
                if (!isNullOrEmpty(proxy_url)) {
         
     | 
| 
      
 101 
     | 
    
         
            +
                  curl_easy_setopt(easy, CURLOPT_PROXY, proxy_url);
         
     | 
| 
       98 
102 
     | 
    
         
             
                }
         
     | 
| 
       99 
103 
     | 
    
         | 
| 
       100 
104 
     | 
    
         
             
                if (!verify) {
         
     | 
| 
       101 
105 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0);
         
     | 
| 
       102 
106 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, 0);
         
     | 
| 
      
 107 
     | 
    
         
            +
                } else if (!isNullOrEmpty(ca_cert_path)) {
         
     | 
| 
      
 108 
     | 
    
         
            +
                  curl_easy_setopt(easy, CURLOPT_CAINFO, ca_cert_path);
         
     | 
| 
       103 
109 
     | 
    
         
             
                }
         
     | 
| 
       104 
110 
     | 
    
         | 
| 
       105 
111 
     | 
    
         
             
                CurlMime curl_mime_;
         
     | 
| 
       106 
     | 
    
         
            -
                if (raw_data 
     | 
| 
      
 112 
     | 
    
         
            +
                if (!isNullOrEmpty(raw_data)) {
         
     | 
| 
       107 
113 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_POSTFIELDS, raw_data);
         
     | 
| 
       108 
114 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)strlen(raw_data));
         
     | 
| 
       109 
115 
     | 
    
         
             
                } else if (!data.is_none() || !files.is_none()) {
         
     | 
| 
         @@ -165,13 +171,23 @@ py_object RedC::request(const string &method, const string &url, const char *raw 
     | 
|
| 
       165 
171 
     | 
    
         
             
                  d.request_headers = std::move(slist_headers);
         
     | 
| 
       166 
172 
     | 
    
         
             
                  d.curl_mime_ = std::move(curl_mime_);
         
     | 
| 
       167 
173 
     | 
    
         | 
| 
       168 
     | 
    
         
            -
                  if (!stream_callback.is_none()) {
         
     | 
| 
       169 
     | 
    
         
            -
                    d.stream_callback = stream_callback;
         
     | 
| 
       170 
     | 
    
         
            -
                  }
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
174 
     | 
    
         
             
                  curl_easy_setopt(easy, CURLOPT_HEADERDATA, &d);
         
     | 
| 
       173 
     | 
    
         
            -
                  if ( 
     | 
| 
      
 175 
     | 
    
         
            +
                  if (!is_nobody) {
         
     | 
| 
       174 
176 
     | 
    
         
             
                    curl_easy_setopt(easy, CURLOPT_WRITEDATA, &d);
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                    if (!stream_callback.is_none()) {
         
     | 
| 
      
 179 
     | 
    
         
            +
                      d.stream_callback = stream_callback;
         
     | 
| 
      
 180 
     | 
    
         
            +
                    }
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                    if (!progress_callback.is_none()) {
         
     | 
| 
      
 183 
     | 
    
         
            +
                      d.progress_callback = progress_callback;
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                      curl_easy_setopt(easy, CURLOPT_XFERINFODATA, &d);
         
     | 
| 
      
 186 
     | 
    
         
            +
                      curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 0L);
         
     | 
| 
      
 187 
     | 
    
         
            +
                      curl_easy_setopt(easy, CURLOPT_XFERINFOFUNCTION, &RedC::progress_callback);
         
     | 
| 
      
 188 
     | 
    
         
            +
                    } else {
         
     | 
| 
      
 189 
     | 
    
         
            +
                      curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 1L);
         
     | 
| 
      
 190 
     | 
    
         
            +
                    }
         
     | 
| 
       175 
191 
     | 
    
         
             
                  }
         
     | 
| 
       176 
192 
     | 
    
         
             
                }
         
     | 
| 
       177 
193 
     | 
    
         | 
| 
         @@ -187,9 +203,6 @@ py_object RedC::request(const string &method, const string &url, const char *raw 
     | 
|
| 
       187 
203 
     | 
    
         | 
| 
       188 
204 
     | 
    
         
             
            void RedC::worker_loop() {
         
     | 
| 
       189 
205 
     | 
    
         
             
              while (running_) {
         
     | 
| 
       190 
     | 
    
         
            -
                if (!running_)
         
     | 
| 
       191 
     | 
    
         
            -
                  break;
         
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
       193 
206 
     | 
    
         
             
                CURL *e;
         
     | 
| 
       194 
207 
     | 
    
         
             
                if (queue_.try_dequeue(e)) {
         
     | 
| 
       195 
208 
     | 
    
         
             
                  CURLMcode res = curl_multi_add_handle(multi_handle_, e);
         
     | 
| 
         @@ -202,7 +215,7 @@ void RedC::worker_loop() { 
     | 
|
| 
       202 
215 
     | 
    
         
             
                      lock.unlock();
         
     | 
| 
       203 
216 
     | 
    
         
             
                      {
         
     | 
| 
       204 
217 
     | 
    
         
             
                        acq_gil gil;
         
     | 
| 
       205 
     | 
    
         
            -
                         
     | 
| 
      
 218 
     | 
    
         
            +
                        call_soon_threadsafe_(nb::cpp_function([data = std::move(data), res]() {
         
     | 
| 
       206 
219 
     | 
    
         
             
                          data.future.attr("set_result")(nb::make_tuple(-1, NULL, NULL, (int)res, curl_multi_strerror(res)));
         
     | 
| 
       207 
220 
     | 
    
         
             
                        }));
         
     | 
| 
       208 
221 
     | 
    
         
             
                      }
         
     | 
| 
         @@ -214,6 +227,10 @@ void RedC::worker_loop() { 
     | 
|
| 
       214 
227 
     | 
    
         
             
                  curl_multi_poll(multi_handle_, nullptr, 0, 30000, &numfds);
         
     | 
| 
       215 
228 
     | 
    
         
             
                }
         
     | 
| 
       216 
229 
     | 
    
         | 
| 
      
 230 
     | 
    
         
            +
                if (!running_) {
         
     | 
| 
      
 231 
     | 
    
         
            +
                  return;
         
     | 
| 
      
 232 
     | 
    
         
            +
                }
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
       217 
234 
     | 
    
         
             
                curl_multi_perform(multi_handle_, &still_running_);
         
     | 
| 
       218 
235 
     | 
    
         | 
| 
       219 
236 
     | 
    
         
             
                CURLMsg *msg;
         
     | 
| 
         @@ -257,7 +274,7 @@ void RedC::worker_loop() { 
     | 
|
| 
       257 
274 
     | 
    
         
             
                          result = nb::make_tuple(-1, NULL, NULL, (int)res, curl_easy_strerror(res));
         
     | 
| 
       258 
275 
     | 
    
         
             
                        }
         
     | 
| 
       259 
276 
     | 
    
         | 
| 
       260 
     | 
    
         
            -
                         
     | 
| 
      
 277 
     | 
    
         
            +
                        call_soon_threadsafe_(nb::cpp_function([data = std::move(data), result = std::move(result)]() {
         
     | 
| 
       261 
278 
     | 
    
         
             
                          data.future.attr("set_result")(std::move(result));
         
     | 
| 
       262 
279 
     | 
    
         
             
                        }));
         
     | 
| 
       263 
280 
     | 
    
         
             
                      }
         
     | 
| 
         @@ -272,9 +289,12 @@ void RedC::worker_loop() { 
     | 
|
| 
       272 
289 
     | 
    
         | 
| 
       273 
290 
     | 
    
         
             
            void RedC::cleanup() {
         
     | 
| 
       274 
291 
     | 
    
         
             
              std::unique_lock<std::mutex> lock(mutex_);
         
     | 
| 
       275 
     | 
    
         
            -
              acq_gil gil;
         
     | 
| 
       276 
292 
     | 
    
         
             
              for (auto &[easy, data] : transfers_) {
         
     | 
| 
       277 
     | 
    
         
            -
                 
     | 
| 
      
 293 
     | 
    
         
            +
                {
         
     | 
| 
      
 294 
     | 
    
         
            +
                  acq_gil gil;
         
     | 
| 
      
 295 
     | 
    
         
            +
                  call_soon_threadsafe_(data.future.attr("cancel"));
         
     | 
| 
      
 296 
     | 
    
         
            +
                }
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
       278 
298 
     | 
    
         
             
                curl_multi_remove_handle(multi_handle_, easy);
         
     | 
| 
       279 
299 
     | 
    
         
             
                curl_easy_cleanup(easy);
         
     | 
| 
       280 
300 
     | 
    
         
             
              }
         
     | 
| 
         @@ -294,6 +314,20 @@ size_t RedC::header_callback(char *buffer, size_t size, size_t nitems, Data *cli 
     | 
|
| 
       294 
314 
     | 
    
         
             
              return total_size;
         
     | 
| 
       295 
315 
     | 
    
         
             
            }
         
     | 
| 
       296 
316 
     | 
    
         | 
| 
      
 317 
     | 
    
         
            +
            size_t RedC::progress_callback(Data *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
         
     | 
| 
      
 318 
     | 
    
         
            +
                                           curl_off_t ulnow) {
         
     | 
| 
      
 319 
     | 
    
         
            +
              if (!clientp->progress_callback.is_none()) {
         
     | 
| 
      
 320 
     | 
    
         
            +
                try {
         
     | 
| 
      
 321 
     | 
    
         
            +
                  acq_gil gil;
         
     | 
| 
      
 322 
     | 
    
         
            +
                  clientp->progress_callback(dltotal, dlnow, ultotal, ulnow);
         
     | 
| 
      
 323 
     | 
    
         
            +
                } catch (const std::exception &e) {
         
     | 
| 
      
 324 
     | 
    
         
            +
                  std::cerr << "Error in progress_callback: " << e.what() << std::endl;
         
     | 
| 
      
 325 
     | 
    
         
            +
                }
         
     | 
| 
      
 326 
     | 
    
         
            +
              }
         
     | 
| 
      
 327 
     | 
    
         
            +
             
     | 
| 
      
 328 
     | 
    
         
            +
              return 0;
         
     | 
| 
      
 329 
     | 
    
         
            +
            }
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
       297 
331 
     | 
    
         
             
            size_t RedC::write_callback(char *data, size_t size, size_t nmemb, Data *clientp) {
         
     | 
| 
       298 
332 
     | 
    
         
             
              size_t total_size = size * nmemb;
         
     | 
| 
       299 
333 
     | 
    
         | 
| 
         @@ -314,9 +348,11 @@ size_t RedC::write_callback(char *data, size_t size, size_t nmemb, Data *clientp 
     | 
|
| 
       314 
348 
     | 
    
         
             
            NB_MODULE(redc_ext, m) {
         
     | 
| 
       315 
349 
     | 
    
         
             
              nb::class_<RedC>(m, "RedC")
         
     | 
| 
       316 
350 
     | 
    
         
             
                  .def(nb::init<const long &>())
         
     | 
| 
      
 351 
     | 
    
         
            +
                  .def("is_running", &RedC::is_running)
         
     | 
| 
       317 
352 
     | 
    
         
             
                  .def("request", &RedC::request, arg("method"), arg("url"), arg("raw_data") = "", arg("data") = nb::none(),
         
     | 
| 
       318 
353 
     | 
    
         
             
                       arg("files") = nb::none(), arg("headers") = nb::none(), arg("timeout_ms") = 60 * 1000,
         
     | 
| 
       319 
354 
     | 
    
         
             
                       arg("connect_timeout_ms") = 0, arg("allow_redirect") = true, arg("proxy_url") = "", arg("verify") = true,
         
     | 
| 
       320 
     | 
    
         
            -
                       arg("stream_callback") = nb::none(), arg(" 
     | 
| 
      
 355 
     | 
    
         
            +
                       arg("ca_cert_path") = "", arg("stream_callback") = nb::none(), arg("progress_callback") = nb::none(),
         
     | 
| 
      
 356 
     | 
    
         
            +
                       arg("verbose") = false)
         
     | 
| 
       321 
357 
     | 
    
         
             
                  .def("close", &RedC::close);
         
     | 
| 
       322 
     | 
    
         
            -
            }
         
     | 
| 
      
 358 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -5,7 +5,6 @@ 
     | 
|
| 
       5 
5 
     | 
    
         
             
            #include <cstring>
         
     | 
| 
       6 
6 
     | 
    
         
             
            #include <map>
         
     | 
| 
       7 
7 
     | 
    
         
             
            #include <mutex>
         
     | 
| 
       8 
     | 
    
         
            -
            #include <string>
         
     | 
| 
       9 
8 
     | 
    
         
             
            #include <thread>
         
     | 
| 
       10 
9 
     | 
    
         
             
            #include <vector>
         
     | 
| 
       11 
10 
     | 
    
         | 
| 
         @@ -21,17 +20,23 @@ 
     | 
|
| 
       21 
20 
     | 
    
         
             
            namespace nb = nanobind;
         
     | 
| 
       22 
21 
     | 
    
         
             
            using namespace nb::literals;
         
     | 
| 
       23 
22 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
            using string = std::string;
         
     | 
| 
       25 
     | 
    
         
            -
            using py_object = nb::object;
         
     | 
| 
       26 
23 
     | 
    
         
             
            using acq_gil = nb::gil_scoped_acquire;
         
     | 
| 
       27 
     | 
    
         
            -
            using  
     | 
| 
      
 24 
     | 
    
         
            +
            using rel_gil = nb::gil_scoped_release;
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            using py_object = nb::object;
         
     | 
| 
       28 
27 
     | 
    
         
             
            using py_bytes = nb::bytes;
         
     | 
| 
      
 28 
     | 
    
         
            +
            using arg = nb::arg;
         
     | 
| 
       29 
29 
     | 
    
         
             
            using dict = nb::dict;
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
      
 31 
     | 
    
         
            +
            bool isNullOrEmpty(const char *str) {
         
     | 
| 
      
 32 
     | 
    
         
            +
              return !str || !*str;
         
     | 
| 
      
 33 
     | 
    
         
            +
            }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       31 
35 
     | 
    
         
             
            struct Data {
         
     | 
| 
       32 
36 
     | 
    
         
             
              py_object future;
         
     | 
| 
       33 
37 
     | 
    
         
             
              py_object loop;
         
     | 
| 
       34 
     | 
    
         
            -
              py_object stream_callback 
     | 
| 
      
 38 
     | 
    
         
            +
              py_object stream_callback{nb::none()};
         
     | 
| 
      
 39 
     | 
    
         
            +
              py_object progress_callback{nb::none()};
         
     | 
| 
       35 
40 
     | 
    
         | 
| 
       36 
41 
     | 
    
         
             
              std::vector<char> headers;
         
     | 
| 
       37 
42 
     | 
    
         
             
              CurlSlist request_headers;
         
     | 
| 
         @@ -48,25 +53,25 @@ class RedC { 
     | 
|
| 
       48 
53 
     | 
    
         
             
              bool is_running();
         
     | 
| 
       49 
54 
     | 
    
         
             
              void close();
         
     | 
| 
       50 
55 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
              py_object request(const  
     | 
| 
       52 
     | 
    
         
            -
                                const py_object & 
     | 
| 
       53 
     | 
    
         
            -
                                const  
     | 
| 
       54 
     | 
    
         
            -
                                const  
     | 
| 
       55 
     | 
    
         
            -
                                const  
     | 
| 
       56 
     | 
    
         
            -
                                const bool &verbose = false);
         
     | 
| 
      
 56 
     | 
    
         
            +
              py_object request(const char *method, const char *url, const char *raw_data = "", const py_object &data = nb::none(),
         
     | 
| 
      
 57 
     | 
    
         
            +
                                const py_object &files = nb::none(), const py_object &headers = nb::none(),
         
     | 
| 
      
 58 
     | 
    
         
            +
                                const long &timeout_ms = 60 * 1000, const long &connect_timeout_ms = 0,
         
     | 
| 
      
 59 
     | 
    
         
            +
                                const bool &allow_redirect = true, const char *proxy_url = "", const bool &verify = true,
         
     | 
| 
      
 60 
     | 
    
         
            +
                                const char *ca_cert_path = "", const py_object &stream_callback = nb::none(),
         
     | 
| 
      
 61 
     | 
    
         
            +
                                const py_object &progress_callback = nb::none(), const bool &verbose = false);
         
     | 
| 
       57 
62 
     | 
    
         | 
| 
       58 
63 
     | 
    
         
             
             private:
         
     | 
| 
       59 
     | 
    
         
            -
              int still_running_ 
     | 
| 
      
 64 
     | 
    
         
            +
              int still_running_{0};
         
     | 
| 
       60 
65 
     | 
    
         
             
              long buffer_size_;
         
     | 
| 
       61 
66 
     | 
    
         
             
              py_object loop_;
         
     | 
| 
       62 
     | 
    
         
            -
              py_object  
     | 
| 
      
 67 
     | 
    
         
            +
              py_object call_soon_threadsafe_;
         
     | 
| 
       63 
68 
     | 
    
         | 
| 
       64 
69 
     | 
    
         
             
              CURLM *multi_handle_;
         
     | 
| 
       65 
70 
     | 
    
         | 
| 
       66 
71 
     | 
    
         
             
              std::map<CURL *, Data> transfers_;
         
     | 
| 
       67 
72 
     | 
    
         
             
              std::mutex mutex_;
         
     | 
| 
       68 
73 
     | 
    
         
             
              std::thread worker_thread_;
         
     | 
| 
       69 
     | 
    
         
            -
              std::atomic<bool> running_ 
     | 
| 
      
 74 
     | 
    
         
            +
              std::atomic<bool> running_{false};
         
     | 
| 
       70 
75 
     | 
    
         | 
| 
       71 
76 
     | 
    
         
             
              moodycamel::ConcurrentQueue<CURL *> queue_;
         
     | 
| 
       72 
77 
     | 
    
         | 
| 
         @@ -75,7 +80,9 @@ class RedC { 
     | 
|
| 
       75 
80 
     | 
    
         
             
              void CHECK_RUNNING();
         
     | 
| 
       76 
81 
     | 
    
         | 
| 
       77 
82 
     | 
    
         
             
              static size_t header_callback(char *buffer, size_t size, size_t nitems, Data *clientp);
         
     | 
| 
      
 83 
     | 
    
         
            +
              static size_t progress_callback(Data *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
         
     | 
| 
      
 84 
     | 
    
         
            +
                                              curl_off_t ulnow);
         
     | 
| 
       78 
85 
     | 
    
         
             
              static size_t write_callback(char *data, size_t size, size_t nmemb, Data *clientp);
         
     | 
| 
       79 
86 
     | 
    
         
             
            };
         
     | 
| 
       80 
87 
     | 
    
         | 
| 
       81 
     | 
    
         
            -
            #endif  // REDC_H
         
     | 
| 
      
 88 
     | 
    
         
            +
            #endif  // REDC_H
         
     | 
| 
         @@ -1,6 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            from .exceptions import HTTPError
         
     | 
| 
       2 
2 
     | 
    
         
             
            from .utils import Headers, json_loads
         
     | 
| 
       3 
     | 
    
         
            -
            from .codes import HTTPStatus
         
     | 
| 
       4 
3 
     | 
    
         | 
| 
       5 
4 
     | 
    
         | 
| 
       6 
5 
     | 
    
         
             
            class Response:
         
     | 
| 
         @@ -11,6 +10,7 @@ class Response: 
     | 
|
| 
       11 
10 
     | 
    
         
             
                    response: bytes,
         
     | 
| 
       12 
11 
     | 
    
         
             
                    curl_code: int,
         
     | 
| 
       13 
12 
     | 
    
         
             
                    curl_error_message: str,
         
     | 
| 
      
 13 
     | 
    
         
            +
                    raise_for_status: bool = False,
         
     | 
| 
       14 
14 
     | 
    
         
             
                ):
         
     | 
| 
       15 
15 
     | 
    
         
             
                    """Represents an HTTP response of RedC"""
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
         @@ -27,6 +27,9 @@ class Response: 
     | 
|
| 
       27 
27 
     | 
    
         
             
                    self.curl_error_message = curl_error_message
         
     | 
| 
       28 
28 
     | 
    
         
             
                    """CURL error message"""
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
      
 30 
     | 
    
         
            +
                    if raise_for_status:
         
     | 
| 
      
 31 
     | 
    
         
            +
                        self.raise_for_status()
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       30 
33 
     | 
    
         
             
                @property
         
     | 
| 
       31 
34 
     | 
    
         
             
                def content(self) -> bytes:
         
     | 
| 
       32 
35 
     | 
    
         
             
                    """Returns the raw response content"""
         
     | 
| 
         @@ -58,16 +61,8 @@ class Response: 
     | 
|
| 
       58 
61 
     | 
    
         | 
| 
       59 
62 
     | 
    
         
             
                def raise_for_status(self):
         
     | 
| 
       60 
63 
     | 
    
         
             
                    """Raises an HTTPError if the response status indicates an error"""
         
     | 
| 
       61 
     | 
    
         
            -
                    if self.status_code == -1 
     | 
| 
       62 
     | 
    
         
            -
                        raise HTTPError( 
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                    if 400 <= self.status_code <= 599:
         
     | 
| 
       65 
     | 
    
         
            -
                        short_description = HTTPStatus.get_description(self.status_code)
         
     | 
| 
       66 
     | 
    
         
            -
                        raise HTTPError(
         
     | 
| 
       67 
     | 
    
         
            -
                            self.status_code
         
     | 
| 
       68 
     | 
    
         
            -
                            if not short_description
         
     | 
| 
       69 
     | 
    
         
            -
                            else f"{self.status_code} - {short_description}"
         
     | 
| 
       70 
     | 
    
         
            -
                        )
         
     | 
| 
      
 64 
     | 
    
         
            +
                    if self.status_code == -1 or (400 <= self.status_code <= 599):
         
     | 
| 
      
 65 
     | 
    
         
            +
                        raise HTTPError(self.status_code, self.curl_code, self.curl_error_message)
         
     | 
| 
       71 
66 
     | 
    
         | 
| 
       72 
67 
     | 
    
         
             
                def __bool__(self):
         
     | 
| 
       73 
68 
     | 
    
         
             
                    return self.status_code != -1 and 200 <= self.status_code <= 299
         
     | 
| 
         @@ -1,40 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import inspect
         
     | 
| 
       2 
     | 
    
         
            -
            from typing import Callable
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            class StreamCallback:
         
     | 
| 
       6 
     | 
    
         
            -
                """A class for creating a stream callback"""
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
                def __init__(self, callback: Callable[[bytes, int], None]):
         
     | 
| 
       9 
     | 
    
         
            -
                    """A callback handler for streaming data
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                    Example:
         
     | 
| 
       12 
     | 
    
         
            -
                        .. code-block:: python
         
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                            >>> def callback(data: bytes, data_size: int):
         
     | 
| 
       15 
     | 
    
         
            -
                            ...     print(f"Received {len(data)}")
         
     | 
| 
       16 
     | 
    
         
            -
                            >>> stream_callback = StreamCallback(callback)
         
     | 
| 
       17 
     | 
    
         
            -
                            >>> client.get("https://example.com/", stream_callback=stream_callback)
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                    Parameters:
         
     | 
| 
       20 
     | 
    
         
            -
                        callback (``Callable[[bytes, int], None]``):
         
     | 
| 
       21 
     | 
    
         
            -
                            A function that accepts two arguments: data (``bytes``) and data_size (``int``)
         
     | 
| 
       22 
     | 
    
         
            -
                            The function cannot be asynchronous
         
     | 
| 
       23 
     | 
    
         
            -
                    """
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                    self.callback = callback
         
     | 
| 
       26 
     | 
    
         
            -
                    self._validate_callback()
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                def _validate_callback(self):
         
     | 
| 
       29 
     | 
    
         
            -
                    if inspect.iscoroutinefunction(self.callback):
         
     | 
| 
       30 
     | 
    
         
            -
                        raise TypeError("Callback function cannot be asynchronous")
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                    signature = inspect.signature(self.callback)
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                    parameters = signature.parameters
         
     | 
| 
       35 
     | 
    
         
            -
                    num_parameters = len(parameters)
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                    if num_parameters != 2:
         
     | 
| 
       38 
     | 
    
         
            -
                        raise TypeError(
         
     | 
| 
       39 
     | 
    
         
            -
                            f"Callback function must accept two arguments only callback(data: bytes, data_size: int) but it accepts {num_parameters}."
         
     | 
| 
       40 
     | 
    
         
            -
                        )
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |