geventhttpclient 2.2.1__tar.gz → 2.3.1__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.
Files changed (48) hide show
  1. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/MANIFEST.in +2 -3
  2. {geventhttpclient-2.2.1/src/geventhttpclient.egg-info → geventhttpclient-2.3.1}/PKG-INFO +71 -70
  3. geventhttpclient-2.3.1/README.md +185 -0
  4. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/ext/_parser.c +17 -17
  5. geventhttpclient-2.3.1/geventhttpclient/__init__.py +9 -0
  6. geventhttpclient-2.3.1/geventhttpclient/api.py +157 -0
  7. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/client.py +3 -3
  8. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/connectionpool.py +7 -7
  9. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/header.py +22 -4
  10. geventhttpclient-2.3.1/geventhttpclient/requests.py +252 -0
  11. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/response.py +2 -1
  12. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/useragent.py +274 -195
  13. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1/geventhttpclient.egg-info}/PKG-INFO +71 -70
  14. geventhttpclient-2.3.1/geventhttpclient.egg-info/SOURCES.txt +28 -0
  15. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/pyproject.toml +30 -21
  16. geventhttpclient-2.2.1/README.md +0 -184
  17. geventhttpclient-2.2.1/src/geventhttpclient/__init__.py +0 -7
  18. geventhttpclient-2.2.1/src/geventhttpclient/tests/conftest.py +0 -57
  19. geventhttpclient-2.2.1/src/geventhttpclient/tests/oncert.pem +0 -20
  20. geventhttpclient-2.2.1/src/geventhttpclient/tests/server.crt +0 -22
  21. geventhttpclient-2.2.1/src/geventhttpclient/tests/server.key +0 -28
  22. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_client.py +0 -347
  23. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_headers.py +0 -227
  24. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_httplib.py +0 -73
  25. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_httplib2.py +0 -31
  26. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_keep_alive.py +0 -95
  27. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_network_failures.py +0 -154
  28. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_no_module_ssl.py +0 -36
  29. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_parser.py +0 -148
  30. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_ssl.py +0 -312
  31. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_url.py +0 -164
  32. geventhttpclient-2.2.1/src/geventhttpclient/tests/test_useragent.py +0 -233
  33. geventhttpclient-2.2.1/src/geventhttpclient.egg-info/SOURCES.txt +0 -41
  34. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/LICENSE-MIT +0 -0
  35. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/ext/Python_compat.h +0 -0
  36. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/httplib.py +0 -0
  37. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/httplib2.py +0 -0
  38. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient/url.py +0 -0
  39. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient.egg-info/dependency_links.txt +0 -0
  40. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient.egg-info/requires.txt +0 -0
  41. {geventhttpclient-2.2.1/src → geventhttpclient-2.3.1}/geventhttpclient.egg-info/top_level.txt +0 -0
  42. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/llhttp/LICENSE-MIT +0 -0
  43. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/llhttp/include/llhttp.h +0 -0
  44. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/llhttp/src/api.c +0 -0
  45. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/llhttp/src/http.c +0 -0
  46. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/llhttp/src/llhttp.c +0 -0
  47. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/setup.cfg +0 -0
  48. {geventhttpclient-2.2.1 → geventhttpclient-2.3.1}/setup.py +0 -0
@@ -1,10 +1,9 @@
1
- include src/geventhttpclient/*.py
1
+ include geventhttpclient/*.py
2
2
  include ext/*.c
3
3
  include ext/*.h
4
4
  recursive-include llhttp *.h *.c LICENSE-MIT
5
- recursive-include src/geventhttpclient/tests *
5
+ recursive-exclude tests *
6
6
  global-exclude __pycache__
7
7
  global-exclude *.py[co]
8
8
  include pyproject.toml
9
- include LICENSE.txt
10
9
  include README.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geventhttpclient
3
- Version: 2.2.1
3
+ Version: 2.3.1
4
4
  Summary: HTTP client library for gevent
5
5
  Author-email: Antonin Amand <antonin.amand@gmail.com>
6
6
  License: This software is licensed under the MIT License.
@@ -68,67 +68,72 @@ Requires-Dist: oauth2; extra == "examples"
68
68
 
69
69
  # geventhttpclient
70
70
 
71
- A high performance, concurrent HTTP client library for python using
71
+ A [high performance](https://github.com/geventhttpclient/geventhttpclient/blob/master/README.md#Benchmarks),
72
+ concurrent HTTP client library for python using
72
73
  [gevent](http://gevent.org).
73
74
 
74
- `gevent.httplib` support was removed in [gevent 1.0](https://github.com/surfly/gevent/commit/b45b83b1bc4de14e3c4859362825044b8e3df7d6
75
- ), **geventhttpclient** now provides that missing functionality.
75
+ `gevent.httplib` support for patching `http.client` was removed in
76
+ [gevent 1.0](https://github.com/surfly/gevent/commit/b45b83b1bc4de14e3c4859362825044b8e3df7d6),
77
+ `geventhttpclient` now provides that missing functionality.
76
78
 
77
- **geventhttpclient** uses a fast [http parser](https://github.com/nodejs/llhttp),
79
+ `geventhttpclient` uses a fast [http parser](https://github.com/nodejs/llhttp),
78
80
  written in C.
79
81
 
80
- **geventhttpclient** has been specifically designed for high concurrency,
82
+ `geventhttpclient` has been specifically designed for high concurrency,
81
83
  streaming and support HTTP 1.1 persistent connections. More generally it is
82
84
  designed for efficiently pulling from REST APIs and streaming APIs
83
85
  like Twitter's.
84
86
 
85
- Safe SSL support is provided by default. **geventhttpclient** depends on
87
+ Safe SSL support is provided by default. `geventhttpclient` depends on
86
88
  the certifi CA Bundle. This is the same CA Bundle which ships with the
87
89
  Requests codebase, and is derived from Mozilla Firefox's canonical set.
88
90
 
89
- As of version 2.1, only Python 3.9+ is fully supported (with prebuilt wheels).
90
-
91
- A simple example:
91
+ Since version 2.3, `geventhttpclient` features a largely `requests`
92
+ compatible interface. It covers basic HTTP usage including cookie
93
+ management, form data encoding or decoding of compressed data,
94
+ but otherwise isn't as feature rich as the original `requests`. For
95
+ simple use-cases, it can serve as a drop-in replacement.
92
96
 
93
97
  ```python
94
- #!/usr/bin/python
98
+ import geventhttpclient as requests
99
+ requests.get("https://github.com").text
100
+ requests.post("http://httpbingo.org/post", data="asdfasd").json()
101
+
102
+ from geventhttpclient import Session
103
+ s = Session()
104
+ s.get("http://httpbingo.org/headers").json()
105
+ s.get("https://github.com").content
106
+ ```
107
+
108
+ This interface builds on top of the lower level `HTTPClient`.
95
109
 
110
+ ```python
96
111
  from geventhttpclient import HTTPClient
97
112
  from geventhttpclient.url import URL
98
113
 
99
- url = URL('http://gevent.org/')
100
-
101
- http = HTTPClient(url.host)
102
-
103
- # issue a get request
104
- response = http.get(url.request_uri)
105
-
106
- # read status_code
114
+ url = URL("http://gevent.org/")
115
+ client = HTTPClient(url.host)
116
+ response = client.get(url.request_uri)
107
117
  response.status_code
108
-
109
- # read response body
110
118
  body = response.read()
111
-
112
- # close connections
113
- http.close()
119
+ client.close()
114
120
  ```
115
121
 
116
122
  ## httplib compatibility and monkey patch
117
123
 
118
- **geventhttpclient.httplib** module contains classes for drop in
119
- replacement of httplib connection and response objects.
120
- If you use httplib directly you can replace the **httplib** imports
121
- by **geventhttpclient.httplib**.
124
+ `geventhttpclient.httplib` module contains classes for drop in
125
+ replacement of `http.client` connection and response objects.
126
+ If you use http.client directly you can replace the `httplib` imports
127
+ by `geventhttpclient.httplib`.
122
128
 
123
129
  ```python
124
- # from httplib import HTTPConnection
130
+ # from http.client import HTTPConnection
125
131
  from geventhttpclient.httplib import HTTPConnection
126
132
  ```
127
133
 
128
- If you use **httplib2**, **urllib** or **urllib2**; you can patch **httplib** to
129
- use the wrappers from **geventhttpclient**.
130
- For **httplib2**, make sure you patch before you import or the *super*
131
- calls will fail.
134
+ If you use `httplib2`, `urllib` or `urllib2`; you can patch `httplib` to
135
+ use the wrappers from `geventhttpclient`. For `httplib2`, make sure you
136
+ patch before you import or the `super()` calls will fail.
132
137
 
133
138
  ```python
134
139
  import geventhttpclient.httplib
@@ -139,12 +144,11 @@ import httplib2
139
144
 
140
145
  ## High Concurrency
141
146
 
142
- HTTPClient has connection pool built in and is greenlet safe by design.
143
- You can use the same instance among several greenlets.
147
+ `HTTPClient` has a connection pool built in and is greenlet safe by design.
148
+ You can use the same instance among several greenlets. It is the low
149
+ level building block of this library.
144
150
 
145
151
  ```python
146
- #!/usr/bin/env python
147
-
148
152
  import gevent.pool
149
153
  import json
150
154
 
@@ -153,67 +157,63 @@ from geventhttpclient.url import URL
153
157
 
154
158
 
155
159
  # go to http://developers.facebook.com/tools/explorer and copy the access token
156
- TOKEN = '<go to http://developers.facebook.com/tools/explorer and copy the access token>'
160
+ TOKEN = "<MY_DEV_TOKEN>"
157
161
 
158
- url = URL('https://graph.facebook.com/me/friends')
159
- url['access_token'] = TOKEN
162
+ url = URL("https://graph.facebook.com/me/friends", params={"access_token": TOKEN})
160
163
 
161
164
  # setting the concurrency to 10 allow to create 10 connections and
162
165
  # reuse them.
163
- http = HTTPClient.from_url(url, concurrency=10)
166
+ client = HTTPClient.from_url(url, concurrency=10)
164
167
 
165
- response = http.get(url.request_uri)
168
+ response = client.get(url.request_uri)
166
169
  assert response.status_code == 200
167
170
 
168
171
  # response comply to the read protocol. It passes the stream to
169
172
  # the json parser as it's being read.
170
- data = json.load(response)['data']
173
+ data = json.load(response)["data"]
171
174
 
172
- def print_friend_username(http, friend_id):
173
- friend_url = URL('/' + str(friend_id))
174
- friend_url['access_token'] = TOKEN
175
+ def print_friend_username(client, friend_id):
176
+ friend_url = URL(f"/{friend_id}", params={"access_token": TOKEN})
175
177
  # the greenlet will block until a connection is available
176
- response = http.get(friend_url.request_uri)
178
+ response = client.get(friend_url.request_uri)
177
179
  assert response.status_code == 200
178
180
  friend = json.load(response)
179
- if 'username' in friend:
180
- print('%s: %s' % (friend['username'], friend['name']))
181
+ if "username" in friend:
182
+ print(f"{friend['username']}: {friend['name']}")
181
183
  else:
182
- print('%s has no username.' % friend['name'])
184
+ print(f"{friend['name']} has no username.")
183
185
 
184
186
  # allow to run 20 greenlet at a time, this is more than concurrency
185
187
  # of the http client but isn't a problem since the client has its own
186
188
  # connection pool.
187
189
  pool = gevent.pool.Pool(20)
188
190
  for item in data:
189
- friend_id = item['id']
190
- pool.spawn(print_friend_username, http, friend_id)
191
+ friend_id = item["id"]
192
+ pool.spawn(print_friend_username, client, friend_id)
191
193
 
192
194
  pool.join()
193
- http.close()
195
+ client.close()
194
196
  ```
195
197
 
196
198
  ## Streaming
197
199
 
198
- **geventhttpclient** supports streaming.
199
- Response objects have a read(N) and readline() method that read the stream
200
- incrementally.
201
- See *src/examples/twitter_streaming.py* for pulling twitter stream API.
200
+ `geventhttpclient` supports streaming. Response objects have a `read(n)` and
201
+ `readline()` method that read the stream incrementally.
202
+ See [examples/twitter_streaming.py](https://github.com/geventhttpclient/geventhttpclient/blob/master/examples/twitter_streaming.py)
203
+ for pulling twitter stream API.
202
204
 
203
205
  Here is an example on how to download a big file chunk by chunk to save memory:
204
206
 
205
207
  ```python
206
- #!/usr/bin/env python
207
-
208
208
  from geventhttpclient import HTTPClient, URL
209
209
 
210
- url = URL('http://127.0.0.1:80/100.dat')
211
- http = HTTPClient.from_url(url)
212
- response = http.get(url.query_string)
210
+ url = URL("http://127.0.0.1:80/100.dat")
211
+ client = HTTPClient.from_url(url)
212
+ response = client.get(url.query_string)
213
213
  assert response.status_code == 200
214
214
 
215
215
  CHUNK_SIZE = 1024 * 16 # 16KB
216
- with open('/tmp/100.dat', 'w') as f:
216
+ with open("/tmp/100.dat", "w") as f:
217
217
  data = response.read(CHUNK_SIZE)
218
218
  while data:
219
219
  f.write(data)
@@ -222,11 +222,12 @@ with open('/tmp/100.dat', 'w') as f:
222
222
 
223
223
  ## Benchmarks
224
224
 
225
- The benchmark does 10000 get requests against a local nginx server in the default
225
+ The benchmark runs 10000 `GET` requests against a local nginx server in the default
226
226
  configuration with a concurrency of 10. See `benchmarks` folder. The requests per
227
227
  second for a couple of popular clients is given in the table below. Please read
228
228
  [benchmarks/README.md](https://github.com/geventhttpclient/geventhttpclient/blob/master/benchmarks/README.md)
229
- for mor details. Also note, HTTPX is better be used with asyncio, not gevent.
229
+ for more details. Also note, [HTTPX](https://www.python-httpx.org/) is better be
230
+ used with `asyncio`, not `gevent`.
230
231
 
231
232
  | HTTP Client | RPS |
232
233
  |--------------------|--------|
@@ -240,8 +241,8 @@ for mor details. Also note, HTTPX is better be used with asyncio, not gevent.
240
241
 
241
242
  ## License
242
243
 
243
- This package is distributed under the [MIT license](https://github.com/geventhttpclient/geventhttpclient/blob/master/LICENSE-MIT).
244
- Previous versions of geventhttpclient used http_parser.c, which in turn
245
- was based on `src/http/ngx_http_parse.c` from NGINX, copyright Igor Sysoev,
246
- Joyent, Inc., and other Node contributors. For more information, see
247
- http://github.com/joyent/http-parser
244
+ This package is distributed under the [MIT license](https://github.com/geventhttpclient/geventhttpclient/blob/master/LICENSE-MIT).
245
+ Previous versions of geventhttpclient used `http_parser.c`, which in turn was
246
+ based on `http/ngx_http_parse.c` from [NGINX](https://nginx.org), copyright Igor
247
+ Sysoev, Joyent, Inc., and other Node contributors. For more information, see
248
+ http://github.com/joyent/http-parser
@@ -0,0 +1,185 @@
1
+ [![GitHub Workflow CI Status](https://img.shields.io/github/actions/workflow/status/geventhttpclient/geventhttpclient/test.yml?branch=master&logo=github&style=flat)](https://github.com/geventhttpclient/geventhttpclient/actions)
2
+ [![PyPI](https://img.shields.io/pypi/v/geventhttpclient.svg?style=flat)](https://pypi.org/project/geventhttpclient/)
3
+ ![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fgeventhttpclient%2Fgeventhttpclient%2Fmaster%2Fpyproject.toml)
4
+ ![PyPI - Downloads](https://img.shields.io/pypi/dm/geventhttpclient)
5
+
6
+ # geventhttpclient
7
+
8
+ A [high performance](https://github.com/geventhttpclient/geventhttpclient/blob/master/README.md#Benchmarks),
9
+ concurrent HTTP client library for python using
10
+ [gevent](http://gevent.org).
11
+
12
+ `gevent.httplib` support for patching `http.client` was removed in
13
+ [gevent 1.0](https://github.com/surfly/gevent/commit/b45b83b1bc4de14e3c4859362825044b8e3df7d6),
14
+ `geventhttpclient` now provides that missing functionality.
15
+
16
+ `geventhttpclient` uses a fast [http parser](https://github.com/nodejs/llhttp),
17
+ written in C.
18
+
19
+ `geventhttpclient` has been specifically designed for high concurrency,
20
+ streaming and support HTTP 1.1 persistent connections. More generally it is
21
+ designed for efficiently pulling from REST APIs and streaming APIs
22
+ like Twitter's.
23
+
24
+ Safe SSL support is provided by default. `geventhttpclient` depends on
25
+ the certifi CA Bundle. This is the same CA Bundle which ships with the
26
+ Requests codebase, and is derived from Mozilla Firefox's canonical set.
27
+
28
+ Since version 2.3, `geventhttpclient` features a largely `requests`
29
+ compatible interface. It covers basic HTTP usage including cookie
30
+ management, form data encoding or decoding of compressed data,
31
+ but otherwise isn't as feature rich as the original `requests`. For
32
+ simple use-cases, it can serve as a drop-in replacement.
33
+
34
+ ```python
35
+ import geventhttpclient as requests
36
+ requests.get("https://github.com").text
37
+ requests.post("http://httpbingo.org/post", data="asdfasd").json()
38
+
39
+ from geventhttpclient import Session
40
+ s = Session()
41
+ s.get("http://httpbingo.org/headers").json()
42
+ s.get("https://github.com").content
43
+ ```
44
+
45
+ This interface builds on top of the lower level `HTTPClient`.
46
+
47
+ ```python
48
+ from geventhttpclient import HTTPClient
49
+ from geventhttpclient.url import URL
50
+
51
+ url = URL("http://gevent.org/")
52
+ client = HTTPClient(url.host)
53
+ response = client.get(url.request_uri)
54
+ response.status_code
55
+ body = response.read()
56
+ client.close()
57
+ ```
58
+
59
+ ## httplib compatibility and monkey patch
60
+
61
+ `geventhttpclient.httplib` module contains classes for drop in
62
+ replacement of `http.client` connection and response objects.
63
+ If you use http.client directly you can replace the `httplib` imports
64
+ by `geventhttpclient.httplib`.
65
+
66
+ ```python
67
+ # from http.client import HTTPConnection
68
+ from geventhttpclient.httplib import HTTPConnection
69
+ ```
70
+
71
+ If you use `httplib2`, `urllib` or `urllib2`; you can patch `httplib` to
72
+ use the wrappers from `geventhttpclient`. For `httplib2`, make sure you
73
+ patch before you import or the `super()` calls will fail.
74
+
75
+ ```python
76
+ import geventhttpclient.httplib
77
+ geventhttpclient.httplib.patch()
78
+
79
+ import httplib2
80
+ ```
81
+
82
+ ## High Concurrency
83
+
84
+ `HTTPClient` has a connection pool built in and is greenlet safe by design.
85
+ You can use the same instance among several greenlets. It is the low
86
+ level building block of this library.
87
+
88
+ ```python
89
+ import gevent.pool
90
+ import json
91
+
92
+ from geventhttpclient import HTTPClient
93
+ from geventhttpclient.url import URL
94
+
95
+
96
+ # go to http://developers.facebook.com/tools/explorer and copy the access token
97
+ TOKEN = "<MY_DEV_TOKEN>"
98
+
99
+ url = URL("https://graph.facebook.com/me/friends", params={"access_token": TOKEN})
100
+
101
+ # setting the concurrency to 10 allow to create 10 connections and
102
+ # reuse them.
103
+ client = HTTPClient.from_url(url, concurrency=10)
104
+
105
+ response = client.get(url.request_uri)
106
+ assert response.status_code == 200
107
+
108
+ # response comply to the read protocol. It passes the stream to
109
+ # the json parser as it's being read.
110
+ data = json.load(response)["data"]
111
+
112
+ def print_friend_username(client, friend_id):
113
+ friend_url = URL(f"/{friend_id}", params={"access_token": TOKEN})
114
+ # the greenlet will block until a connection is available
115
+ response = client.get(friend_url.request_uri)
116
+ assert response.status_code == 200
117
+ friend = json.load(response)
118
+ if "username" in friend:
119
+ print(f"{friend['username']}: {friend['name']}")
120
+ else:
121
+ print(f"{friend['name']} has no username.")
122
+
123
+ # allow to run 20 greenlet at a time, this is more than concurrency
124
+ # of the http client but isn't a problem since the client has its own
125
+ # connection pool.
126
+ pool = gevent.pool.Pool(20)
127
+ for item in data:
128
+ friend_id = item["id"]
129
+ pool.spawn(print_friend_username, client, friend_id)
130
+
131
+ pool.join()
132
+ client.close()
133
+ ```
134
+
135
+ ## Streaming
136
+
137
+ `geventhttpclient` supports streaming. Response objects have a `read(n)` and
138
+ `readline()` method that read the stream incrementally.
139
+ See [examples/twitter_streaming.py](https://github.com/geventhttpclient/geventhttpclient/blob/master/examples/twitter_streaming.py)
140
+ for pulling twitter stream API.
141
+
142
+ Here is an example on how to download a big file chunk by chunk to save memory:
143
+
144
+ ```python
145
+ from geventhttpclient import HTTPClient, URL
146
+
147
+ url = URL("http://127.0.0.1:80/100.dat")
148
+ client = HTTPClient.from_url(url)
149
+ response = client.get(url.query_string)
150
+ assert response.status_code == 200
151
+
152
+ CHUNK_SIZE = 1024 * 16 # 16KB
153
+ with open("/tmp/100.dat", "w") as f:
154
+ data = response.read(CHUNK_SIZE)
155
+ while data:
156
+ f.write(data)
157
+ data = response.read(CHUNK_SIZE)
158
+ ```
159
+
160
+ ## Benchmarks
161
+
162
+ The benchmark runs 10000 `GET` requests against a local nginx server in the default
163
+ configuration with a concurrency of 10. See `benchmarks` folder. The requests per
164
+ second for a couple of popular clients is given in the table below. Please read
165
+ [benchmarks/README.md](https://github.com/geventhttpclient/geventhttpclient/blob/master/benchmarks/README.md)
166
+ for more details. Also note, [HTTPX](https://www.python-httpx.org/) is better be
167
+ used with `asyncio`, not `gevent`.
168
+
169
+ | HTTP Client | RPS |
170
+ |--------------------|--------|
171
+ | GeventHTTPClient | 7268.9 |
172
+ | Httplib2 (patched) | 2323.9 |
173
+ | Urllib3 | 2242.5 |
174
+ | Requests | 1046.1 |
175
+ | Httpx | 770.3 |
176
+
177
+ *Linux(x86_64), Python 3.11.6 @ Intel i7-7560U*
178
+
179
+ ## License
180
+
181
+ This package is distributed under the [MIT license](https://github.com/geventhttpclient/geventhttpclient/blob/master/LICENSE-MIT).
182
+ Previous versions of geventhttpclient used `http_parser.c`, which in turn was
183
+ based on `http/ngx_http_parse.c` from [NGINX](https://nginx.org), copyright Igor
184
+ Sysoev, Joyent, Inc., and other Node contributors. For more information, see
185
+ http://github.com/joyent/http-parser
@@ -23,9 +23,9 @@ typedef struct {
23
23
  static int on_message_begin(llhttp_t* parser)
24
24
  {
25
25
  int fail = 0;
26
- PyObject* self = (PyObject*)parser->data;
27
- if (PyObject_HasAttrString(self, "_on_message_begin")) {
28
- PyObject* callable = PyObject_GetAttrString(self, "_on_message_begin");
26
+ PyHTTPResponseParser *self = (PyHTTPResponseParser*) parser->data;
27
+ PyObject* callable = PyObject_GetAttrString((PyObject*)self, "_on_message_begin");
28
+ if (callable) {
29
29
  PyObject* result = PyObject_CallObject(callable, NULL);
30
30
  PyObject* exception = PyErr_Occurred();
31
31
  if (exception != NULL) {
@@ -45,8 +45,8 @@ static int on_message_complete(llhttp_t* parser)
45
45
  int fail = 0;
46
46
  PyHTTPResponseParser *self = (PyHTTPResponseParser*) parser->data;
47
47
  self->should_keep_alive = llhttp_should_keep_alive(parser) ? KA_TRUE : KA_FALSE;
48
- if (PyObject_HasAttrString(self, "_on_message_complete")) {
49
- PyObject* callable = PyObject_GetAttrString(self, "_on_message_complete");
48
+ PyObject* callable = PyObject_GetAttrString((PyObject*)self, "_on_message_complete");
49
+ if (callable) {
50
50
  PyObject* result = PyObject_CallObject(callable, NULL);
51
51
  PyObject* exception = PyErr_Occurred();
52
52
  if (exception != NULL) {
@@ -65,9 +65,9 @@ static int on_headers_complete(llhttp_t* parser)
65
65
  {
66
66
  /* 1 => skip body, 2 => upgrade, 0 => continue, -1 => error */
67
67
  int skip_body = 0;
68
- PyObject* self = (PyObject*)parser->data;
69
- if (PyObject_HasAttrString(self, "_on_headers_complete")) {
70
- PyObject* callable = PyObject_GetAttrString(self, "_on_headers_complete");
68
+ PyHTTPResponseParser *self = (PyHTTPResponseParser*) parser->data;
69
+ PyObject* callable = PyObject_GetAttrString((PyObject*)self, "_on_headers_complete");
70
+ if (callable) {
71
71
  PyObject* result = PyObject_CallObject(callable, NULL);
72
72
  PyObject* exception = PyErr_Occurred();
73
73
  if (exception != NULL) {
@@ -84,9 +84,9 @@ static int on_headers_complete(llhttp_t* parser)
84
84
  static int on_http_data_cb(llhttp_t* parser, const char *at, size_t length, const char * python_cb)
85
85
  {
86
86
  int fail = 0;
87
- PyObject* self = (PyObject*)parser->data;
88
- if (PyObject_HasAttrString(self, python_cb)) {
89
- PyObject* callable = PyObject_GetAttrString(self, python_cb);
87
+ PyHTTPResponseParser *self = (PyHTTPResponseParser*) parser->data;
88
+ PyObject* callable = PyObject_GetAttrString((PyObject*)self, python_cb);
89
+ if (callable) {
90
90
  PyObject* args = Py_BuildValue("(s#)", at, length);
91
91
  PyObject* result = PyObject_CallObject(callable, args);
92
92
  PyObject* exception = PyErr_Occurred();
@@ -100,7 +100,7 @@ static int on_http_data_cb(llhttp_t* parser, const char *at, size_t length, cons
100
100
  Py_DECREF(callable);
101
101
  Py_DECREF(args);
102
102
  }
103
- return fail;
103
+ return fail;
104
104
  }
105
105
 
106
106
  static int on_status(llhttp_t* parser, const char *at, size_t length)
@@ -121,9 +121,9 @@ static int on_header_value(llhttp_t* parser, const char *at, size_t length)
121
121
  static int on_body(llhttp_t* parser, const char *at, size_t length)
122
122
  {
123
123
  int fail = 0;
124
- PyObject* self = (PyObject*)parser->data;
125
- if (PyObject_HasAttrString(self, "_on_body")) {
126
- PyObject* callable = PyObject_GetAttrString(self, "_on_body");
124
+ PyHTTPResponseParser *self = (PyHTTPResponseParser*) parser->data;
125
+ PyObject* callable = PyObject_GetAttrString((PyObject*)self, "_on_body");
126
+ if (callable) {
127
127
  PyObject* bytearray = PyByteArray_FromStringAndSize(at, length);
128
128
  PyObject* result = PyObject_CallFunctionObjArgs(
129
129
  callable, bytearray, NULL);
@@ -269,7 +269,7 @@ PyHTTPResponseParser_should_keep_alive(PyHTTPResponseParser* self)
269
269
  if (self->error != HPE_OK) {
270
270
  Py_RETURN_FALSE;
271
271
  }
272
- int should_keep_alive;
272
+ int should_keep_alive = 0;
273
273
  switch (self->should_keep_alive) {
274
274
  case KA_INCOMPLETE:
275
275
  should_keep_alive = llhttp_should_keep_alive(self->parser);
@@ -305,7 +305,7 @@ static PyMethodDef PyHTTPResponseParser_methods[] = {
305
305
  "Get remaining content length to read"},
306
306
  {"should_keep_alive", (PyCFunction)PyHTTPResponseParser_should_keep_alive,
307
307
  METH_NOARGS,
308
- "Tell wether the connection should stay connected (HTTP 1.1)"},
308
+ "Tell whether the connection should stay connected (HTTP 1.1)"},
309
309
  {"parser_failed", (PyCFunction)PyHTTPResponseParser_parser_failed,
310
310
  METH_NOARGS,
311
311
  "Tell if parser have failed."},
@@ -0,0 +1,9 @@
1
+ # package
2
+
3
+ __version__ = "2.3.1" # dont forget to update version in pyproject.toml as well
4
+
5
+ from geventhttpclient.api import delete, get, head, options, patch, post, put, request
6
+ from geventhttpclient.client import HTTPClient
7
+ from geventhttpclient.requests import Session
8
+ from geventhttpclient.url import URL
9
+ from geventhttpclient.useragent import UserAgent