redc 0.1.5__tar.gz → 0.1.7__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.5 → redc-0.1.7}/.github/workflows/before-all.sh +1 -1
- {redc-0.1.5 → redc-0.1.7}/PKG-INFO +7 -7
- {redc-0.1.5 → redc-0.1.7}/README.md +6 -6
- {redc-0.1.5 → redc-0.1.7}/pyproject.toml +1 -1
- {redc-0.1.5 → redc-0.1.7}/redc/__init__.py +1 -1
- {redc-0.1.5 → redc-0.1.7}/redc/ext/redc.cpp +85 -60
- {redc-0.1.5 → redc-0.1.7}/redc/ext/redc.h +11 -0
- {redc-0.1.5 → redc-0.1.7}/.clang-format +0 -0
- {redc-0.1.5 → redc-0.1.7}/.github/workflows/build_wheels.yml +0 -0
- {redc-0.1.5 → redc-0.1.7}/.gitignore +0 -0
- {redc-0.1.5 → redc-0.1.7}/CMake/PreventInSourceBuild.cmake +0 -0
- {redc-0.1.5 → redc-0.1.7}/CMakeLists.txt +0 -0
- {redc-0.1.5 → redc-0.1.7}/LICENSE +0 -0
- {redc-0.1.5 → redc-0.1.7}/assets/images/redc-logo.png +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/callback.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/callbacks.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/client.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/codes.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/exceptions/__init__.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/ext/utils/concurrentqueue.h +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/ext/utils/curl_utils.h +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/ext/utils/memoryview.h +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/response.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/utils/__init__.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/utils/_io_utils.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/utils/headers.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/utils/http.py +0 -0
- {redc-0.1.5 → redc-0.1.7}/redc/utils/json_encoder.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: redc
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.7
|
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>
|
@@ -14,17 +14,17 @@ Description-Content-Type: text/markdown
|
|
14
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://pypi.org/project/RedC) [](https://curl.se/ch/8.14.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
|
|
21
21
|
## Features
|
22
22
|
|
23
|
-
-
|
24
|
-
-
|
25
|
-
-
|
26
|
-
-
|
27
|
-
-
|
23
|
+
- **Asynchronous by Design**: Built with `asyncio` for non-blocking HTTP requests
|
24
|
+
- **HTTP/2 Support**: Fully compatible with `HTTP/2` for faster and more efficient communication
|
25
|
+
- **curl Backend**: Leverages the speed and reliability of curl for HTTP operations
|
26
|
+
- **Streaming Support**: Stream large responses with ease using callback functions
|
27
|
+
- **Proxy Support**: Easily configure proxies for your requests
|
28
28
|
|
29
29
|
## Installation
|
30
30
|
|
@@ -2,17 +2,17 @@
|
|
2
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://pypi.org/project/RedC) [](https://curl.se/ch/8.14.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
|
|
9
9
|
## Features
|
10
10
|
|
11
|
-
-
|
12
|
-
-
|
13
|
-
-
|
14
|
-
-
|
15
|
-
-
|
11
|
+
- **Asynchronous by Design**: Built with `asyncio` for non-blocking HTTP requests
|
12
|
+
- **HTTP/2 Support**: Fully compatible with `HTTP/2` for faster and more efficient communication
|
13
|
+
- **curl Backend**: Leverages the speed and reliability of curl for HTTP operations
|
14
|
+
- **Streaming Support**: Stream large responses with ease using callback functions
|
15
|
+
- **Proxy Support**: Easily configure proxies for your requests
|
16
16
|
|
17
17
|
## Installation
|
18
18
|
|
@@ -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.7"
|
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" }]
|
@@ -162,42 +162,42 @@ py_object RedC::request(const char *method, const char *url, const char *raw_dat
|
|
162
162
|
|
163
163
|
{
|
164
164
|
std::unique_lock<std::mutex> lock(mutex_);
|
165
|
-
auto
|
165
|
+
auto [it, inserted] =
|
166
|
+
transfers_.try_emplace(easy, Data{
|
167
|
+
future,
|
168
|
+
loop_,
|
169
|
+
stream_callback,
|
170
|
+
progress_callback,
|
171
|
+
file_stream,
|
172
|
+
!stream_callback.is_none() && !is_nobody, // has_stream_callback
|
173
|
+
!progress_callback.is_none() && !is_nobody, // has_progress_callback
|
174
|
+
{}, // response headers
|
175
|
+
std::move(slist_headers),
|
176
|
+
std::move(curl_mime_),
|
177
|
+
{} // response
|
178
|
+
});
|
179
|
+
auto &d = it->second;
|
166
180
|
lock.unlock();
|
167
181
|
|
168
|
-
d.future = future;
|
169
|
-
d.request_headers = std::move(slist_headers);
|
170
|
-
d.curl_mime_ = std::move(curl_mime_);
|
171
|
-
|
172
182
|
curl_easy_setopt(easy, CURLOPT_HEADERDATA, &d);
|
173
183
|
|
174
184
|
if (!is_nobody) {
|
175
185
|
curl_easy_setopt(easy, CURLOPT_WRITEDATA, &d);
|
176
186
|
|
177
|
-
if (!file_stream.is_none()) {
|
178
|
-
d.file_stream = file_stream;
|
179
|
-
|
180
|
-
curl_easy_setopt(easy, CURLOPT_UPLOAD, 1L);
|
181
|
-
curl_easy_setopt(easy, CURLOPT_READDATA, &d);
|
182
|
-
curl_easy_setopt(easy, CURLOPT_READFUNCTION, &RedC::read_callback);
|
183
|
-
curl_easy_setopt(easy, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size);
|
184
|
-
}
|
185
|
-
|
186
|
-
if (!stream_callback.is_none()) {
|
187
|
-
d.stream_callback = stream_callback;
|
188
|
-
d.has_stream_callback = true;
|
189
|
-
}
|
190
|
-
|
191
187
|
if (!progress_callback.is_none()) {
|
192
|
-
d.progress_callback = progress_callback;
|
193
|
-
d.has_progress_callback = true;
|
194
|
-
|
195
188
|
curl_easy_setopt(easy, CURLOPT_XFERINFODATA, &d);
|
196
189
|
curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 0L);
|
197
190
|
curl_easy_setopt(easy, CURLOPT_XFERINFOFUNCTION, &RedC::progress_callback);
|
198
191
|
} else {
|
199
192
|
curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 1L);
|
200
193
|
}
|
194
|
+
|
195
|
+
if (!file_stream.is_none()) {
|
196
|
+
curl_easy_setopt(easy, CURLOPT_UPLOAD, 1L);
|
197
|
+
curl_easy_setopt(easy, CURLOPT_READDATA, &d);
|
198
|
+
curl_easy_setopt(easy, CURLOPT_READFUNCTION, &RedC::read_callback);
|
199
|
+
curl_easy_setopt(easy, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size);
|
200
|
+
}
|
201
201
|
}
|
202
202
|
}
|
203
203
|
|
@@ -218,17 +218,15 @@ void RedC::worker_loop() {
|
|
218
218
|
CURLMcode res = curl_multi_add_handle(multi_handle_, e);
|
219
219
|
if (res != CURLM_OK) {
|
220
220
|
std::unique_lock<std::mutex> lock(mutex_);
|
221
|
-
auto
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
}));
|
231
|
-
}
|
221
|
+
auto node = transfers_.extract(e);
|
222
|
+
lock.unlock();
|
223
|
+
|
224
|
+
if (!node.empty()) {
|
225
|
+
Data &data = node.mapped();
|
226
|
+
acq_gil gil;
|
227
|
+
call_soon_threadsafe_(nb::cpp_function([data = std::move(data), res]() {
|
228
|
+
data.future.attr("set_result")(nb::make_tuple(-1, NULL, NULL, (int)res, curl_multi_strerror(res)));
|
229
|
+
}));
|
232
230
|
}
|
233
231
|
curl_easy_cleanup(e);
|
234
232
|
}
|
@@ -248,18 +246,16 @@ void RedC::worker_loop() {
|
|
248
246
|
while ((msg = curl_multi_info_read(multi_handle_, &msgs_left))) {
|
249
247
|
if (msg->msg == CURLMSG_DONE) {
|
250
248
|
std::unique_lock<std::mutex> lock(mutex_);
|
251
|
-
auto
|
252
|
-
|
253
|
-
Data data = std::move(it->second);
|
254
|
-
transfers_.erase(it);
|
255
|
-
lock.unlock();
|
249
|
+
auto node = transfers_.extract(msg->easy_handle);
|
250
|
+
lock.unlock();
|
256
251
|
|
257
|
-
|
258
|
-
|
252
|
+
if (!node.empty()) {
|
253
|
+
Data &data = node.mapped();
|
254
|
+
acq_gil gil;
|
259
255
|
|
260
|
-
|
256
|
+
CURLcode res = msg->data.result;
|
261
257
|
|
262
|
-
|
258
|
+
/*
|
263
259
|
* Result is allways Tuple:
|
264
260
|
|
265
261
|
* 0: HTTP response status code.
|
@@ -274,24 +270,23 @@ void RedC::worker_loop() {
|
|
274
270
|
*
|
275
271
|
* 4: cURL error message string; can be null
|
276
272
|
*/
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
}
|
286
|
-
|
287
|
-
call_soon_threadsafe_(nb::cpp_function([data = std::move(data), result = std::move(result)]() {
|
288
|
-
data.future.attr("set_result")(std::move(result));
|
289
|
-
}));
|
273
|
+
py_object result;
|
274
|
+
if (res == CURLE_OK) {
|
275
|
+
short status_code = 0;
|
276
|
+
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status_code);
|
277
|
+
result = nb::make_tuple(status_code, py_bytes(data.headers.data(), data.headers.size()),
|
278
|
+
py_bytes(data.response.data(), data.response.size()), (int)res, NULL);
|
279
|
+
} else {
|
280
|
+
result = nb::make_tuple(-1, NULL, NULL, (int)res, curl_easy_strerror(res));
|
290
281
|
}
|
291
282
|
|
292
|
-
|
293
|
-
|
283
|
+
call_soon_threadsafe_(nb::cpp_function([data = std::move(data), result = std::move(result)]() {
|
284
|
+
data.future.attr("set_result")(std::move(result));
|
285
|
+
}));
|
294
286
|
}
|
287
|
+
|
288
|
+
curl_multi_remove_handle(multi_handle_, msg->easy_handle);
|
289
|
+
curl_easy_cleanup(msg->easy_handle);
|
295
290
|
}
|
296
291
|
}
|
297
292
|
}
|
@@ -300,13 +295,22 @@ void RedC::worker_loop() {
|
|
300
295
|
void RedC::cleanup() {
|
301
296
|
std::unique_lock<std::mutex> lock(mutex_);
|
302
297
|
acq_gil gil;
|
303
|
-
for (auto &[easy, data] : transfers_) {
|
304
|
-
call_soon_threadsafe_(data.future.attr("cancel"));
|
305
298
|
|
299
|
+
std::vector<py_object> futures;
|
300
|
+
futures.reserve(transfers_.size());
|
301
|
+
|
302
|
+
for (auto &[easy, data] : transfers_) {
|
303
|
+
futures.push_back(data.future);
|
306
304
|
curl_multi_remove_handle(multi_handle_, easy);
|
307
305
|
curl_easy_cleanup(easy);
|
308
306
|
}
|
307
|
+
|
309
308
|
transfers_.clear();
|
309
|
+
lock.unlock();
|
310
|
+
|
311
|
+
for (auto &future : futures) {
|
312
|
+
call_soon_threadsafe_(future.attr("cancel"));
|
313
|
+
}
|
310
314
|
}
|
311
315
|
|
312
316
|
void RedC::CHECK_RUNNING() {
|
@@ -362,8 +366,29 @@ size_t RedC::write_callback(char *data, size_t size, size_t nmemb, Data *clientp
|
|
362
366
|
return total_size;
|
363
367
|
}
|
364
368
|
|
369
|
+
int redc_tp_traverse(PyObject *self, visitproc visit, void *arg) {
|
370
|
+
Py_VISIT(Py_TYPE(self));
|
371
|
+
|
372
|
+
if (!nb::inst_ready(self))
|
373
|
+
return 0;
|
374
|
+
|
375
|
+
RedC *me = nb::inst_ptr<RedC>(self);
|
376
|
+
Py_VISIT(me->loop_.ptr());
|
377
|
+
Py_VISIT(me->call_soon_threadsafe_.ptr());
|
378
|
+
return 0;
|
379
|
+
}
|
380
|
+
|
381
|
+
int redc_tp_clear(PyObject *self) {
|
382
|
+
RedC *c = nb::inst_ptr<RedC>(self);
|
383
|
+
c->loop_ = {};
|
384
|
+
c->call_soon_threadsafe_ = {};
|
385
|
+
return 0;
|
386
|
+
}
|
387
|
+
|
388
|
+
PyType_Slot slots[] = {{Py_tp_traverse, (void *)redc_tp_traverse}, {Py_tp_clear, (void *)redc_tp_clear}, {0, 0}};
|
389
|
+
|
365
390
|
NB_MODULE(redc_ext, m) {
|
366
|
-
nb::class_<RedC>(m, "RedC")
|
391
|
+
nb::class_<RedC>(m, "RedC", nb::type_slots(slots))
|
367
392
|
.def(nb::init<const long &>())
|
368
393
|
.def("is_running", &RedC::is_running)
|
369
394
|
.def("request", &RedC::request, arg("method"), arg("url"), arg("raw_data") = "", arg("file_stream") = nb::none(),
|
@@ -33,6 +33,14 @@ bool isNullOrEmpty(const char *str) {
|
|
33
33
|
}
|
34
34
|
|
35
35
|
struct Data {
|
36
|
+
Data() = default;
|
37
|
+
|
38
|
+
Data(const Data &) = delete;
|
39
|
+
Data &operator=(const Data &) = delete;
|
40
|
+
|
41
|
+
Data(Data &&) = default;
|
42
|
+
Data &operator=(Data &&) = default;
|
43
|
+
|
36
44
|
py_object future;
|
37
45
|
py_object loop;
|
38
46
|
py_object stream_callback{nb::none()};
|
@@ -90,6 +98,9 @@ class RedC {
|
|
90
98
|
static size_t progress_callback(Data *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal,
|
91
99
|
curl_off_t ulnow);
|
92
100
|
static size_t write_callback(char *data, size_t size, size_t nmemb, Data *clientp);
|
101
|
+
|
102
|
+
friend int redc_tp_traverse(PyObject *, visitproc, void *);
|
103
|
+
friend int redc_tp_clear(PyObject *);
|
93
104
|
};
|
94
105
|
|
95
106
|
#endif // REDC_H
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|