uncurl-httpx 0.1.0__tar.gz → 0.1.2__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.
- {uncurl_httpx-0.1.0/uncurl_httpx.egg-info → uncurl_httpx-0.1.2}/PKG-INFO +98 -13
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/README.md +96 -11
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/pyproject.toml +2 -2
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/tests/test_api.py +36 -1
- uncurl_httpx-0.1.2/uncurl/__init__.py +3 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/uncurl/api.py +87 -63
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/uncurl/bin.py +3 -3
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2/uncurl_httpx.egg-info}/PKG-INFO +98 -13
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/uncurl_httpx.egg-info/requires.txt +1 -1
- uncurl_httpx-0.1.0/uncurl/__init__.py +0 -3
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/LICENSE +0 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/setup.cfg +0 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/setup.py +0 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/tests/test_bin.py +0 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/uncurl_httpx.egg-info/SOURCES.txt +0 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/uncurl_httpx.egg-info/dependency_links.txt +0 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/uncurl_httpx.egg-info/entry_points.txt +0 -0
- {uncurl_httpx-0.1.0 → uncurl_httpx-0.1.2}/uncurl_httpx.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uncurl-httpx
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: uncurl for httpx, fork from uncurl-requests
|
|
5
5
|
Author-email: cyliu <liucy1983@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/liucy1983/uncurl-httpx
|
|
7
7
|
Requires-Python: >=3.12
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE
|
|
10
|
-
Requires-Dist: httpx>=0.28.
|
|
10
|
+
Requires-Dist: httpx>=0.28.1
|
|
11
11
|
Requires-Dist: pyperclip>=1.9.0
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: pytest>=9.0.3; extra == "dev"
|
|
@@ -59,7 +59,10 @@ The underlying API:
|
|
|
59
59
|
```python
|
|
60
60
|
import uncurl
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
parsed = uncurl.parse(
|
|
63
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
64
|
+
)
|
|
65
|
+
print(parsed.headers)
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
如果你想直接发起请求,也可以调用:
|
|
@@ -74,24 +77,54 @@ response = uncurl.request(
|
|
|
74
77
|
print(response.status_code)
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
If you want the generated `httpx` code string, ask explicitly:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import uncurl
|
|
84
|
+
|
|
85
|
+
print(
|
|
86
|
+
uncurl.parse(
|
|
87
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'",
|
|
88
|
+
as_object=False,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This prints:
|
|
78
94
|
|
|
79
95
|
```bash
|
|
80
|
-
|
|
96
|
+
httpx.get("https://pypi.python.org/pypi/uncurl-httpx", headers={
|
|
81
97
|
"Accept-Encoding": "gzip,deflate,sdch",
|
|
82
|
-
})
|
|
98
|
+
})
|
|
83
99
|
```
|
|
84
100
|
|
|
85
101
|
You can also retrieve the components as python objects:
|
|
86
102
|
|
|
87
103
|
```python
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
https://pypi.python.org/pypi/uncurl-httpx
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
import uncurl
|
|
105
|
+
|
|
106
|
+
context = uncurl.parse_context(
|
|
107
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
108
|
+
)
|
|
109
|
+
print(context.url)
|
|
110
|
+
print(context.headers)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`parse()` already returns a mutable object by default, so you can tweak and send it directly:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import uncurl
|
|
117
|
+
|
|
118
|
+
parsed = uncurl.parse(
|
|
119
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
120
|
+
)
|
|
121
|
+
parsed.url = "https://example.com/api"
|
|
122
|
+
parsed.headers["X-Debug"] = "1"
|
|
123
|
+
|
|
124
|
+
response = parsed.request(timeout=5.0)
|
|
125
|
+
print(response.status_code)
|
|
94
126
|
```
|
|
127
|
+
|
|
95
128
|
On Mac OS, you can also pipe input to uncurl:
|
|
96
129
|
|
|
97
130
|
```bash
|
|
@@ -101,5 +134,57 @@ pbpaste | uncurl
|
|
|
101
134
|
## Install
|
|
102
135
|
|
|
103
136
|
```console
|
|
104
|
-
$ pip install uncurl
|
|
137
|
+
$ pip install uncurl-httpx
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Release to PyPI with GitHub Actions
|
|
141
|
+
|
|
142
|
+
This repository includes `.github/workflows/publish.yml` for automated releases. However, if GitHub Actions is disabled on your account, you can publish manually.
|
|
143
|
+
|
|
144
|
+
## Manual Release to PyPI
|
|
145
|
+
|
|
146
|
+
Create a local `.env` file at the project root:
|
|
147
|
+
|
|
148
|
+
```dotenv
|
|
149
|
+
PYPI_TOKEN=pypi-your-token-from-pypi-org
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Then run:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
make publish
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Or directly:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
uv run python scripts/publish.py
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The script will:
|
|
165
|
+
1. Run tests
|
|
166
|
+
2. Build distributions
|
|
167
|
+
3. Check with `twine`
|
|
168
|
+
4. Upload to PyPI
|
|
169
|
+
|
|
170
|
+
After upload completes, the new version will appear on [PyPI](https://pypi.org/project/uncurl-httpx/).
|
|
171
|
+
|
|
172
|
+
## Automated Release with GitHub Actions
|
|
173
|
+
|
|
174
|
+
This repository includes `.github/workflows/publish.yml`.
|
|
175
|
+
Pushing a tag like `v0.1.1` will:
|
|
176
|
+
|
|
177
|
+
1. run the test suite,
|
|
178
|
+
2. build the package,
|
|
179
|
+
3. validate the built artifacts with `twine check`,
|
|
180
|
+
4. publish to PyPI.
|
|
181
|
+
|
|
182
|
+
Before using it, configure a PyPI Trusted Publisher for this GitHub repository and workflow.
|
|
183
|
+
|
|
184
|
+
Create and push a release tag with:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
git tag v0.1.1
|
|
188
|
+
git push origin v0.1.1
|
|
105
189
|
```
|
|
190
|
+
|
|
@@ -42,7 +42,10 @@ The underlying API:
|
|
|
42
42
|
```python
|
|
43
43
|
import uncurl
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
parsed = uncurl.parse(
|
|
46
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
47
|
+
)
|
|
48
|
+
print(parsed.headers)
|
|
46
49
|
```
|
|
47
50
|
|
|
48
51
|
如果你想直接发起请求,也可以调用:
|
|
@@ -57,24 +60,54 @@ response = uncurl.request(
|
|
|
57
60
|
print(response.status_code)
|
|
58
61
|
```
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
If you want the generated `httpx` code string, ask explicitly:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import uncurl
|
|
67
|
+
|
|
68
|
+
print(
|
|
69
|
+
uncurl.parse(
|
|
70
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'",
|
|
71
|
+
as_object=False,
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This prints:
|
|
61
77
|
|
|
62
78
|
```bash
|
|
63
|
-
|
|
79
|
+
httpx.get("https://pypi.python.org/pypi/uncurl-httpx", headers={
|
|
64
80
|
"Accept-Encoding": "gzip,deflate,sdch",
|
|
65
|
-
})
|
|
81
|
+
})
|
|
66
82
|
```
|
|
67
83
|
|
|
68
84
|
You can also retrieve the components as python objects:
|
|
69
85
|
|
|
70
86
|
```python
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
https://pypi.python.org/pypi/uncurl-httpx
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
import uncurl
|
|
88
|
+
|
|
89
|
+
context = uncurl.parse_context(
|
|
90
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
91
|
+
)
|
|
92
|
+
print(context.url)
|
|
93
|
+
print(context.headers)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`parse()` already returns a mutable object by default, so you can tweak and send it directly:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import uncurl
|
|
100
|
+
|
|
101
|
+
parsed = uncurl.parse(
|
|
102
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
103
|
+
)
|
|
104
|
+
parsed.url = "https://example.com/api"
|
|
105
|
+
parsed.headers["X-Debug"] = "1"
|
|
106
|
+
|
|
107
|
+
response = parsed.request(timeout=5.0)
|
|
108
|
+
print(response.status_code)
|
|
77
109
|
```
|
|
110
|
+
|
|
78
111
|
On Mac OS, you can also pipe input to uncurl:
|
|
79
112
|
|
|
80
113
|
```bash
|
|
@@ -84,5 +117,57 @@ pbpaste | uncurl
|
|
|
84
117
|
## Install
|
|
85
118
|
|
|
86
119
|
```console
|
|
87
|
-
$ pip install uncurl
|
|
120
|
+
$ pip install uncurl-httpx
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Release to PyPI with GitHub Actions
|
|
124
|
+
|
|
125
|
+
This repository includes `.github/workflows/publish.yml` for automated releases. However, if GitHub Actions is disabled on your account, you can publish manually.
|
|
126
|
+
|
|
127
|
+
## Manual Release to PyPI
|
|
128
|
+
|
|
129
|
+
Create a local `.env` file at the project root:
|
|
130
|
+
|
|
131
|
+
```dotenv
|
|
132
|
+
PYPI_TOKEN=pypi-your-token-from-pypi-org
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Then run:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
make publish
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Or directly:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
uv run python scripts/publish.py
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The script will:
|
|
148
|
+
1. Run tests
|
|
149
|
+
2. Build distributions
|
|
150
|
+
3. Check with `twine`
|
|
151
|
+
4. Upload to PyPI
|
|
152
|
+
|
|
153
|
+
After upload completes, the new version will appear on [PyPI](https://pypi.org/project/uncurl-httpx/).
|
|
154
|
+
|
|
155
|
+
## Automated Release with GitHub Actions
|
|
156
|
+
|
|
157
|
+
This repository includes `.github/workflows/publish.yml`.
|
|
158
|
+
Pushing a tag like `v0.1.1` will:
|
|
159
|
+
|
|
160
|
+
1. run the test suite,
|
|
161
|
+
2. build the package,
|
|
162
|
+
3. validate the built artifacts with `twine check`,
|
|
163
|
+
4. publish to PyPI.
|
|
164
|
+
|
|
165
|
+
Before using it, configure a PyPI Trusted Publisher for this GitHub repository and workflow.
|
|
166
|
+
|
|
167
|
+
Create and push a release tag with:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
git tag v0.1.1
|
|
171
|
+
git push origin v0.1.1
|
|
88
172
|
```
|
|
173
|
+
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "uncurl-httpx"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "uncurl for httpx, fork from uncurl-requests"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -12,7 +12,7 @@ authors = [
|
|
|
12
12
|
{ name = "cyliu", email = "liucy1983@gmail.com" },
|
|
13
13
|
]
|
|
14
14
|
dependencies = [
|
|
15
|
-
"httpx>=0.28.
|
|
15
|
+
"httpx>=0.28.1",
|
|
16
16
|
"pyperclip>=1.9.0",
|
|
17
17
|
]
|
|
18
18
|
|
|
@@ -3,7 +3,7 @@ from unittest.mock import sentinel
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def assert_parse(command, expected, **kwargs):
|
|
6
|
-
assert uncurl.parse(command, **kwargs) == expected
|
|
6
|
+
assert uncurl.parse(command, as_object=False, **kwargs) == expected
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def test_request_executes_httpx_request(mocker=None):
|
|
@@ -30,6 +30,41 @@ def test_request_executes_httpx_request(mocker=None):
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def test_parse_can_return_mutable_object():
|
|
34
|
+
parsed = uncurl.parse("curl 'https://pypi.python.org/pypi/uncurl-httpx'", as_object=True)
|
|
35
|
+
|
|
36
|
+
assert isinstance(parsed, uncurl.ParsedContext)
|
|
37
|
+
parsed.method = 'post'
|
|
38
|
+
parsed.data = 'patched'
|
|
39
|
+
|
|
40
|
+
assert parsed.to_code() == """httpx.post(\"https://pypi.python.org/pypi/uncurl-httpx\",
|
|
41
|
+
data='patched',
|
|
42
|
+
)"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_parse_object_can_request():
|
|
46
|
+
from unittest.mock import patch
|
|
47
|
+
|
|
48
|
+
parsed = uncurl.parse(
|
|
49
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'",
|
|
50
|
+
as_object=True,
|
|
51
|
+
)
|
|
52
|
+
parsed.url = 'https://example.com/override'
|
|
53
|
+
|
|
54
|
+
with patch("uncurl.api.httpx.request", return_value=sentinel.response) as request_mock:
|
|
55
|
+
response = parsed.request(timeout=1.0)
|
|
56
|
+
|
|
57
|
+
assert response is sentinel.response
|
|
58
|
+
request_mock.assert_called_once_with(
|
|
59
|
+
'get',
|
|
60
|
+
'https://example.com/override',
|
|
61
|
+
timeout=1.0,
|
|
62
|
+
headers={
|
|
63
|
+
'Accept-Encoding': 'gzip,deflate,sdch',
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
33
68
|
def test_basic_get():
|
|
34
69
|
assert_parse("curl 'https://pypi.python.org/pypi/uncurl-httpx'", """httpx.get("https://pypi.python.org/pypi/uncurl-httpx")"""
|
|
35
70
|
)
|
|
@@ -3,8 +3,10 @@ import argparse
|
|
|
3
3
|
import json
|
|
4
4
|
import re
|
|
5
5
|
import shlex
|
|
6
|
-
from collections import OrderedDict
|
|
6
|
+
from collections import OrderedDict
|
|
7
|
+
from dataclasses import dataclass, field
|
|
7
8
|
from http.cookies import SimpleCookie
|
|
9
|
+
from typing import Any, Optional, Tuple
|
|
8
10
|
|
|
9
11
|
import httpx
|
|
10
12
|
|
|
@@ -26,7 +28,85 @@ parser.add_argument('-U', '--proxy-user', default='')
|
|
|
26
28
|
|
|
27
29
|
BASE_INDENT = " " * 4
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ParsedContext:
|
|
34
|
+
method: str
|
|
35
|
+
url: str
|
|
36
|
+
data: Optional[str] = None
|
|
37
|
+
headers: OrderedDict = field(default_factory=OrderedDict)
|
|
38
|
+
cookies: OrderedDict = field(default_factory=OrderedDict)
|
|
39
|
+
verify: bool = False
|
|
40
|
+
auth: Tuple[str, ...] = ()
|
|
41
|
+
proxy: Any = field(default_factory=dict)
|
|
42
|
+
|
|
43
|
+
def build_request_kwargs(self, **kwargs):
|
|
44
|
+
request_kwargs = dict(kwargs)
|
|
45
|
+
if 'allow_redirects' in request_kwargs:
|
|
46
|
+
request_kwargs['follow_redirects'] = request_kwargs.pop('allow_redirects')
|
|
47
|
+
|
|
48
|
+
if self.data:
|
|
49
|
+
request_kwargs['data'] = self.data
|
|
50
|
+
|
|
51
|
+
if self.headers:
|
|
52
|
+
request_kwargs['headers'] = dict(self.headers)
|
|
53
|
+
|
|
54
|
+
if self.cookies:
|
|
55
|
+
request_kwargs['cookies'] = dict(self.cookies)
|
|
56
|
+
|
|
57
|
+
if self.auth:
|
|
58
|
+
request_kwargs['auth'] = self.auth
|
|
59
|
+
|
|
60
|
+
if self.proxy:
|
|
61
|
+
request_kwargs['proxy'] = self.proxy
|
|
62
|
+
|
|
63
|
+
if self.verify:
|
|
64
|
+
request_kwargs['verify'] = False
|
|
65
|
+
|
|
66
|
+
return request_kwargs
|
|
67
|
+
|
|
68
|
+
def to_code(self, **kwargs):
|
|
69
|
+
request_kwargs = []
|
|
70
|
+
|
|
71
|
+
for key, value in sorted(kwargs.items()):
|
|
72
|
+
if key == 'allow_redirects':
|
|
73
|
+
key = 'follow_redirects'
|
|
74
|
+
request_kwargs.append(format_request_arg(key, value))
|
|
75
|
+
|
|
76
|
+
if self.data:
|
|
77
|
+
request_kwargs.append(format_request_arg('data', self.data))
|
|
78
|
+
|
|
79
|
+
if self.headers:
|
|
80
|
+
request_kwargs.append(format_dict_arg('headers', self.headers))
|
|
81
|
+
|
|
82
|
+
if self.cookies:
|
|
83
|
+
request_kwargs.append(format_dict_arg('cookies', self.cookies))
|
|
84
|
+
|
|
85
|
+
if self.auth:
|
|
86
|
+
request_kwargs.append(format_request_arg('auth', self.auth))
|
|
87
|
+
|
|
88
|
+
if self.proxy:
|
|
89
|
+
request_kwargs.append(format_request_arg('proxy', self.proxy))
|
|
90
|
+
|
|
91
|
+
if self.verify:
|
|
92
|
+
request_kwargs.append(format_request_arg('verify', False, trailing_comma=False))
|
|
93
|
+
|
|
94
|
+
if not request_kwargs:
|
|
95
|
+
return "httpx.{method}({url})".format(
|
|
96
|
+
method=self.method,
|
|
97
|
+
url=json.dumps(self.url),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return """httpx.{method}({url},
|
|
101
|
+
{request_kwargs}
|
|
102
|
+
)""".format(
|
|
103
|
+
method=self.method,
|
|
104
|
+
url=json.dumps(self.url),
|
|
105
|
+
request_kwargs='\n'.join(request_kwargs),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def request(self, **kwargs):
|
|
109
|
+
return httpx.request(self.method, self.url, **self.build_request_kwargs(**kwargs))
|
|
30
110
|
|
|
31
111
|
|
|
32
112
|
def normalize_newlines(multiline_text):
|
|
@@ -95,74 +175,18 @@ def parse_context(curl_command):
|
|
|
95
175
|
)
|
|
96
176
|
|
|
97
177
|
|
|
98
|
-
def parse(curl_command, **
|
|
178
|
+
def parse(curl_command, as_object=True, **kwargs):
|
|
99
179
|
parsed_context = parse_context(curl_command)
|
|
100
180
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if key == 'allow_redirects':
|
|
104
|
-
key = 'follow_redirects'
|
|
105
|
-
request_kargs.append(format_request_arg(key, value))
|
|
106
|
-
|
|
107
|
-
if parsed_context.data:
|
|
108
|
-
request_kargs.append(format_request_arg('data', parsed_context.data))
|
|
109
|
-
|
|
110
|
-
if parsed_context.headers:
|
|
111
|
-
request_kargs.append(format_dict_arg('headers', parsed_context.headers))
|
|
112
|
-
|
|
113
|
-
if parsed_context.cookies:
|
|
114
|
-
request_kargs.append(format_dict_arg('cookies', parsed_context.cookies))
|
|
115
|
-
|
|
116
|
-
if parsed_context.auth:
|
|
117
|
-
request_kargs.append(format_request_arg('auth', parsed_context.auth))
|
|
118
|
-
|
|
119
|
-
if parsed_context.proxy:
|
|
120
|
-
request_kargs.append(format_request_arg('proxy', parsed_context.proxy))
|
|
121
|
-
|
|
122
|
-
if parsed_context.verify:
|
|
123
|
-
request_kargs.append(format_request_arg('verify', False, trailing_comma=False))
|
|
124
|
-
|
|
125
|
-
if not request_kargs:
|
|
126
|
-
return "httpx.{method}({url})".format(
|
|
127
|
-
method=parsed_context.method,
|
|
128
|
-
url=json.dumps(parsed_context.url),
|
|
129
|
-
)
|
|
181
|
+
if as_object:
|
|
182
|
+
return parsed_context
|
|
130
183
|
|
|
131
|
-
return
|
|
132
|
-
{request_kargs}
|
|
133
|
-
)""".format(
|
|
134
|
-
method=parsed_context.method,
|
|
135
|
-
url=json.dumps(parsed_context.url),
|
|
136
|
-
request_kargs='\n'.join(request_kargs),
|
|
137
|
-
)
|
|
184
|
+
return parsed_context.to_code(**kwargs)
|
|
138
185
|
|
|
139
186
|
|
|
140
187
|
def request(curl_command, **kwargs):
|
|
141
188
|
parsed_context = parse_context(curl_command)
|
|
142
|
-
|
|
143
|
-
request_kwargs = dict(kwargs)
|
|
144
|
-
if 'allow_redirects' in request_kwargs:
|
|
145
|
-
request_kwargs['follow_redirects'] = request_kwargs.pop('allow_redirects')
|
|
146
|
-
|
|
147
|
-
if parsed_context.data:
|
|
148
|
-
request_kwargs['data'] = parsed_context.data
|
|
149
|
-
|
|
150
|
-
if parsed_context.headers:
|
|
151
|
-
request_kwargs['headers'] = dict(parsed_context.headers)
|
|
152
|
-
|
|
153
|
-
if parsed_context.cookies:
|
|
154
|
-
request_kwargs['cookies'] = dict(parsed_context.cookies)
|
|
155
|
-
|
|
156
|
-
if parsed_context.auth:
|
|
157
|
-
request_kwargs['auth'] = parsed_context.auth
|
|
158
|
-
|
|
159
|
-
if parsed_context.proxy:
|
|
160
|
-
request_kwargs['proxy'] = parsed_context.proxy
|
|
161
|
-
|
|
162
|
-
if parsed_context.verify:
|
|
163
|
-
request_kwargs['verify'] = False
|
|
164
|
-
|
|
165
|
-
return httpx.request(parsed_context.method, parsed_context.url, **request_kwargs)
|
|
189
|
+
return parsed_context.request(**kwargs)
|
|
166
190
|
|
|
167
191
|
|
|
168
192
|
def format_request_arg(key, value, trailing_comma=True):
|
|
@@ -10,10 +10,10 @@ def main():
|
|
|
10
10
|
if sys.stdin.isatty():
|
|
11
11
|
if len(sys.argv) > 1:
|
|
12
12
|
# If an argument is passed
|
|
13
|
-
result = parse(sys.argv[1])
|
|
13
|
+
result = parse(sys.argv[1], as_object=False)
|
|
14
14
|
else:
|
|
15
15
|
# Otherwise pull from clipboard
|
|
16
|
-
result = parse(pyperclip.paste())
|
|
16
|
+
result = parse(pyperclip.paste(), as_object=False)
|
|
17
17
|
else:
|
|
18
|
-
result = parse(sys.stdin.read())
|
|
18
|
+
result = parse(sys.stdin.read(), as_object=False)
|
|
19
19
|
print("\n" + result)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: uncurl-httpx
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: uncurl for httpx, fork from uncurl-requests
|
|
5
5
|
Author-email: cyliu <liucy1983@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/liucy1983/uncurl-httpx
|
|
7
7
|
Requires-Python: >=3.12
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE
|
|
10
|
-
Requires-Dist: httpx>=0.28.
|
|
10
|
+
Requires-Dist: httpx>=0.28.1
|
|
11
11
|
Requires-Dist: pyperclip>=1.9.0
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: pytest>=9.0.3; extra == "dev"
|
|
@@ -59,7 +59,10 @@ The underlying API:
|
|
|
59
59
|
```python
|
|
60
60
|
import uncurl
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
parsed = uncurl.parse(
|
|
63
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
64
|
+
)
|
|
65
|
+
print(parsed.headers)
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
如果你想直接发起请求,也可以调用:
|
|
@@ -74,24 +77,54 @@ response = uncurl.request(
|
|
|
74
77
|
print(response.status_code)
|
|
75
78
|
```
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
If you want the generated `httpx` code string, ask explicitly:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import uncurl
|
|
84
|
+
|
|
85
|
+
print(
|
|
86
|
+
uncurl.parse(
|
|
87
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'",
|
|
88
|
+
as_object=False,
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This prints:
|
|
78
94
|
|
|
79
95
|
```bash
|
|
80
|
-
|
|
96
|
+
httpx.get("https://pypi.python.org/pypi/uncurl-httpx", headers={
|
|
81
97
|
"Accept-Encoding": "gzip,deflate,sdch",
|
|
82
|
-
})
|
|
98
|
+
})
|
|
83
99
|
```
|
|
84
100
|
|
|
85
101
|
You can also retrieve the components as python objects:
|
|
86
102
|
|
|
87
103
|
```python
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
https://pypi.python.org/pypi/uncurl-httpx
|
|
92
|
-
|
|
93
|
-
|
|
104
|
+
import uncurl
|
|
105
|
+
|
|
106
|
+
context = uncurl.parse_context(
|
|
107
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
108
|
+
)
|
|
109
|
+
print(context.url)
|
|
110
|
+
print(context.headers)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`parse()` already returns a mutable object by default, so you can tweak and send it directly:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
import uncurl
|
|
117
|
+
|
|
118
|
+
parsed = uncurl.parse(
|
|
119
|
+
"curl 'https://pypi.python.org/pypi/uncurl-httpx' -H 'Accept-Encoding: gzip,deflate,sdch'"
|
|
120
|
+
)
|
|
121
|
+
parsed.url = "https://example.com/api"
|
|
122
|
+
parsed.headers["X-Debug"] = "1"
|
|
123
|
+
|
|
124
|
+
response = parsed.request(timeout=5.0)
|
|
125
|
+
print(response.status_code)
|
|
94
126
|
```
|
|
127
|
+
|
|
95
128
|
On Mac OS, you can also pipe input to uncurl:
|
|
96
129
|
|
|
97
130
|
```bash
|
|
@@ -101,5 +134,57 @@ pbpaste | uncurl
|
|
|
101
134
|
## Install
|
|
102
135
|
|
|
103
136
|
```console
|
|
104
|
-
$ pip install uncurl
|
|
137
|
+
$ pip install uncurl-httpx
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Release to PyPI with GitHub Actions
|
|
141
|
+
|
|
142
|
+
This repository includes `.github/workflows/publish.yml` for automated releases. However, if GitHub Actions is disabled on your account, you can publish manually.
|
|
143
|
+
|
|
144
|
+
## Manual Release to PyPI
|
|
145
|
+
|
|
146
|
+
Create a local `.env` file at the project root:
|
|
147
|
+
|
|
148
|
+
```dotenv
|
|
149
|
+
PYPI_TOKEN=pypi-your-token-from-pypi-org
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Then run:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
make publish
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Or directly:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
uv run python scripts/publish.py
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The script will:
|
|
165
|
+
1. Run tests
|
|
166
|
+
2. Build distributions
|
|
167
|
+
3. Check with `twine`
|
|
168
|
+
4. Upload to PyPI
|
|
169
|
+
|
|
170
|
+
After upload completes, the new version will appear on [PyPI](https://pypi.org/project/uncurl-httpx/).
|
|
171
|
+
|
|
172
|
+
## Automated Release with GitHub Actions
|
|
173
|
+
|
|
174
|
+
This repository includes `.github/workflows/publish.yml`.
|
|
175
|
+
Pushing a tag like `v0.1.1` will:
|
|
176
|
+
|
|
177
|
+
1. run the test suite,
|
|
178
|
+
2. build the package,
|
|
179
|
+
3. validate the built artifacts with `twine check`,
|
|
180
|
+
4. publish to PyPI.
|
|
181
|
+
|
|
182
|
+
Before using it, configure a PyPI Trusted Publisher for this GitHub repository and workflow.
|
|
183
|
+
|
|
184
|
+
Create and push a release tag with:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
git tag v0.1.1
|
|
188
|
+
git push origin v0.1.1
|
|
105
189
|
```
|
|
190
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|