urban-http 0.1.0__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.
- urban_http-0.1.0/.github/workflows/ci.yml +62 -0
- urban_http-0.1.0/.github/workflows/publish.yml +87 -0
- urban_http-0.1.0/.gitignore +29 -0
- urban_http-0.1.0/LICENSE +21 -0
- urban_http-0.1.0/PKG-INFO +235 -0
- urban_http-0.1.0/README.md +172 -0
- urban_http-0.1.0/RELEASING.md +62 -0
- urban_http-0.1.0/demo/index.html +408 -0
- urban_http-0.1.0/demo/server.py +210 -0
- urban_http-0.1.0/docs/curl-coverage.md +171 -0
- urban_http-0.1.0/docs/curl-use-cases.md +175 -0
- urban_http-0.1.0/docs/openapi.md +106 -0
- urban_http-0.1.0/docs/postman-collections.md +76 -0
- urban_http-0.1.0/docs/protocols.md +142 -0
- urban_http-0.1.0/docs/variables.md +93 -0
- urban_http-0.1.0/pyproject.toml +62 -0
- urban_http-0.1.0/src/urban_http/__init__.py +59 -0
- urban_http-0.1.0/src/urban_http/cli.py +119 -0
- urban_http-0.1.0/src/urban_http/command.py +61 -0
- urban_http-0.1.0/src/urban_http/cookies.py +101 -0
- urban_http-0.1.0/src/urban_http/digest.py +113 -0
- urban_http-0.1.0/src/urban_http/execute.py +349 -0
- urban_http-0.1.0/src/urban_http/glob.py +95 -0
- urban_http-0.1.0/src/urban_http/model.py +132 -0
- urban_http-0.1.0/src/urban_http/net.py +170 -0
- urban_http-0.1.0/src/urban_http/openapi.py +345 -0
- urban_http-0.1.0/src/urban_http/parse.py +810 -0
- urban_http-0.1.0/src/urban_http/postman.py +221 -0
- urban_http-0.1.0/src/urban_http/protocols/__init__.py +59 -0
- urban_http-0.1.0/src/urban_http/protocols/file.py +73 -0
- urban_http-0.1.0/src/urban_http/protocols/ftp.py +103 -0
- urban_http-0.1.0/src/urban_http/protocols/imap.py +99 -0
- urban_http-0.1.0/src/urban_http/protocols/mqtt.py +81 -0
- urban_http-0.1.0/src/urban_http/protocols/parse.py +314 -0
- urban_http-0.1.0/src/urban_http/protocols/pop3.py +72 -0
- urban_http-0.1.0/src/urban_http/protocols/registry.py +35 -0
- urban_http-0.1.0/src/urban_http/protocols/scp.py +83 -0
- urban_http-0.1.0/src/urban_http/protocols/sftp.py +82 -0
- urban_http-0.1.0/src/urban_http/protocols/smb.py +98 -0
- urban_http-0.1.0/src/urban_http/protocols/smtp.py +88 -0
- urban_http-0.1.0/src/urban_http/protocols/tftp.py +119 -0
- urban_http-0.1.0/src/urban_http/protocols/types.py +119 -0
- urban_http-0.1.0/src/urban_http/protocols/ws.py +91 -0
- urban_http-0.1.0/src/urban_http/tokenize.py +189 -0
- urban_http-0.1.0/src/urban_http/variables.py +114 -0
- urban_http-0.1.0/tests/conftest.py +5 -0
- urban_http-0.1.0/tests/test_advanced.py +234 -0
- urban_http-0.1.0/tests/test_cookies.py +50 -0
- urban_http-0.1.0/tests/test_digest.py +57 -0
- urban_http-0.1.0/tests/test_execute.py +90 -0
- urban_http-0.1.0/tests/test_glob.py +56 -0
- urban_http-0.1.0/tests/test_imap_scp.py +90 -0
- urban_http-0.1.0/tests/test_openapi.py +181 -0
- urban_http-0.1.0/tests/test_parse.py +178 -0
- urban_http-0.1.0/tests/test_postman.py +117 -0
- urban_http-0.1.0/tests/test_protocols_adapters.py +244 -0
- urban_http-0.1.0/tests/test_protocols_parse.py +91 -0
- urban_http-0.1.0/tests/test_protocols_signaling.py +51 -0
- urban_http-0.1.0/tests/test_tokenize.py +41 -0
- urban_http-0.1.0/tests/test_variables.py +86 -0
- urban_http-0.1.0/tests/test_websocket.py +80 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ci-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: ${{ matrix.python-version }}
|
|
31
|
+
|
|
32
|
+
- name: Installer le paquet et les dépendances de test
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
python -m pip install ".[dev]" pyyaml
|
|
36
|
+
|
|
37
|
+
- name: Lancer les tests
|
|
38
|
+
run: pytest -q
|
|
39
|
+
|
|
40
|
+
build:
|
|
41
|
+
name: Build & vérification du paquet
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v4
|
|
45
|
+
|
|
46
|
+
- uses: actions/setup-python@v5
|
|
47
|
+
with:
|
|
48
|
+
python-version: "3.12"
|
|
49
|
+
|
|
50
|
+
- name: Build sdist + wheel
|
|
51
|
+
run: |
|
|
52
|
+
python -m pip install --upgrade build twine
|
|
53
|
+
python -m build
|
|
54
|
+
|
|
55
|
+
- name: Vérifier les métadonnées du paquet
|
|
56
|
+
run: python -m twine check dist/*
|
|
57
|
+
|
|
58
|
+
- name: Conserver les artefacts
|
|
59
|
+
uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: distributions
|
|
62
|
+
path: dist/
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Publier sur PyPI
|
|
2
|
+
|
|
3
|
+
# Déclenché en poussant un tag de version, ex. : git tag v0.2.0 && git push origin v0.2.0
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
repository:
|
|
10
|
+
description: "Cible : pypi ou testpypi"
|
|
11
|
+
default: "pypi"
|
|
12
|
+
type: choice
|
|
13
|
+
options: ["pypi", "testpypi"]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
name: Construire le paquet
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
|
|
29
|
+
- name: Vérifier que le tag correspond à la version du paquet
|
|
30
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
31
|
+
run: |
|
|
32
|
+
TAG="${GITHUB_REF_NAME#v}"
|
|
33
|
+
VERSION="$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
|
|
34
|
+
echo "tag=$TAG version_pyproject=$VERSION"
|
|
35
|
+
if [ "$TAG" != "$VERSION" ]; then
|
|
36
|
+
echo "::error::Le tag v$TAG ne correspond pas à project.version=$VERSION dans pyproject.toml"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
- name: Build sdist + wheel
|
|
41
|
+
run: |
|
|
42
|
+
python -m pip install --upgrade build twine
|
|
43
|
+
python -m build
|
|
44
|
+
python -m twine check dist/*
|
|
45
|
+
|
|
46
|
+
- uses: actions/upload-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: distributions
|
|
49
|
+
path: dist/
|
|
50
|
+
|
|
51
|
+
publish-testpypi:
|
|
52
|
+
name: Publier sur TestPyPI
|
|
53
|
+
if: github.event_name == 'workflow_dispatch' && inputs.repository == 'testpypi'
|
|
54
|
+
needs: build
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
environment:
|
|
57
|
+
name: testpypi
|
|
58
|
+
url: https://test.pypi.org/p/urban-http
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write # OIDC pour le Trusted Publishing, aucun token stocké
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: distributions
|
|
65
|
+
path: dist/
|
|
66
|
+
- name: Téléverser vers TestPyPI
|
|
67
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
68
|
+
with:
|
|
69
|
+
repository-url: https://test.pypi.org/legacy/
|
|
70
|
+
|
|
71
|
+
publish-pypi:
|
|
72
|
+
name: Publier sur PyPI
|
|
73
|
+
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.repository == 'pypi')
|
|
74
|
+
needs: build
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
environment:
|
|
77
|
+
name: pypi
|
|
78
|
+
url: https://pypi.org/p/urban-http
|
|
79
|
+
permissions:
|
|
80
|
+
id-token: write # OIDC pour le Trusted Publishing, aucun token stocké
|
|
81
|
+
steps:
|
|
82
|
+
- uses: actions/download-artifact@v4
|
|
83
|
+
with:
|
|
84
|
+
name: distributions
|
|
85
|
+
path: dist/
|
|
86
|
+
- name: Téléverser vers PyPI
|
|
87
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Environnements virtuels
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
env/
|
|
16
|
+
|
|
17
|
+
# Tests et couverture
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
coverage.xml
|
|
21
|
+
htmlcov/
|
|
22
|
+
.tox/
|
|
23
|
+
.mypy_cache/
|
|
24
|
+
.ruff_cache/
|
|
25
|
+
|
|
26
|
+
# Éditeurs / OS
|
|
27
|
+
.DS_Store
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
urban_http-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 black beard
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: urban-http
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Transformer une commande curl ou une collection Postman en appel exécutable (HTTP et autres protocoles).
|
|
5
|
+
Project-URL: Repository, https://github.com/gaelgael5/urban-http
|
|
6
|
+
Author: urban-http
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 black beard
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Keywords: curl,http,parser,postman,sdk
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
37
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
38
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
39
|
+
Requires-Python: >=3.9
|
|
40
|
+
Provides-Extra: all
|
|
41
|
+
Requires-Dist: flask>=3; extra == 'all'
|
|
42
|
+
Requires-Dist: paho-mqtt>=2; extra == 'all'
|
|
43
|
+
Requires-Dist: paramiko>=3; extra == 'all'
|
|
44
|
+
Requires-Dist: smbprotocol>=1.12; extra == 'all'
|
|
45
|
+
Requires-Dist: websockets>=12; extra == 'all'
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
48
|
+
Provides-Extra: mqtt
|
|
49
|
+
Requires-Dist: paho-mqtt>=2; extra == 'mqtt'
|
|
50
|
+
Provides-Extra: openapi
|
|
51
|
+
Requires-Dist: pyyaml>=6; extra == 'openapi'
|
|
52
|
+
Provides-Extra: scp
|
|
53
|
+
Requires-Dist: paramiko>=3; extra == 'scp'
|
|
54
|
+
Provides-Extra: sftp
|
|
55
|
+
Requires-Dist: paramiko>=3; extra == 'sftp'
|
|
56
|
+
Provides-Extra: smb
|
|
57
|
+
Requires-Dist: smbprotocol>=1.12; extra == 'smb'
|
|
58
|
+
Provides-Extra: web
|
|
59
|
+
Requires-Dist: flask>=3; extra == 'web'
|
|
60
|
+
Provides-Extra: websocket
|
|
61
|
+
Requires-Dist: websockets>=12; extra == 'websocket'
|
|
62
|
+
Description-Content-Type: text/markdown
|
|
63
|
+
|
|
64
|
+
# urban-http
|
|
65
|
+
|
|
66
|
+
[](https://github.com/gaelgael5/urban-http/actions/workflows/ci.yml)
|
|
67
|
+
|
|
68
|
+
SDK **Python** pour transformer une commande **curl** ou une **collection Postman**
|
|
69
|
+
en **appel exécutable** — HTTP via la bibliothèque standard, et les autres
|
|
70
|
+
protocoles de curl (SMTP, FTP, SFTP, POP3, MQTT, TFTP, FILE, SMB, WebSocket) via
|
|
71
|
+
des adaptateurs dont les dépendances sont gérées en **extras PyPI**. Protocoles
|
|
72
|
+
couverts : HTTP, SMTP, FTP/FTPS, SFTP, SCP, POP3, IMAP, MQTT, TFTP, FILE, SMB,
|
|
73
|
+
WebSocket.
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
commande curl ───▶ parse_curl() ────┐
|
|
77
|
+
├──▶ HttpRequestModel ──▶ prepare_request() / execute()
|
|
78
|
+
collection Postman ─▶ parse_postman() ┘
|
|
79
|
+
└▶ parse_command() ──▶ ProtocolRequest ──▶ run() (adaptateur)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install urban-http # cœur : HTTP + SMTP/FTP/POP3/TFTP/FILE (stdlib, zéro dépendance)
|
|
86
|
+
pip install "urban-http[sftp,mqtt,smb]" # extras pour les protocoles qui requièrent une bibliothèque
|
|
87
|
+
pip install "urban-http[all]" # tout
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Le **cœur n'a aucune dépendance obligatoire**. Chaque protocole qui s'appuie sur
|
|
91
|
+
une bibliothèque tierce est un extra :
|
|
92
|
+
|
|
93
|
+
| Extra | Protocole | Bibliothèque PyPI |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `sftp` | SFTP | paramiko |
|
|
96
|
+
| `mqtt` | MQTT / MQTTS | paho-mqtt |
|
|
97
|
+
| `smb` | SMB / SMBS | smbprotocol |
|
|
98
|
+
| `websocket` | WS / WSS | websockets |
|
|
99
|
+
| `web` | site de test local | flask (optionnel) |
|
|
100
|
+
|
|
101
|
+
SMTP, FTP/FTPS, POP3, IMAP, TFTP et FILE utilisent uniquement la bibliothèque
|
|
102
|
+
standard (`smtplib`, `ftplib`, `poplib`, `imaplib`, `socket`, `open`) : aucun
|
|
103
|
+
extra. SCP partage paramiko avec SFTP.
|
|
104
|
+
|
|
105
|
+
## Utilisation
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from urban_http import parse_curl, execute
|
|
109
|
+
|
|
110
|
+
result = parse_curl(
|
|
111
|
+
"curl -X POST https://api.example.com/items "
|
|
112
|
+
"-H 'Content-Type: application/json' -d '{\"name\": \"clavier\"}' -u alice:secret -m 10"
|
|
113
|
+
)
|
|
114
|
+
req = result.requests[0]
|
|
115
|
+
req.method # "POST"
|
|
116
|
+
req.url # "https://api.example.com/items"
|
|
117
|
+
req.header("Content-Type") # "application/json"
|
|
118
|
+
|
|
119
|
+
resp = execute(req).response
|
|
120
|
+
print(resp.status, resp.text())
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Tout ce qui n'est pas reproductible est **signalé dans `warnings`** (jamais ignoré
|
|
124
|
+
en silence) : chaque warning a un `code` stable, un `message` et un niveau.
|
|
125
|
+
|
|
126
|
+
## Variables
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# {{nom}} partout, et $NOM / ${NOM} comme dans le shell (quotes doubles et nu)
|
|
130
|
+
parse_curl('curl -H "Authorization: Bearer $TOKEN" {{baseUrl}}/users',
|
|
131
|
+
{"baseUrl": "https://api.example.com", "TOKEN": "tok-42"})
|
|
132
|
+
|
|
133
|
+
# Dynamiques Postman générées : {{$guid}}, {{$randomUUID}}, {{$timestamp}}, {{$isoTimestamp}}, {{$randomInt}}
|
|
134
|
+
from urban_http import parse_postman_environment
|
|
135
|
+
variables = parse_postman_environment(env_json) # export d'environnement Postman
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Collections Postman
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
from urban_http import parse_postman, is_postman_collection
|
|
142
|
+
|
|
143
|
+
result = parse_postman(collection_json, {"baseUrl": "https://api.example.com"})
|
|
144
|
+
for req in result.requests:
|
|
145
|
+
print(req.name, req.method, req.url)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Format Collection v2.0 / v2.1 : dossiers imbriqués, variables, tous les modes de
|
|
149
|
+
corps (raw, urlencoded, formdata, file, graphql), auth basic/bearer/apikey avec
|
|
150
|
+
héritage collection → dossier → requête.
|
|
151
|
+
|
|
152
|
+
## Autres protocoles
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from urban_http import run, register_default_adapters
|
|
156
|
+
|
|
157
|
+
register_default_adapters() # une fois
|
|
158
|
+
|
|
159
|
+
# Envoi d'un mail (smtplib), téléversement FTP (ftplib), publication MQTT (paho)...
|
|
160
|
+
result = run("curl smtp://mail.example.com --mail-from moi@x.com --mail-rcpt toi@y.com -T message.eml")
|
|
161
|
+
print(result.protocol.summary)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`run()` détecte le protocole et route : HTTP via urllib, sinon l'adaptateur
|
|
165
|
+
enregistré. Chaque adaptateur accepte un `client_factory` injectable (tests sans
|
|
166
|
+
réseau, ou autre bibliothèque). Détail : **[docs/protocols.md](docs/protocols.md)**.
|
|
167
|
+
|
|
168
|
+
## OpenAPI → curl
|
|
169
|
+
|
|
170
|
+
`openapi_to_curl(contrat)` lit un contrat OpenAPI v3.x (dict, JSON, ou YAML avec
|
|
171
|
+
l'extra `openapi`) et renvoie, pour chaque opération, une commande curl avec des
|
|
172
|
+
variables injectables, le tableau de ses paramètres, et un modèle générique du
|
|
173
|
+
corps de requête.
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from urban_http import openapi_to_curl, parse_curl
|
|
177
|
+
|
|
178
|
+
for op in openapi_to_curl(contrat):
|
|
179
|
+
print(op.method, op.path)
|
|
180
|
+
print(op.curl) # avec des {{placeholders}}
|
|
181
|
+
print(op.example_variables) # {nom: valeur} prêt pour parse_curl
|
|
182
|
+
req = parse_curl(op.curl, op.example_variables).requests[0]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Le corps est généré exhaustivement avec des valeurs types : chaîne → nom de la
|
|
186
|
+
propriété, entier → 0, date/date-time → instant courant, sous-objet développé,
|
|
187
|
+
tableau à un seul élément. Détail : **[docs/openapi.md](docs/openapi.md)**.
|
|
188
|
+
|
|
189
|
+
## Ligne de commande
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
urban-http "curl -X POST https://api.example.com -d a=1" # analyse et affiche la requête
|
|
193
|
+
urban-http "curl file:///etc/hostname" --run # analyse ET exécute
|
|
194
|
+
urban-http "curl {{baseUrl}}/get" --var baseUrl=https://httpbin.org
|
|
195
|
+
urban-http --openapi contrat.json # génère les curl d'un contrat OpenAPI
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Site de test
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
python demo/server.py # http://127.0.0.1:8000
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Une page web qui analyse en direct n'importe quelle commande curl ou collection
|
|
205
|
+
Postman (analyse faite en Python côté serveur) et exécute les requêtes HTTP. Les
|
|
206
|
+
exemples HTTP ciblent httpbin.org.
|
|
207
|
+
|
|
208
|
+
## Développement
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
python -m pytest # 199 tests : tokenizer, parser, Postman, variables,
|
|
212
|
+
# globbing, cookies, Digest, exécution, protocoles,
|
|
213
|
+
# options TLS/réseau, OpenAPI
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
La CI (GitHub Actions) lance les tests sur Python 3.9 → 3.13 et construit le
|
|
217
|
+
paquet à chaque push / PR. Une version se publie sur PyPI en poussant un tag
|
|
218
|
+
`vX.Y.Z` — voir **[RELEASING.md](RELEASING.md)**.
|
|
219
|
+
|
|
220
|
+
## Couverture de curl
|
|
221
|
+
|
|
222
|
+
- **HTTP/HTTPS** : requête, corps, multipart, globbing, redirections, retries,
|
|
223
|
+
timeout, auth Basic/Bearer/Digest, cookies (inline et fichiers Netscape),
|
|
224
|
+
variables — plus l'import Postman. Détail : [docs/curl-use-cases.md](docs/curl-use-cases.md).
|
|
225
|
+
- **Autres protocoles** : SMTP, FTP/FTPS, SFTP, SCP, POP3, IMAP, MQTT, TFTP,
|
|
226
|
+
FILE, SMB, WebSocket. Détail : [docs/protocols.md](docs/protocols.md).
|
|
227
|
+
- **Panorama complet** (ce qui est couvert et ce qui ne l'est pas) :
|
|
228
|
+
[docs/curl-coverage.md](docs/curl-coverage.md).
|
|
229
|
+
|
|
230
|
+
## Feuille de route
|
|
231
|
+
|
|
232
|
+
- Protocoles restants : LDAP, TELNET.
|
|
233
|
+
- Auth : AWS SigV4, NTLM/Negotiate, Digest `qop=auth-int`.
|
|
234
|
+
- Requêtes conditionnelles (`-z`, `--etag-compare`), reprise `-C -` automatique.
|
|
235
|
+
- Générateurs de code supplémentaires (httpx, requests).
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# urban-http
|
|
2
|
+
|
|
3
|
+
[](https://github.com/gaelgael5/urban-http/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
SDK **Python** pour transformer une commande **curl** ou une **collection Postman**
|
|
6
|
+
en **appel exécutable** — HTTP via la bibliothèque standard, et les autres
|
|
7
|
+
protocoles de curl (SMTP, FTP, SFTP, POP3, MQTT, TFTP, FILE, SMB, WebSocket) via
|
|
8
|
+
des adaptateurs dont les dépendances sont gérées en **extras PyPI**. Protocoles
|
|
9
|
+
couverts : HTTP, SMTP, FTP/FTPS, SFTP, SCP, POP3, IMAP, MQTT, TFTP, FILE, SMB,
|
|
10
|
+
WebSocket.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
commande curl ───▶ parse_curl() ────┐
|
|
14
|
+
├──▶ HttpRequestModel ──▶ prepare_request() / execute()
|
|
15
|
+
collection Postman ─▶ parse_postman() ┘
|
|
16
|
+
└▶ parse_command() ──▶ ProtocolRequest ──▶ run() (adaptateur)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install urban-http # cœur : HTTP + SMTP/FTP/POP3/TFTP/FILE (stdlib, zéro dépendance)
|
|
23
|
+
pip install "urban-http[sftp,mqtt,smb]" # extras pour les protocoles qui requièrent une bibliothèque
|
|
24
|
+
pip install "urban-http[all]" # tout
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Le **cœur n'a aucune dépendance obligatoire**. Chaque protocole qui s'appuie sur
|
|
28
|
+
une bibliothèque tierce est un extra :
|
|
29
|
+
|
|
30
|
+
| Extra | Protocole | Bibliothèque PyPI |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `sftp` | SFTP | paramiko |
|
|
33
|
+
| `mqtt` | MQTT / MQTTS | paho-mqtt |
|
|
34
|
+
| `smb` | SMB / SMBS | smbprotocol |
|
|
35
|
+
| `websocket` | WS / WSS | websockets |
|
|
36
|
+
| `web` | site de test local | flask (optionnel) |
|
|
37
|
+
|
|
38
|
+
SMTP, FTP/FTPS, POP3, IMAP, TFTP et FILE utilisent uniquement la bibliothèque
|
|
39
|
+
standard (`smtplib`, `ftplib`, `poplib`, `imaplib`, `socket`, `open`) : aucun
|
|
40
|
+
extra. SCP partage paramiko avec SFTP.
|
|
41
|
+
|
|
42
|
+
## Utilisation
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from urban_http import parse_curl, execute
|
|
46
|
+
|
|
47
|
+
result = parse_curl(
|
|
48
|
+
"curl -X POST https://api.example.com/items "
|
|
49
|
+
"-H 'Content-Type: application/json' -d '{\"name\": \"clavier\"}' -u alice:secret -m 10"
|
|
50
|
+
)
|
|
51
|
+
req = result.requests[0]
|
|
52
|
+
req.method # "POST"
|
|
53
|
+
req.url # "https://api.example.com/items"
|
|
54
|
+
req.header("Content-Type") # "application/json"
|
|
55
|
+
|
|
56
|
+
resp = execute(req).response
|
|
57
|
+
print(resp.status, resp.text())
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Tout ce qui n'est pas reproductible est **signalé dans `warnings`** (jamais ignoré
|
|
61
|
+
en silence) : chaque warning a un `code` stable, un `message` et un niveau.
|
|
62
|
+
|
|
63
|
+
## Variables
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# {{nom}} partout, et $NOM / ${NOM} comme dans le shell (quotes doubles et nu)
|
|
67
|
+
parse_curl('curl -H "Authorization: Bearer $TOKEN" {{baseUrl}}/users',
|
|
68
|
+
{"baseUrl": "https://api.example.com", "TOKEN": "tok-42"})
|
|
69
|
+
|
|
70
|
+
# Dynamiques Postman générées : {{$guid}}, {{$randomUUID}}, {{$timestamp}}, {{$isoTimestamp}}, {{$randomInt}}
|
|
71
|
+
from urban_http import parse_postman_environment
|
|
72
|
+
variables = parse_postman_environment(env_json) # export d'environnement Postman
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Collections Postman
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from urban_http import parse_postman, is_postman_collection
|
|
79
|
+
|
|
80
|
+
result = parse_postman(collection_json, {"baseUrl": "https://api.example.com"})
|
|
81
|
+
for req in result.requests:
|
|
82
|
+
print(req.name, req.method, req.url)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Format Collection v2.0 / v2.1 : dossiers imbriqués, variables, tous les modes de
|
|
86
|
+
corps (raw, urlencoded, formdata, file, graphql), auth basic/bearer/apikey avec
|
|
87
|
+
héritage collection → dossier → requête.
|
|
88
|
+
|
|
89
|
+
## Autres protocoles
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from urban_http import run, register_default_adapters
|
|
93
|
+
|
|
94
|
+
register_default_adapters() # une fois
|
|
95
|
+
|
|
96
|
+
# Envoi d'un mail (smtplib), téléversement FTP (ftplib), publication MQTT (paho)...
|
|
97
|
+
result = run("curl smtp://mail.example.com --mail-from moi@x.com --mail-rcpt toi@y.com -T message.eml")
|
|
98
|
+
print(result.protocol.summary)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`run()` détecte le protocole et route : HTTP via urllib, sinon l'adaptateur
|
|
102
|
+
enregistré. Chaque adaptateur accepte un `client_factory` injectable (tests sans
|
|
103
|
+
réseau, ou autre bibliothèque). Détail : **[docs/protocols.md](docs/protocols.md)**.
|
|
104
|
+
|
|
105
|
+
## OpenAPI → curl
|
|
106
|
+
|
|
107
|
+
`openapi_to_curl(contrat)` lit un contrat OpenAPI v3.x (dict, JSON, ou YAML avec
|
|
108
|
+
l'extra `openapi`) et renvoie, pour chaque opération, une commande curl avec des
|
|
109
|
+
variables injectables, le tableau de ses paramètres, et un modèle générique du
|
|
110
|
+
corps de requête.
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from urban_http import openapi_to_curl, parse_curl
|
|
114
|
+
|
|
115
|
+
for op in openapi_to_curl(contrat):
|
|
116
|
+
print(op.method, op.path)
|
|
117
|
+
print(op.curl) # avec des {{placeholders}}
|
|
118
|
+
print(op.example_variables) # {nom: valeur} prêt pour parse_curl
|
|
119
|
+
req = parse_curl(op.curl, op.example_variables).requests[0]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Le corps est généré exhaustivement avec des valeurs types : chaîne → nom de la
|
|
123
|
+
propriété, entier → 0, date/date-time → instant courant, sous-objet développé,
|
|
124
|
+
tableau à un seul élément. Détail : **[docs/openapi.md](docs/openapi.md)**.
|
|
125
|
+
|
|
126
|
+
## Ligne de commande
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
urban-http "curl -X POST https://api.example.com -d a=1" # analyse et affiche la requête
|
|
130
|
+
urban-http "curl file:///etc/hostname" --run # analyse ET exécute
|
|
131
|
+
urban-http "curl {{baseUrl}}/get" --var baseUrl=https://httpbin.org
|
|
132
|
+
urban-http --openapi contrat.json # génère les curl d'un contrat OpenAPI
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Site de test
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
python demo/server.py # http://127.0.0.1:8000
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Une page web qui analyse en direct n'importe quelle commande curl ou collection
|
|
142
|
+
Postman (analyse faite en Python côté serveur) et exécute les requêtes HTTP. Les
|
|
143
|
+
exemples HTTP ciblent httpbin.org.
|
|
144
|
+
|
|
145
|
+
## Développement
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
python -m pytest # 199 tests : tokenizer, parser, Postman, variables,
|
|
149
|
+
# globbing, cookies, Digest, exécution, protocoles,
|
|
150
|
+
# options TLS/réseau, OpenAPI
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
La CI (GitHub Actions) lance les tests sur Python 3.9 → 3.13 et construit le
|
|
154
|
+
paquet à chaque push / PR. Une version se publie sur PyPI en poussant un tag
|
|
155
|
+
`vX.Y.Z` — voir **[RELEASING.md](RELEASING.md)**.
|
|
156
|
+
|
|
157
|
+
## Couverture de curl
|
|
158
|
+
|
|
159
|
+
- **HTTP/HTTPS** : requête, corps, multipart, globbing, redirections, retries,
|
|
160
|
+
timeout, auth Basic/Bearer/Digest, cookies (inline et fichiers Netscape),
|
|
161
|
+
variables — plus l'import Postman. Détail : [docs/curl-use-cases.md](docs/curl-use-cases.md).
|
|
162
|
+
- **Autres protocoles** : SMTP, FTP/FTPS, SFTP, SCP, POP3, IMAP, MQTT, TFTP,
|
|
163
|
+
FILE, SMB, WebSocket. Détail : [docs/protocols.md](docs/protocols.md).
|
|
164
|
+
- **Panorama complet** (ce qui est couvert et ce qui ne l'est pas) :
|
|
165
|
+
[docs/curl-coverage.md](docs/curl-coverage.md).
|
|
166
|
+
|
|
167
|
+
## Feuille de route
|
|
168
|
+
|
|
169
|
+
- Protocoles restants : LDAP, TELNET.
|
|
170
|
+
- Auth : AWS SigV4, NTLM/Negotiate, Digest `qop=auth-int`.
|
|
171
|
+
- Requêtes conditionnelles (`-z`, `--etag-compare`), reprise `-C -` automatique.
|
|
172
|
+
- Générateurs de code supplémentaires (httpx, requests).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Publier une version sur PyPI
|
|
2
|
+
|
|
3
|
+
La CI publie automatiquement sur PyPI quand on **pousse un tag de version**.
|
|
4
|
+
L'authentification utilise le **Trusted Publishing (OIDC)** de PyPI : **aucun
|
|
5
|
+
token n'est stocké** dans le dépôt.
|
|
6
|
+
|
|
7
|
+
## Configuration (une seule fois)
|
|
8
|
+
|
|
9
|
+
### 1. Déclarer le « trusted publisher » sur PyPI
|
|
10
|
+
|
|
11
|
+
Sur <https://pypi.org>, projet `urban-http` → *Settings* → *Publishing* (ou, avant
|
|
12
|
+
le tout premier envoi, <https://pypi.org/manage/account/publishing/> → *pending
|
|
13
|
+
publisher*), ajouter un éditeur GitHub Actions avec :
|
|
14
|
+
|
|
15
|
+
| Champ | Valeur |
|
|
16
|
+
|---|---|
|
|
17
|
+
| PyPI Project Name | `urban-http` |
|
|
18
|
+
| Owner | `gaelgael5` |
|
|
19
|
+
| Repository name | `urban-http` |
|
|
20
|
+
| Workflow name | `publish.yml` |
|
|
21
|
+
| Environment name | `pypi` |
|
|
22
|
+
|
|
23
|
+
(Pour TestPyPI, répéter la même chose sur <https://test.pypi.org> avec
|
|
24
|
+
l'environment `testpypi`.)
|
|
25
|
+
|
|
26
|
+
### 2. Créer les environnements GitHub
|
|
27
|
+
|
|
28
|
+
Dans le dépôt → *Settings* → *Environments*, créer `pypi` (et `testpypi` si
|
|
29
|
+
souhaité). On peut y ajouter une règle de protection (révision obligatoire avant
|
|
30
|
+
publication).
|
|
31
|
+
|
|
32
|
+
## Publier une version
|
|
33
|
+
|
|
34
|
+
1. Mettre à jour la version dans `pyproject.toml` (`project.version`).
|
|
35
|
+
2. Commiter et pousser sur `main`.
|
|
36
|
+
3. Créer et pousser le tag correspondant :
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
git tag v0.2.0
|
|
40
|
+
git push origin v0.2.0
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Le workflow `publish.yml` vérifie que le tag (`v0.2.0`) correspond bien à
|
|
44
|
+
`project.version` (`0.2.0`), construit le sdist + wheel, les passe à
|
|
45
|
+
`twine check`, puis publie sur PyPI.
|
|
46
|
+
|
|
47
|
+
### Tester d'abord sur TestPyPI
|
|
48
|
+
|
|
49
|
+
Lancer manuellement le workflow *Publier sur PyPI* (onglet Actions →
|
|
50
|
+
*Run workflow*) en choisissant la cible `testpypi`.
|
|
51
|
+
|
|
52
|
+
## Repli : token d'API
|
|
53
|
+
|
|
54
|
+
Si le Trusted Publishing n'est pas souhaité, créer un token sur PyPI, l'ajouter
|
|
55
|
+
en secret `PYPI_API_TOKEN`, et passer le mot de passe à l'action dans
|
|
56
|
+
`publish.yml` :
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
60
|
+
with:
|
|
61
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
62
|
+
```
|