restiny 0.1.1__tar.gz → 0.2.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.
Potentially problematic release.
This version of restiny might be problematic. Click here for more details.
- {restiny-0.1.1 → restiny-0.2.0}/PKG-INFO +8 -9
- {restiny-0.1.1 → restiny-0.2.0}/README.md +4 -4
- {restiny-0.1.1 → restiny-0.2.0}/pyproject.toml +5 -6
- restiny-0.2.0/restiny/__about__.py +1 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/core/app.py +5 -4
- {restiny-0.1.1 → restiny-0.2.0}/restiny/core/request_area.py +36 -33
- {restiny-0.1.1 → restiny-0.2.0}/restiny/enums.py +0 -1
- {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/__init__.py +2 -2
- restiny-0.2.0/restiny/widgets/dynamic_fields.py +540 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/path_chooser.py +36 -12
- {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/PKG-INFO +8 -9
- {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/SOURCES.txt +0 -1
- {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/requires.txt +1 -1
- restiny-0.1.1/restiny/__about__.py +0 -1
- restiny-0.1.1/restiny/assets/__pycache__/__init__.cpython-313.pyc +0 -0
- restiny-0.1.1/restiny/widgets/dynamic_fields.py +0 -287
- {restiny-0.1.1 → restiny-0.2.0}/LICENSE +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/__init__.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/__main__.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/assets/__init__.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/assets/style.tcss +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/consts.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/core/__init__.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/core/response_area.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/core/url_area.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/screens/__init__.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/screens/dialog.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/utils.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/custom_directory_tree.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/custom_text_area.py +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/dependency_links.txt +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/entry_points.txt +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/top_level.txt +0 -0
- {restiny-0.1.1 → restiny-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: restiny
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A minimalist HTTP client, no bullshit
|
|
5
5
|
Author-email: Kalebe Chimanski de Almeida <kalebe.chi.almeida@gmail.com>
|
|
6
6
|
License: Apache License
|
|
@@ -216,12 +216,11 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
216
216
|
Classifier: Environment :: Console
|
|
217
217
|
Classifier: Typing :: Typed
|
|
218
218
|
Classifier: Programming Language :: Python :: 3
|
|
219
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
220
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
221
219
|
Classifier: Programming Language :: Python :: 3.10
|
|
222
220
|
Classifier: Programming Language :: Python :: 3.11
|
|
223
221
|
Classifier: Programming Language :: Python :: 3.12
|
|
224
222
|
Classifier: Programming Language :: Python :: 3.13
|
|
223
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
225
224
|
Classifier: Operating System :: POSIX :: Linux
|
|
226
225
|
Classifier: Operating System :: MacOS
|
|
227
226
|
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
@@ -229,20 +228,20 @@ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
|
|
|
229
228
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
230
229
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
231
230
|
Classifier: Natural Language :: English
|
|
232
|
-
Requires-Python: >=3.
|
|
231
|
+
Requires-Python: >=3.10
|
|
233
232
|
Description-Content-Type: text/markdown
|
|
234
233
|
License-File: LICENSE
|
|
235
|
-
Requires-Dist: textual<6.
|
|
234
|
+
Requires-Dist: textual<6.4,>=6.3
|
|
236
235
|
Requires-Dist: textual[syntax]
|
|
237
236
|
Requires-Dist: httpx<0.29,>=0.28
|
|
238
237
|
Requires-Dist: pyperclip<1.10,>=1.9
|
|
239
238
|
Dynamic: license-file
|
|
240
239
|
|
|
241
240
|

|
|
242
|
-

|
|
243
242
|
|
|
244
243
|
|
|
245
|
-
- [
|
|
244
|
+
- [RESTiny](#restiny)
|
|
246
245
|
- [How to install](#how-to-install)
|
|
247
246
|
- [How to install (Alternative: Download pre-built executables)](#how-to-install-alternative-download-pre-built-executables)
|
|
248
247
|
- [How to run](#how-to-run)
|
|
@@ -251,11 +250,11 @@ Dynamic: license-file
|
|
|
251
250
|
- [Why??](#why)
|
|
252
251
|
|
|
253
252
|
|
|
254
|
-
#
|
|
253
|
+
# RESTiny
|
|
255
254
|
|
|
256
255
|
_A minimal, elegant HTTP client for Python — with a TUI interface powered by [Textual](https://github.com/Textualize/textual)._
|
|
257
256
|
|
|
258
|
-
|
|
257
|
+

|
|
259
258
|
|
|
260
259
|
## How to install
|
|
261
260
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|

|
|
2
|
-

|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
- [
|
|
5
|
+
- [RESTiny](#restiny)
|
|
6
6
|
- [How to install](#how-to-install)
|
|
7
7
|
- [How to install (Alternative: Download pre-built executables)](#how-to-install-alternative-download-pre-built-executables)
|
|
8
8
|
- [How to run](#how-to-run)
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
- [Why??](#why)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
#
|
|
14
|
+
# RESTiny
|
|
15
15
|
|
|
16
16
|
_A minimal, elegant HTTP client for Python — with a TUI interface powered by [Textual](https://github.com/Textualize/textual)._
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+

|
|
19
19
|
|
|
20
20
|
## How to install
|
|
21
21
|
|
|
@@ -12,9 +12,9 @@ description = "A minimalist HTTP client, no bullshit"
|
|
|
12
12
|
authors = [{ name = "Kalebe Chimanski de Almeida", email = "kalebe.chi.almeida@gmail.com" }]
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
license = { file = "LICENSE" }
|
|
15
|
-
requires-python = ">=3.
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
16
|
dependencies = [
|
|
17
|
-
"textual>=6.
|
|
17
|
+
"textual>=6.3,<6.4",
|
|
18
18
|
"textual[syntax]",
|
|
19
19
|
"httpx>=0.28,<0.29",
|
|
20
20
|
"pyperclip>=1.9,<1.10",
|
|
@@ -27,12 +27,11 @@ classifiers = [
|
|
|
27
27
|
"Environment :: Console",
|
|
28
28
|
"Typing :: Typed",
|
|
29
29
|
"Programming Language :: Python :: 3",
|
|
30
|
-
"Programming Language :: Python :: 3.8",
|
|
31
|
-
"Programming Language :: Python :: 3.9",
|
|
32
30
|
"Programming Language :: Python :: 3.10",
|
|
33
31
|
"Programming Language :: Python :: 3.11",
|
|
34
32
|
"Programming Language :: Python :: 3.12",
|
|
35
33
|
"Programming Language :: Python :: 3.13",
|
|
34
|
+
"Programming Language :: Python :: 3.14",
|
|
36
35
|
"Operating System :: POSIX :: Linux",
|
|
37
36
|
"Operating System :: MacOS",
|
|
38
37
|
"Operating System :: Microsoft :: Windows :: Windows 10",
|
|
@@ -65,11 +64,11 @@ restiny = ["assets/**/*"]
|
|
|
65
64
|
##########
|
|
66
65
|
[tool.ruff]
|
|
67
66
|
line-length = 79
|
|
68
|
-
target-version = "
|
|
67
|
+
target-version = "py314"
|
|
69
68
|
|
|
70
69
|
[tool.ruff.lint]
|
|
71
70
|
select = ["E", "F", "B", "I", "UP"]
|
|
72
|
-
ignore = ["E501", "B006"]
|
|
71
|
+
ignore = ["E501", "B006", "UP037"]
|
|
73
72
|
|
|
74
73
|
[tool.ruff.format]
|
|
75
74
|
quote-style = "single"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.0'
|
|
@@ -237,10 +237,11 @@ class RESTinyApp(App, inherit_bindings=False):
|
|
|
237
237
|
)
|
|
238
238
|
elif request_area_data.body.type == BodyMode.FILE:
|
|
239
239
|
file = request_area_data.body.payload
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
if 'content-type' not in headers:
|
|
241
|
+
headers['content-type'] = (
|
|
242
|
+
mimetypes.guess_type(file.name)[0]
|
|
243
|
+
or 'application/octet-stream'
|
|
244
|
+
)
|
|
244
245
|
request = http_client.build_request(
|
|
245
246
|
method=url_area_data.method,
|
|
246
247
|
url=url_area_data.url,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import mimetypes
|
|
2
1
|
from dataclasses import dataclass
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
|
|
@@ -19,10 +18,11 @@ from textual.widgets import (
|
|
|
19
18
|
from restiny.enums import BodyMode, BodyRawLanguage
|
|
20
19
|
from restiny.widgets import (
|
|
21
20
|
CustomTextArea,
|
|
22
|
-
DynamicField,
|
|
23
21
|
DynamicFields,
|
|
24
22
|
PathChooser,
|
|
23
|
+
TextDynamicField,
|
|
25
24
|
)
|
|
25
|
+
from restiny.widgets.dynamic_fields import TextOrFileDynamicField
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
@dataclass
|
|
@@ -98,12 +98,12 @@ class RequestArea(Static):
|
|
|
98
98
|
with TabbedContent():
|
|
99
99
|
with TabPane('Headers'):
|
|
100
100
|
yield DynamicFields(
|
|
101
|
-
fields=[
|
|
101
|
+
fields=[TextDynamicField(enabled=False, key='', value='')],
|
|
102
102
|
id='headers',
|
|
103
103
|
)
|
|
104
104
|
with TabPane('Query params'):
|
|
105
105
|
yield DynamicFields(
|
|
106
|
-
fields=[
|
|
106
|
+
fields=[TextDynamicField(enabled=False, key='', value='')],
|
|
107
107
|
id='params',
|
|
108
108
|
)
|
|
109
109
|
with TabPane('Body'):
|
|
@@ -114,7 +114,7 @@ class RequestArea(Static):
|
|
|
114
114
|
('Raw', BodyMode.RAW),
|
|
115
115
|
('File', BodyMode.FILE),
|
|
116
116
|
('Form (urlencoded)', BodyMode.FORM_URLENCODED),
|
|
117
|
-
|
|
117
|
+
('Form (multipart)', BodyMode.FORM_MULTIPART),
|
|
118
118
|
),
|
|
119
119
|
allow_blank=False,
|
|
120
120
|
tooltip='Body type',
|
|
@@ -149,9 +149,24 @@ class RequestArea(Static):
|
|
|
149
149
|
id='body-mode-form-urlencoded', classes='h-auto mt-1'
|
|
150
150
|
):
|
|
151
151
|
yield DynamicFields(
|
|
152
|
-
[
|
|
152
|
+
[
|
|
153
|
+
TextDynamicField(
|
|
154
|
+
enabled=False, key='', value=''
|
|
155
|
+
)
|
|
156
|
+
],
|
|
153
157
|
id='body-form-urlencoded',
|
|
154
158
|
)
|
|
159
|
+
with Horizontal(
|
|
160
|
+
id='body-mode-form-multipart', classes='h-auto mt-1'
|
|
161
|
+
):
|
|
162
|
+
yield DynamicFields(
|
|
163
|
+
[
|
|
164
|
+
TextOrFileDynamicField(
|
|
165
|
+
enabled=False, key='', value=''
|
|
166
|
+
)
|
|
167
|
+
],
|
|
168
|
+
id='body-form-multipart',
|
|
169
|
+
)
|
|
155
170
|
|
|
156
171
|
with TabPane('Options'):
|
|
157
172
|
with Horizontal(classes='h-auto'):
|
|
@@ -187,6 +202,9 @@ class RequestArea(Static):
|
|
|
187
202
|
self.body_form_urlencoded_fields = self.query_one(
|
|
188
203
|
'#body-form-urlencoded', DynamicFields
|
|
189
204
|
)
|
|
205
|
+
self.body_form_multipart_fields = self.query_one(
|
|
206
|
+
'#body-form-multipart', DynamicFields
|
|
207
|
+
)
|
|
190
208
|
|
|
191
209
|
self.options_timeout_input = self.query_one('#options-timeout', Input)
|
|
192
210
|
self.options_follow_redirects_switch = self.query_one(
|
|
@@ -204,6 +222,8 @@ class RequestArea(Static):
|
|
|
204
222
|
self.body_mode_switcher.current = 'body-mode-raw'
|
|
205
223
|
elif message.value == BodyMode.FORM_URLENCODED:
|
|
206
224
|
self.body_mode_switcher.current = 'body-mode-form-urlencoded'
|
|
225
|
+
elif message.value == BodyMode.FORM_MULTIPART:
|
|
226
|
+
self.body_mode_switcher.current = 'body-mode-form-multipart'
|
|
207
227
|
|
|
208
228
|
@on(Select.Changed, '#body-raw-language')
|
|
209
229
|
def on_change_body_text_language(self, message: Select.Changed) -> None:
|
|
@@ -225,30 +245,6 @@ class RequestArea(Static):
|
|
|
225
245
|
else:
|
|
226
246
|
self.body_enabled_switch.value = True
|
|
227
247
|
|
|
228
|
-
@on(PathChooser.Changed)
|
|
229
|
-
async def on_change_file(self, message: PathChooser.Changed) -> None:
|
|
230
|
-
content_type_header_field: DynamicField | None = None
|
|
231
|
-
for header_field in self.header_fields.fields:
|
|
232
|
-
if header_field.key.lower() == 'content-type':
|
|
233
|
-
content_type_header_field = header_field
|
|
234
|
-
break
|
|
235
|
-
|
|
236
|
-
content_type: str | None = mimetypes.guess_type(str(message.path))[0]
|
|
237
|
-
if not content_type:
|
|
238
|
-
return
|
|
239
|
-
|
|
240
|
-
if content_type_header_field:
|
|
241
|
-
content_type_header_field.value = content_type
|
|
242
|
-
return
|
|
243
|
-
|
|
244
|
-
empty_field = self.header_fields.empty_fields[0]
|
|
245
|
-
empty_field.enabled = True
|
|
246
|
-
empty_field.key = 'Content-Type'
|
|
247
|
-
empty_field.value = content_type
|
|
248
|
-
await self.header_fields.add_field(
|
|
249
|
-
field=DynamicField(enabled=False, key='', value='')
|
|
250
|
-
)
|
|
251
|
-
|
|
252
248
|
@on(Input.Changed, '#options-timeout')
|
|
253
249
|
def on_change_timeout(self, message: Input.Changed) -> None:
|
|
254
250
|
new_value = message.value
|
|
@@ -303,6 +299,16 @@ class RequestArea(Static):
|
|
|
303
299
|
value=form_item['value'],
|
|
304
300
|
)
|
|
305
301
|
)
|
|
302
|
+
elif body_type == BodyMode.FORM_MULTIPART:
|
|
303
|
+
payload = []
|
|
304
|
+
for form_item in self.body_form_multipart_fields.values:
|
|
305
|
+
payload.append(
|
|
306
|
+
FormMultipartField(
|
|
307
|
+
enabled=form_item['enabled'],
|
|
308
|
+
key=form_item['key'],
|
|
309
|
+
value=form_item['value'],
|
|
310
|
+
)
|
|
311
|
+
)
|
|
306
312
|
|
|
307
313
|
return RequestAreaData.Body(
|
|
308
314
|
enabled=body_send,
|
|
@@ -330,6 +336,3 @@ class RequestArea(Static):
|
|
|
330
336
|
body=get_body(),
|
|
331
337
|
options=get_options(),
|
|
332
338
|
)
|
|
333
|
-
|
|
334
|
-
def set_content_type() -> None:
|
|
335
|
-
raise NotImplementedError()
|
|
@@ -4,11 +4,11 @@ This module contains reusable widgets used in the DataFox interface.
|
|
|
4
4
|
|
|
5
5
|
from restiny.widgets.custom_directory_tree import CustomDirectoryTree
|
|
6
6
|
from restiny.widgets.custom_text_area import CustomTextArea
|
|
7
|
-
from restiny.widgets.dynamic_fields import
|
|
7
|
+
from restiny.widgets.dynamic_fields import DynamicFields, TextDynamicField
|
|
8
8
|
from restiny.widgets.path_chooser import PathChooser
|
|
9
9
|
|
|
10
10
|
__all__ = [
|
|
11
|
-
'
|
|
11
|
+
'TextDynamicField',
|
|
12
12
|
'DynamicFields',
|
|
13
13
|
'CustomDirectoryTree',
|
|
14
14
|
'CustomTextArea',
|