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.

Files changed (34) hide show
  1. {restiny-0.1.1 → restiny-0.2.0}/PKG-INFO +8 -9
  2. {restiny-0.1.1 → restiny-0.2.0}/README.md +4 -4
  3. {restiny-0.1.1 → restiny-0.2.0}/pyproject.toml +5 -6
  4. restiny-0.2.0/restiny/__about__.py +1 -0
  5. {restiny-0.1.1 → restiny-0.2.0}/restiny/core/app.py +5 -4
  6. {restiny-0.1.1 → restiny-0.2.0}/restiny/core/request_area.py +36 -33
  7. {restiny-0.1.1 → restiny-0.2.0}/restiny/enums.py +0 -1
  8. {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/__init__.py +2 -2
  9. restiny-0.2.0/restiny/widgets/dynamic_fields.py +540 -0
  10. {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/path_chooser.py +36 -12
  11. {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/PKG-INFO +8 -9
  12. {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/SOURCES.txt +0 -1
  13. {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/requires.txt +1 -1
  14. restiny-0.1.1/restiny/__about__.py +0 -1
  15. restiny-0.1.1/restiny/assets/__pycache__/__init__.cpython-313.pyc +0 -0
  16. restiny-0.1.1/restiny/widgets/dynamic_fields.py +0 -287
  17. {restiny-0.1.1 → restiny-0.2.0}/LICENSE +0 -0
  18. {restiny-0.1.1 → restiny-0.2.0}/restiny/__init__.py +0 -0
  19. {restiny-0.1.1 → restiny-0.2.0}/restiny/__main__.py +0 -0
  20. {restiny-0.1.1 → restiny-0.2.0}/restiny/assets/__init__.py +0 -0
  21. {restiny-0.1.1 → restiny-0.2.0}/restiny/assets/style.tcss +0 -0
  22. {restiny-0.1.1 → restiny-0.2.0}/restiny/consts.py +0 -0
  23. {restiny-0.1.1 → restiny-0.2.0}/restiny/core/__init__.py +0 -0
  24. {restiny-0.1.1 → restiny-0.2.0}/restiny/core/response_area.py +0 -0
  25. {restiny-0.1.1 → restiny-0.2.0}/restiny/core/url_area.py +0 -0
  26. {restiny-0.1.1 → restiny-0.2.0}/restiny/screens/__init__.py +0 -0
  27. {restiny-0.1.1 → restiny-0.2.0}/restiny/screens/dialog.py +0 -0
  28. {restiny-0.1.1 → restiny-0.2.0}/restiny/utils.py +0 -0
  29. {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/custom_directory_tree.py +0 -0
  30. {restiny-0.1.1 → restiny-0.2.0}/restiny/widgets/custom_text_area.py +0 -0
  31. {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/dependency_links.txt +0 -0
  32. {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/entry_points.txt +0 -0
  33. {restiny-0.1.1 → restiny-0.2.0}/restiny.egg-info/top_level.txt +0 -0
  34. {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.1.1
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.8
231
+ Requires-Python: >=3.10
233
232
  Description-Content-Type: text/markdown
234
233
  License-File: LICENSE
235
- Requires-Dist: textual<6.3,>=6.2
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
  ![OS support](https://img.shields.io/badge/OS-macOS%20Linux%20Windows-red)
242
- ![Python versions](https://img.shields.io/badge/Python-3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue)
241
+ ![Python versions](https://img.shields.io/badge/Python-3.10%20|%203.11%20|%203.12%20|%203.13%20|%203.14-blue)
243
242
 
244
243
 
245
- - [Restiny](#restiny)
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
- # Restiny
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
- <img width="1905" alt="image" src="https://github.com/user-attachments/assets/e6f0c03a-e98e-40cd-af1d-38489d650fb1" />
257
+ ![image](https://github.com/user-attachments/assets/e6f0c03a-e98e-40cd-af1d-38489d650fb1)
259
258
 
260
259
  ## How to install
261
260
 
@@ -1,8 +1,8 @@
1
1
  ![OS support](https://img.shields.io/badge/OS-macOS%20Linux%20Windows-red)
2
- ![Python versions](https://img.shields.io/badge/Python-3.8%20|%203.9%20|%203.10%20|%203.11%20|%203.12%20|%203.13-blue)
2
+ ![Python versions](https://img.shields.io/badge/Python-3.10%20|%203.11%20|%203.12%20|%203.13%20|%203.14-blue)
3
3
 
4
4
 
5
- - [Restiny](#restiny)
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
- # Restiny
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
- <img width="1905" alt="image" src="https://github.com/user-attachments/assets/e6f0c03a-e98e-40cd-af1d-38489d650fb1" />
18
+ ![image](https://github.com/user-attachments/assets/e6f0c03a-e98e-40cd-af1d-38489d650fb1)
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.8"
15
+ requires-python = ">=3.10"
16
16
  dependencies = [
17
- "textual>=6.2,<6.3",
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 = "py313"
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
- headers['content-type'] = (
241
- mimetypes.guess_type(file.name)[0]
242
- or 'application/octet-stream'
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=[DynamicField(enabled=False, key='', value='')],
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=[DynamicField(enabled=False, key='', value='')],
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
- # ('Form (multipart)', BodyMode.FORM_MULTIPART)
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
- [DynamicField(enabled=False, key='', value='')],
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()
@@ -39,6 +39,5 @@ class ContentType(StrEnum):
39
39
  JSON = 'application/json'
40
40
  YAML = 'application/x-yaml'
41
41
  XML = 'application/xml'
42
-
43
42
  FORM_URLENCODED = 'application/x-www-form-urlencoded'
44
43
  FORM_MULTIPART = 'multipart/form-data'
@@ -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 DynamicField, DynamicFields
7
+ from restiny.widgets.dynamic_fields import DynamicFields, TextDynamicField
8
8
  from restiny.widgets.path_chooser import PathChooser
9
9
 
10
10
  __all__ = [
11
- 'DynamicField',
11
+ 'TextDynamicField',
12
12
  'DynamicFields',
13
13
  'CustomDirectoryTree',
14
14
  'CustomTextArea',