asgi-cli 0.4.2__tar.gz → 0.4.4__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.
- asgi_cli-0.4.4/PKG-INFO +149 -0
- asgi_cli-0.4.4/pyproject.toml +55 -0
- asgi_cli-0.4.4/src/asgi_cli/__init__.py +1 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/main.py +2 -2
- asgi_cli-0.4.4/src/asgi_cli/tests/__init__.py +0 -0
- asgi_cli-0.4.4/src/asgi_cli/tests/test_formatter.py +28 -0
- asgi_cli-0.4.4/src/asgi_cli/tests/test_loader.py +8 -0
- asgi_cli-0.4.4/src/asgi_cli/tests/test_main.py +35 -0
- asgi_cli-0.4.4/src/asgi_cli/tests/test_parser.py +24 -0
- asgi_cli-0.4.4/src/asgi_cli.egg-info/PKG-INFO +149 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli.egg-info/SOURCES.txt +7 -2
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli.egg-info/entry_points.txt +0 -1
- asgi-cli-0.4.2/PKG-INFO +0 -148
- asgi-cli-0.4.2/setup.py +0 -54
- asgi-cli-0.4.2/src/asgi_cli/__init__.py +0 -1
- asgi-cli-0.4.2/src/asgi_cli.egg-info/PKG-INFO +0 -148
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/LICENSE +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/MANIFEST.in +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/README.md +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/setup.cfg +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/builder.py +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/executor.py +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/formatter.py +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/loader.py +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/parser.py +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/py.typed +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/timing.py +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli/typing.py +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli.egg-info/dependency_links.txt +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli.egg-info/top_level.txt +0 -0
- {asgi-cli-0.4.2 → asgi_cli-0.4.4}/src/asgi_cli.egg-info/zip-safe +0 -0
asgi_cli-0.4.4/PKG-INFO
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: asgi-cli
|
|
3
|
+
Version: 0.4.4
|
|
4
|
+
Summary: Call ASGI Python application from command line, just like CURL
|
|
5
|
+
Author-email: Andriy Kornatskyy <andriy.kornatskyy@live.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/akornatskyy/asgi-cli
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Environment :: Web Environment
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# ASGI CLI
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
[](https://coveralls.io/github/akornatskyy/asgi-cli?branch=master)
|
|
36
|
+
[](https://badge.fury.io/py/asgi-cli)
|
|
37
|
+
|
|
38
|
+
Call [ASGI](https://asgi.readthedocs.io/en/latest/index.html)
|
|
39
|
+
Python application from command line, just like CURL.
|
|
40
|
+
|
|
41
|
+
If you’re using this tool, **★Star** this repository to show your interest, please!
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
pip install -U asgi-cli
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
asgi-cli --help
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
usage: asgi-cli [-h] [-V] [-X COMMAND] [-H HEADER] [-d DATA | -F MULTIPART]
|
|
57
|
+
[-I] [-b] [-p] [-n NUMBER] [-v]
|
|
58
|
+
app [url]
|
|
59
|
+
|
|
60
|
+
positional arguments:
|
|
61
|
+
app an application module
|
|
62
|
+
url a uniform resource locator or path (default /)
|
|
63
|
+
|
|
64
|
+
optional arguments:
|
|
65
|
+
-h, --help show this help message and exit
|
|
66
|
+
-V, --version show program's version number and exit
|
|
67
|
+
-X COMMAND, --request COMMAND
|
|
68
|
+
specify request command to use, e.g. POST (default
|
|
69
|
+
GET)
|
|
70
|
+
-H HEADER, --header HEADER
|
|
71
|
+
pass custom header line, e.g. -H='Accept:
|
|
72
|
+
application/json'
|
|
73
|
+
-d DATA, --data DATA request body data, e.g. '{"msg":"hello"}', 'msg=hello'
|
|
74
|
+
-F MULTIPART, --form MULTIPART
|
|
75
|
+
specify HTTP multipart POST data, e.g. name=value or
|
|
76
|
+
name=@file
|
|
77
|
+
-I, --head show status and headers only
|
|
78
|
+
-b, --benchmark issue a number of requests through repeated iterations
|
|
79
|
+
(reports throughtput and average call time)
|
|
80
|
+
-p, --profile prints out a report of top 10 functions ordered by
|
|
81
|
+
internal time, saves to 'stats.cprof' file
|
|
82
|
+
-n NUMBER a number of requests to issue (default 100K)
|
|
83
|
+
-v, --verbose make the operation more talkative
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Examples
|
|
87
|
+
|
|
88
|
+
_example.py_:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
START = {
|
|
92
|
+
"type": "http.response.start",
|
|
93
|
+
"status": 200,
|
|
94
|
+
"headers": [
|
|
95
|
+
(b"content-length", b"13"),
|
|
96
|
+
(b"content-type", b"text/html; charset=utf-8"),
|
|
97
|
+
],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
BODY1 = {"type": "http.response.body", "body": b"Hello"}
|
|
101
|
+
BODY2 = {"type": "http.response.body", "body": b", world!"}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def app(scope, receive, send) -> None:
|
|
105
|
+
await send(START)
|
|
106
|
+
await send(BODY1)
|
|
107
|
+
await send(BODY2)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then run the examples:
|
|
111
|
+
|
|
112
|
+
`asgi-cli example:app` prints response body:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
Hello, world!
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`asgi-cli -v example:app` pretty prints scope and sent messages:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
{'scope': {'asgi': {'spec_version': '2.1', 'version': '3.0'},
|
|
122
|
+
'client': ('127.0.0.1', 49327),
|
|
123
|
+
'headers': [(b'accept', b'*/*'),
|
|
124
|
+
(b'user-agent', b'asgi-cli/0.0.1'),
|
|
125
|
+
(b'host', b'127.0.0.1:8000')],
|
|
126
|
+
'http_version': '1.1',
|
|
127
|
+
'method': 'GET',
|
|
128
|
+
'path': '/',
|
|
129
|
+
'query_string': b'',
|
|
130
|
+
'raw_path': b'/',
|
|
131
|
+
'root_path': '',
|
|
132
|
+
'scheme': 'http',
|
|
133
|
+
'server': ('127.0.0.1', 8000),
|
|
134
|
+
'type': 'http'}}
|
|
135
|
+
{'message': {'headers': [(b'content-length', b'13'),
|
|
136
|
+
(b'content-type', b'text/html; charset=utf-8')],
|
|
137
|
+
'status': 200,
|
|
138
|
+
'type': 'http.response.start'}}
|
|
139
|
+
{'message': {'body': b'Hello', 'type': 'http.response.body'}}
|
|
140
|
+
{'message': {'body': b', world!', 'type': 'http.response.body'}}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`asgi-cli -b example:app` shows execution stats (runs in 3 iterations, for each iteration displays requests per second and an average call time):
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
#1 => 477.74K, 2.09μs
|
|
147
|
+
#2 => 438.12K, 2.28μs
|
|
148
|
+
#3 => 446.90K, 2.24μs
|
|
149
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "asgi-cli"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Call ASGI Python application from command line, just like CURL"
|
|
9
|
+
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Andriy Kornatskyy", email = "andriy.kornatskyy@live.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = []
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Environment :: Web Environment",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Natural Language :: English",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Programming Language :: Python :: 3.14",
|
|
30
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
31
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
32
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
33
|
+
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
35
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
36
|
+
]
|
|
37
|
+
dependencies = []
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/akornatskyy/asgi-cli"
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
asgi-cli = "asgi_cli.main:main"
|
|
44
|
+
|
|
45
|
+
[tool.setuptools]
|
|
46
|
+
zip-safe = true
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.dynamic]
|
|
49
|
+
version = {attr = "asgi_cli.__version__"}
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["src"]
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.package-data]
|
|
55
|
+
asgi_cli = ["py.typed"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.4"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
|
-
import typing
|
|
5
4
|
import traceback
|
|
5
|
+
import typing
|
|
6
6
|
|
|
7
7
|
from asgi_cli import builder, formatter, loader, parser
|
|
8
8
|
from asgi_cli.executor import Executor
|
|
@@ -42,7 +42,7 @@ def main(args: typing.Optional[typing.List[str]] = None) -> int:
|
|
|
42
42
|
if args is None: # pragma: nocover
|
|
43
43
|
args = sys.argv[1:]
|
|
44
44
|
sys.path.extend([os.path.abspath(".")])
|
|
45
|
-
return asyncio.
|
|
45
|
+
return asyncio.run(go(args))
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
if __name__ == "__main__": # pragma: nocover
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from asgi_cli.formatter import format_number
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FormatterTestCase(unittest.TestCase):
|
|
7
|
+
def test_format_number(self) -> None:
|
|
8
|
+
for number, expected in [
|
|
9
|
+
(12345678901, "12,345.68M"),
|
|
10
|
+
(1234567890, "1,234.57M"),
|
|
11
|
+
(123456789.1, "123.46M"),
|
|
12
|
+
(12345678.9, "12.35M"),
|
|
13
|
+
(1234567.89, "1.23M"),
|
|
14
|
+
(123456.789, "123.46K"),
|
|
15
|
+
(12345.6789, "12.35K"),
|
|
16
|
+
(1234.56789, "1.23K"),
|
|
17
|
+
(123.456789, "123.46"),
|
|
18
|
+
(12.3456789, "12.35"),
|
|
19
|
+
(1.23456789, "1.23"),
|
|
20
|
+
(0.12345678, "123.46m"),
|
|
21
|
+
(0.01234567, "12.35m"),
|
|
22
|
+
(0.00123456, "1.23m"),
|
|
23
|
+
(0.00012345, "123.45u"),
|
|
24
|
+
(0.00001234, "12.34u"),
|
|
25
|
+
(0.00000123, "1.23u"),
|
|
26
|
+
(-1234.56789, "-1,234.57"),
|
|
27
|
+
]:
|
|
28
|
+
self.assertEqual(format_number(number), expected)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from asgi_cli.main import main
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MainTestCase(unittest.TestCase):
|
|
7
|
+
def test_main(self) -> None:
|
|
8
|
+
main(["unknown:app"])
|
|
9
|
+
main(["example:app"])
|
|
10
|
+
main(["-v", "example:app"])
|
|
11
|
+
main(["-d", "", "example:app"])
|
|
12
|
+
main(["-d", "{}", "example:app"])
|
|
13
|
+
main(["example:app", "/receiver"])
|
|
14
|
+
main(["-F", "x=123", "example:app", "/receiver"])
|
|
15
|
+
main(["-F", "fn=@README.md", "example:app", "/receiver"])
|
|
16
|
+
main(["-X", "POST", "-v", "example:app"])
|
|
17
|
+
main(["-X", "POST", "-d", "msg=hello", "example:app"])
|
|
18
|
+
main(["-I", "example:app"])
|
|
19
|
+
main(["-b", "-n", "1K", "example:app"])
|
|
20
|
+
main(["-p", "-n", "1K", "example:app"])
|
|
21
|
+
main(["-H", "Accept: application/json", "example:app"])
|
|
22
|
+
main(["-v", "example:app", "http://example.com/uk/welcome"])
|
|
23
|
+
main(
|
|
24
|
+
[
|
|
25
|
+
"-v",
|
|
26
|
+
"-X",
|
|
27
|
+
"PATCH",
|
|
28
|
+
"-H",
|
|
29
|
+
"Content-Type: application/json",
|
|
30
|
+
"-d",
|
|
31
|
+
"[]",
|
|
32
|
+
"example:app",
|
|
33
|
+
"welcome",
|
|
34
|
+
]
|
|
35
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from asgi_cli.parser import parse_number
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ParserTestCase(unittest.TestCase):
|
|
7
|
+
def test_parse_number(self) -> None:
|
|
8
|
+
for s, expected in [
|
|
9
|
+
("12345", 12345),
|
|
10
|
+
("12345678901", 12345678901),
|
|
11
|
+
("1K", 1e3),
|
|
12
|
+
("25K", 25e3),
|
|
13
|
+
("1M", 1e6),
|
|
14
|
+
("10M", 1e7),
|
|
15
|
+
]:
|
|
16
|
+
self.assertEqual(parse_number(s), expected)
|
|
17
|
+
|
|
18
|
+
for s in [
|
|
19
|
+
"1KK",
|
|
20
|
+
"1G",
|
|
21
|
+
"K",
|
|
22
|
+
"M",
|
|
23
|
+
]:
|
|
24
|
+
self.assertRaises(ValueError, lambda s: parse_number(s), s)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: asgi-cli
|
|
3
|
+
Version: 0.4.4
|
|
4
|
+
Summary: Call ASGI Python application from command line, just like CURL
|
|
5
|
+
Author-email: Andriy Kornatskyy <andriy.kornatskyy@live.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/akornatskyy/asgi-cli
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Environment :: Web Environment
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# ASGI CLI
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
[](https://coveralls.io/github/akornatskyy/asgi-cli?branch=master)
|
|
36
|
+
[](https://badge.fury.io/py/asgi-cli)
|
|
37
|
+
|
|
38
|
+
Call [ASGI](https://asgi.readthedocs.io/en/latest/index.html)
|
|
39
|
+
Python application from command line, just like CURL.
|
|
40
|
+
|
|
41
|
+
If you’re using this tool, **★Star** this repository to show your interest, please!
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
pip install -U asgi-cli
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
asgi-cli --help
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
usage: asgi-cli [-h] [-V] [-X COMMAND] [-H HEADER] [-d DATA | -F MULTIPART]
|
|
57
|
+
[-I] [-b] [-p] [-n NUMBER] [-v]
|
|
58
|
+
app [url]
|
|
59
|
+
|
|
60
|
+
positional arguments:
|
|
61
|
+
app an application module
|
|
62
|
+
url a uniform resource locator or path (default /)
|
|
63
|
+
|
|
64
|
+
optional arguments:
|
|
65
|
+
-h, --help show this help message and exit
|
|
66
|
+
-V, --version show program's version number and exit
|
|
67
|
+
-X COMMAND, --request COMMAND
|
|
68
|
+
specify request command to use, e.g. POST (default
|
|
69
|
+
GET)
|
|
70
|
+
-H HEADER, --header HEADER
|
|
71
|
+
pass custom header line, e.g. -H='Accept:
|
|
72
|
+
application/json'
|
|
73
|
+
-d DATA, --data DATA request body data, e.g. '{"msg":"hello"}', 'msg=hello'
|
|
74
|
+
-F MULTIPART, --form MULTIPART
|
|
75
|
+
specify HTTP multipart POST data, e.g. name=value or
|
|
76
|
+
name=@file
|
|
77
|
+
-I, --head show status and headers only
|
|
78
|
+
-b, --benchmark issue a number of requests through repeated iterations
|
|
79
|
+
(reports throughtput and average call time)
|
|
80
|
+
-p, --profile prints out a report of top 10 functions ordered by
|
|
81
|
+
internal time, saves to 'stats.cprof' file
|
|
82
|
+
-n NUMBER a number of requests to issue (default 100K)
|
|
83
|
+
-v, --verbose make the operation more talkative
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Examples
|
|
87
|
+
|
|
88
|
+
_example.py_:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
START = {
|
|
92
|
+
"type": "http.response.start",
|
|
93
|
+
"status": 200,
|
|
94
|
+
"headers": [
|
|
95
|
+
(b"content-length", b"13"),
|
|
96
|
+
(b"content-type", b"text/html; charset=utf-8"),
|
|
97
|
+
],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
BODY1 = {"type": "http.response.body", "body": b"Hello"}
|
|
101
|
+
BODY2 = {"type": "http.response.body", "body": b", world!"}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async def app(scope, receive, send) -> None:
|
|
105
|
+
await send(START)
|
|
106
|
+
await send(BODY1)
|
|
107
|
+
await send(BODY2)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then run the examples:
|
|
111
|
+
|
|
112
|
+
`asgi-cli example:app` prints response body:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
Hello, world!
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`asgi-cli -v example:app` pretty prints scope and sent messages:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
{'scope': {'asgi': {'spec_version': '2.1', 'version': '3.0'},
|
|
122
|
+
'client': ('127.0.0.1', 49327),
|
|
123
|
+
'headers': [(b'accept', b'*/*'),
|
|
124
|
+
(b'user-agent', b'asgi-cli/0.0.1'),
|
|
125
|
+
(b'host', b'127.0.0.1:8000')],
|
|
126
|
+
'http_version': '1.1',
|
|
127
|
+
'method': 'GET',
|
|
128
|
+
'path': '/',
|
|
129
|
+
'query_string': b'',
|
|
130
|
+
'raw_path': b'/',
|
|
131
|
+
'root_path': '',
|
|
132
|
+
'scheme': 'http',
|
|
133
|
+
'server': ('127.0.0.1', 8000),
|
|
134
|
+
'type': 'http'}}
|
|
135
|
+
{'message': {'headers': [(b'content-length', b'13'),
|
|
136
|
+
(b'content-type', b'text/html; charset=utf-8')],
|
|
137
|
+
'status': 200,
|
|
138
|
+
'type': 'http.response.start'}}
|
|
139
|
+
{'message': {'body': b'Hello', 'type': 'http.response.body'}}
|
|
140
|
+
{'message': {'body': b', world!', 'type': 'http.response.body'}}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`asgi-cli -b example:app` shows execution stats (runs in 3 iterations, for each iteration displays requests per second and an average call time):
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
#1 => 477.74K, 2.09μs
|
|
147
|
+
#2 => 438.12K, 2.28μs
|
|
148
|
+
#3 => 446.90K, 2.24μs
|
|
149
|
+
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.md
|
|
4
|
-
|
|
4
|
+
pyproject.toml
|
|
5
5
|
src/asgi_cli/__init__.py
|
|
6
6
|
src/asgi_cli/builder.py
|
|
7
7
|
src/asgi_cli/executor.py
|
|
@@ -17,4 +17,9 @@ src/asgi_cli.egg-info/SOURCES.txt
|
|
|
17
17
|
src/asgi_cli.egg-info/dependency_links.txt
|
|
18
18
|
src/asgi_cli.egg-info/entry_points.txt
|
|
19
19
|
src/asgi_cli.egg-info/top_level.txt
|
|
20
|
-
src/asgi_cli.egg-info/zip-safe
|
|
20
|
+
src/asgi_cli.egg-info/zip-safe
|
|
21
|
+
src/asgi_cli/tests/__init__.py
|
|
22
|
+
src/asgi_cli/tests/test_formatter.py
|
|
23
|
+
src/asgi_cli/tests/test_loader.py
|
|
24
|
+
src/asgi_cli/tests/test_main.py
|
|
25
|
+
src/asgi_cli/tests/test_parser.py
|
asgi-cli-0.4.2/PKG-INFO
DELETED
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: asgi-cli
|
|
3
|
-
Version: 0.4.2
|
|
4
|
-
Summary: Call ASGI Python application from command line, just like CURL
|
|
5
|
-
Home-page: https://github.com/akornatskyy/asgi-cli
|
|
6
|
-
Author: Andriy Kornatskyy
|
|
7
|
-
Author-email: andriy.kornatskyy@live.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Description: # ASGI CLI
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
[](https://coveralls.io/github/akornatskyy/asgi-cli?branch=master)
|
|
13
|
-
[](https://badge.fury.io/py/asgi-cli)
|
|
14
|
-
|
|
15
|
-
Call [ASGI](https://asgi.readthedocs.io/en/latest/index.html)
|
|
16
|
-
Python application from command line, just like CURL.
|
|
17
|
-
|
|
18
|
-
If you’re using this tool, **★Star** this repository to show your interest, please!
|
|
19
|
-
|
|
20
|
-
## Install
|
|
21
|
-
|
|
22
|
-
```sh
|
|
23
|
-
pip install -U asgi-cli
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
|
|
28
|
-
```sh
|
|
29
|
-
asgi-cli --help
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
```text
|
|
33
|
-
usage: asgi-cli [-h] [-V] [-X COMMAND] [-H HEADER] [-d DATA | -F MULTIPART]
|
|
34
|
-
[-I] [-b] [-p] [-n NUMBER] [-v]
|
|
35
|
-
app [url]
|
|
36
|
-
|
|
37
|
-
positional arguments:
|
|
38
|
-
app an application module
|
|
39
|
-
url a uniform resource locator or path (default /)
|
|
40
|
-
|
|
41
|
-
optional arguments:
|
|
42
|
-
-h, --help show this help message and exit
|
|
43
|
-
-V, --version show program's version number and exit
|
|
44
|
-
-X COMMAND, --request COMMAND
|
|
45
|
-
specify request command to use, e.g. POST (default
|
|
46
|
-
GET)
|
|
47
|
-
-H HEADER, --header HEADER
|
|
48
|
-
pass custom header line, e.g. -H='Accept:
|
|
49
|
-
application/json'
|
|
50
|
-
-d DATA, --data DATA request body data, e.g. '{"msg":"hello"}', 'msg=hello'
|
|
51
|
-
-F MULTIPART, --form MULTIPART
|
|
52
|
-
specify HTTP multipart POST data, e.g. name=value or
|
|
53
|
-
name=@file
|
|
54
|
-
-I, --head show status and headers only
|
|
55
|
-
-b, --benchmark issue a number of requests through repeated iterations
|
|
56
|
-
(reports throughtput and average call time)
|
|
57
|
-
-p, --profile prints out a report of top 10 functions ordered by
|
|
58
|
-
internal time, saves to 'stats.cprof' file
|
|
59
|
-
-n NUMBER a number of requests to issue (default 100K)
|
|
60
|
-
-v, --verbose make the operation more talkative
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Examples
|
|
64
|
-
|
|
65
|
-
_example.py_:
|
|
66
|
-
|
|
67
|
-
```python
|
|
68
|
-
START = {
|
|
69
|
-
"type": "http.response.start",
|
|
70
|
-
"status": 200,
|
|
71
|
-
"headers": [
|
|
72
|
-
(b"content-length", b"13"),
|
|
73
|
-
(b"content-type", b"text/html; charset=utf-8"),
|
|
74
|
-
],
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
BODY1 = {"type": "http.response.body", "body": b"Hello"}
|
|
78
|
-
BODY2 = {"type": "http.response.body", "body": b", world!"}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
async def app(scope, receive, send) -> None:
|
|
82
|
-
await send(START)
|
|
83
|
-
await send(BODY1)
|
|
84
|
-
await send(BODY2)
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Then run the examples:
|
|
88
|
-
|
|
89
|
-
`asgi-cli example:app` prints response body:
|
|
90
|
-
|
|
91
|
-
```text
|
|
92
|
-
Hello, world!
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
`asgi-cli -v example:app` pretty prints scope and sent messages:
|
|
96
|
-
|
|
97
|
-
```text
|
|
98
|
-
{'scope': {'asgi': {'spec_version': '2.1', 'version': '3.0'},
|
|
99
|
-
'client': ('127.0.0.1', 49327),
|
|
100
|
-
'headers': [(b'accept', b'*/*'),
|
|
101
|
-
(b'user-agent', b'asgi-cli/0.0.1'),
|
|
102
|
-
(b'host', b'127.0.0.1:8000')],
|
|
103
|
-
'http_version': '1.1',
|
|
104
|
-
'method': 'GET',
|
|
105
|
-
'path': '/',
|
|
106
|
-
'query_string': b'',
|
|
107
|
-
'raw_path': b'/',
|
|
108
|
-
'root_path': '',
|
|
109
|
-
'scheme': 'http',
|
|
110
|
-
'server': ('127.0.0.1', 8000),
|
|
111
|
-
'type': 'http'}}
|
|
112
|
-
{'message': {'headers': [(b'content-length', b'13'),
|
|
113
|
-
(b'content-type', b'text/html; charset=utf-8')],
|
|
114
|
-
'status': 200,
|
|
115
|
-
'type': 'http.response.start'}}
|
|
116
|
-
{'message': {'body': b'Hello', 'type': 'http.response.body'}}
|
|
117
|
-
{'message': {'body': b', world!', 'type': 'http.response.body'}}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`asgi-cli -b example:app` shows execution stats (runs in 3 iterations, for each iteration displays requests per second and an average call time):
|
|
121
|
-
|
|
122
|
-
```text
|
|
123
|
-
#1 => 477.74K, 2.09μs
|
|
124
|
-
#2 => 438.12K, 2.28μs
|
|
125
|
-
#3 => 446.90K, 2.24μs
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Platform: any
|
|
129
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
130
|
-
Classifier: Environment :: Console
|
|
131
|
-
Classifier: Environment :: Web Environment
|
|
132
|
-
Classifier: Intended Audience :: Developers
|
|
133
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
134
|
-
Classifier: Natural Language :: English
|
|
135
|
-
Classifier: Operating System :: OS Independent
|
|
136
|
-
Classifier: Programming Language :: Python
|
|
137
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
138
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
139
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
140
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
141
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
142
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
143
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
144
|
-
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
145
|
-
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
146
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
147
|
-
Requires-Python: >=3.6
|
|
148
|
-
Description-Content-Type: text/markdown
|
asgi-cli-0.4.2/setup.py
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
|
|
3
|
-
from setuptools import setup
|
|
4
|
-
|
|
5
|
-
README = open("README.md").read()
|
|
6
|
-
VERSION = (
|
|
7
|
-
re.search(
|
|
8
|
-
r"__version__ = \"(.+)\"", open("src/asgi_cli/__init__.py").read()
|
|
9
|
-
)
|
|
10
|
-
.group(1)
|
|
11
|
-
.strip()
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
setup(
|
|
15
|
-
name="asgi-cli",
|
|
16
|
-
version=VERSION,
|
|
17
|
-
python_requires=">=3.6",
|
|
18
|
-
description="Call ASGI Python application from command line,"
|
|
19
|
-
" just like CURL",
|
|
20
|
-
long_description=README,
|
|
21
|
-
long_description_content_type="text/markdown",
|
|
22
|
-
url="https://github.com/akornatskyy/asgi-cli",
|
|
23
|
-
author="Andriy Kornatskyy",
|
|
24
|
-
author_email="andriy.kornatskyy@live.com",
|
|
25
|
-
license="MIT",
|
|
26
|
-
classifiers=[
|
|
27
|
-
"Development Status :: 5 - Production/Stable",
|
|
28
|
-
"Environment :: Console",
|
|
29
|
-
"Environment :: Web Environment",
|
|
30
|
-
"Intended Audience :: Developers",
|
|
31
|
-
"License :: OSI Approved :: MIT License",
|
|
32
|
-
"Natural Language :: English",
|
|
33
|
-
"Operating System :: OS Independent",
|
|
34
|
-
"Programming Language :: Python",
|
|
35
|
-
"Programming Language :: Python :: 3.6",
|
|
36
|
-
"Programming Language :: Python :: 3.7",
|
|
37
|
-
"Programming Language :: Python :: 3.8",
|
|
38
|
-
"Programming Language :: Python :: 3.9",
|
|
39
|
-
"Programming Language :: Python :: Implementation :: CPython",
|
|
40
|
-
"Programming Language :: Python :: Implementation :: PyPy",
|
|
41
|
-
"Topic :: Internet :: WWW/HTTP",
|
|
42
|
-
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
43
|
-
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
44
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
45
|
-
],
|
|
46
|
-
keywords="",
|
|
47
|
-
packages=["asgi_cli"],
|
|
48
|
-
package_data={"asgi_cli": ["py.typed"]},
|
|
49
|
-
package_dir={"": "src"},
|
|
50
|
-
zip_safe=True,
|
|
51
|
-
install_requires=[],
|
|
52
|
-
entry_points={"console_scripts": ["asgi-cli=asgi_cli.main:main"]},
|
|
53
|
-
platforms="any",
|
|
54
|
-
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.2"
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: asgi-cli
|
|
3
|
-
Version: 0.4.2
|
|
4
|
-
Summary: Call ASGI Python application from command line, just like CURL
|
|
5
|
-
Home-page: https://github.com/akornatskyy/asgi-cli
|
|
6
|
-
Author: Andriy Kornatskyy
|
|
7
|
-
Author-email: andriy.kornatskyy@live.com
|
|
8
|
-
License: MIT
|
|
9
|
-
Description: # ASGI CLI
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
[](https://coveralls.io/github/akornatskyy/asgi-cli?branch=master)
|
|
13
|
-
[](https://badge.fury.io/py/asgi-cli)
|
|
14
|
-
|
|
15
|
-
Call [ASGI](https://asgi.readthedocs.io/en/latest/index.html)
|
|
16
|
-
Python application from command line, just like CURL.
|
|
17
|
-
|
|
18
|
-
If you’re using this tool, **★Star** this repository to show your interest, please!
|
|
19
|
-
|
|
20
|
-
## Install
|
|
21
|
-
|
|
22
|
-
```sh
|
|
23
|
-
pip install -U asgi-cli
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Usage
|
|
27
|
-
|
|
28
|
-
```sh
|
|
29
|
-
asgi-cli --help
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
```text
|
|
33
|
-
usage: asgi-cli [-h] [-V] [-X COMMAND] [-H HEADER] [-d DATA | -F MULTIPART]
|
|
34
|
-
[-I] [-b] [-p] [-n NUMBER] [-v]
|
|
35
|
-
app [url]
|
|
36
|
-
|
|
37
|
-
positional arguments:
|
|
38
|
-
app an application module
|
|
39
|
-
url a uniform resource locator or path (default /)
|
|
40
|
-
|
|
41
|
-
optional arguments:
|
|
42
|
-
-h, --help show this help message and exit
|
|
43
|
-
-V, --version show program's version number and exit
|
|
44
|
-
-X COMMAND, --request COMMAND
|
|
45
|
-
specify request command to use, e.g. POST (default
|
|
46
|
-
GET)
|
|
47
|
-
-H HEADER, --header HEADER
|
|
48
|
-
pass custom header line, e.g. -H='Accept:
|
|
49
|
-
application/json'
|
|
50
|
-
-d DATA, --data DATA request body data, e.g. '{"msg":"hello"}', 'msg=hello'
|
|
51
|
-
-F MULTIPART, --form MULTIPART
|
|
52
|
-
specify HTTP multipart POST data, e.g. name=value or
|
|
53
|
-
name=@file
|
|
54
|
-
-I, --head show status and headers only
|
|
55
|
-
-b, --benchmark issue a number of requests through repeated iterations
|
|
56
|
-
(reports throughtput and average call time)
|
|
57
|
-
-p, --profile prints out a report of top 10 functions ordered by
|
|
58
|
-
internal time, saves to 'stats.cprof' file
|
|
59
|
-
-n NUMBER a number of requests to issue (default 100K)
|
|
60
|
-
-v, --verbose make the operation more talkative
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Examples
|
|
64
|
-
|
|
65
|
-
_example.py_:
|
|
66
|
-
|
|
67
|
-
```python
|
|
68
|
-
START = {
|
|
69
|
-
"type": "http.response.start",
|
|
70
|
-
"status": 200,
|
|
71
|
-
"headers": [
|
|
72
|
-
(b"content-length", b"13"),
|
|
73
|
-
(b"content-type", b"text/html; charset=utf-8"),
|
|
74
|
-
],
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
BODY1 = {"type": "http.response.body", "body": b"Hello"}
|
|
78
|
-
BODY2 = {"type": "http.response.body", "body": b", world!"}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
async def app(scope, receive, send) -> None:
|
|
82
|
-
await send(START)
|
|
83
|
-
await send(BODY1)
|
|
84
|
-
await send(BODY2)
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Then run the examples:
|
|
88
|
-
|
|
89
|
-
`asgi-cli example:app` prints response body:
|
|
90
|
-
|
|
91
|
-
```text
|
|
92
|
-
Hello, world!
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
`asgi-cli -v example:app` pretty prints scope and sent messages:
|
|
96
|
-
|
|
97
|
-
```text
|
|
98
|
-
{'scope': {'asgi': {'spec_version': '2.1', 'version': '3.0'},
|
|
99
|
-
'client': ('127.0.0.1', 49327),
|
|
100
|
-
'headers': [(b'accept', b'*/*'),
|
|
101
|
-
(b'user-agent', b'asgi-cli/0.0.1'),
|
|
102
|
-
(b'host', b'127.0.0.1:8000')],
|
|
103
|
-
'http_version': '1.1',
|
|
104
|
-
'method': 'GET',
|
|
105
|
-
'path': '/',
|
|
106
|
-
'query_string': b'',
|
|
107
|
-
'raw_path': b'/',
|
|
108
|
-
'root_path': '',
|
|
109
|
-
'scheme': 'http',
|
|
110
|
-
'server': ('127.0.0.1', 8000),
|
|
111
|
-
'type': 'http'}}
|
|
112
|
-
{'message': {'headers': [(b'content-length', b'13'),
|
|
113
|
-
(b'content-type', b'text/html; charset=utf-8')],
|
|
114
|
-
'status': 200,
|
|
115
|
-
'type': 'http.response.start'}}
|
|
116
|
-
{'message': {'body': b'Hello', 'type': 'http.response.body'}}
|
|
117
|
-
{'message': {'body': b', world!', 'type': 'http.response.body'}}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`asgi-cli -b example:app` shows execution stats (runs in 3 iterations, for each iteration displays requests per second and an average call time):
|
|
121
|
-
|
|
122
|
-
```text
|
|
123
|
-
#1 => 477.74K, 2.09μs
|
|
124
|
-
#2 => 438.12K, 2.28μs
|
|
125
|
-
#3 => 446.90K, 2.24μs
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Platform: any
|
|
129
|
-
Classifier: Development Status :: 5 - Production/Stable
|
|
130
|
-
Classifier: Environment :: Console
|
|
131
|
-
Classifier: Environment :: Web Environment
|
|
132
|
-
Classifier: Intended Audience :: Developers
|
|
133
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
134
|
-
Classifier: Natural Language :: English
|
|
135
|
-
Classifier: Operating System :: OS Independent
|
|
136
|
-
Classifier: Programming Language :: Python
|
|
137
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
138
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
139
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
140
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
141
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
142
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
143
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
144
|
-
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
145
|
-
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
146
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
147
|
-
Requires-Python: >=3.6
|
|
148
|
-
Description-Content-Type: text/markdown
|
|
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
|