kash-shell 0.3.10__py3-none-any.whl → 0.3.11__py3-none-any.whl

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.
Files changed (40) hide show
  1. kash/actions/core/format_markdown_template.py +2 -5
  2. kash/actions/core/markdownify.py +2 -4
  3. kash/actions/core/readability.py +2 -4
  4. kash/actions/core/render_as_html.py +30 -11
  5. kash/actions/core/show_webpage.py +6 -11
  6. kash/actions/core/strip_html.py +2 -6
  7. kash/actions/core/{webpage_config.py → tabbed_webpage_config.py} +5 -3
  8. kash/actions/core/{webpage_generate.py → tabbed_webpage_generate.py} +5 -4
  9. kash/commands/base/files_command.py +28 -10
  10. kash/commands/workspace/workspace_commands.py +1 -2
  11. kash/config/colors.py +2 -2
  12. kash/exec/action_decorators.py +6 -6
  13. kash/exec/llm_transforms.py +6 -3
  14. kash/exec/preconditions.py +6 -0
  15. kash/exec/resolve_args.py +4 -0
  16. kash/file_storage/file_store.py +20 -18
  17. kash/help/function_param_info.py +1 -1
  18. kash/local_server/local_server_routes.py +1 -7
  19. kash/model/items_model.py +74 -28
  20. kash/shell/utils/shell_function_wrapper.py +15 -15
  21. kash/text_handling/doc_normalization.py +1 -1
  22. kash/text_handling/markdown_render.py +1 -0
  23. kash/text_handling/markdown_utils.py +22 -0
  24. kash/utils/common/function_inspect.py +360 -110
  25. kash/utils/file_utils/file_ext.py +4 -0
  26. kash/utils/file_utils/file_formats_model.py +17 -1
  27. kash/web_gen/__init__.py +0 -4
  28. kash/web_gen/simple_webpage.py +52 -0
  29. kash/web_gen/tabbed_webpage.py +23 -16
  30. kash/web_gen/template_render.py +37 -2
  31. kash/web_gen/templates/base_styles.css.jinja +76 -56
  32. kash/web_gen/templates/base_webpage.html.jinja +85 -67
  33. kash/web_gen/templates/item_view.html.jinja +47 -37
  34. kash/web_gen/templates/simple_webpage.html.jinja +24 -0
  35. kash/web_gen/templates/tabbed_webpage.html.jinja +42 -32
  36. {kash_shell-0.3.10.dist-info → kash_shell-0.3.11.dist-info}/METADATA +5 -5
  37. {kash_shell-0.3.10.dist-info → kash_shell-0.3.11.dist-info}/RECORD +40 -38
  38. {kash_shell-0.3.10.dist-info → kash_shell-0.3.11.dist-info}/WHEEL +0 -0
  39. {kash_shell-0.3.10.dist-info → kash_shell-0.3.11.dist-info}/entry_points.txt +0 -0
  40. {kash_shell-0.3.10.dist-info → kash_shell-0.3.11.dist-info}/licenses/LICENSE +0 -0
@@ -2,111 +2,172 @@ import inspect
2
2
  import types
3
3
  from collections.abc import Callable
4
4
  from dataclasses import dataclass
5
+ from enum import Enum
5
6
  from inspect import Parameter
6
- from typing import Any, Union, cast, get_args, get_origin # pyright: ignore
7
+ from typing import Any, Union, cast, get_args, get_origin # pyright: ignore[reportDeprecated]
7
8
 
8
- NO_DEFAULT = Parameter.empty
9
+ NO_DEFAULT = Parameter.empty # Alias for clarity
10
+
11
+ ParameterKind = Parameter.POSITIONAL_ONLY.__class__
9
12
 
10
13
 
11
14
  @dataclass(frozen=True)
12
15
  class FuncParam:
16
+ """
17
+ Hold structured information about a single function parameter.
18
+ """
19
+
13
20
  name: str
14
- type: type | None
15
- default: Any
16
- position: int | None
17
- is_varargs: bool
21
+ kind: ParameterKind # e.g., POSITIONAL_OR_KEYWORD
22
+ annotation: Any # The raw type annotation (can be Parameter.empty)
23
+ default: Any # The raw default value (can be Parameter.empty)
24
+ position: int | None # Position index for positional args, None for keyword-only
18
25
 
19
- @property
20
- def is_positional(self) -> bool:
21
- """Is this a purely positional parameter, with no default value?"""
22
- return self.position is not None and not self.has_default
26
+ # Resolved type information
27
+ # This is the concrete, simplified main type (e.g., str from str | None, list from list[int])
28
+ effective_type: type | None
29
+ inner_type: type | None # For collections, the type of elements (e.g., str from list[str])
30
+ is_explicitly_optional: bool # True if original annotation was Optional[X] or X | None
23
31
 
24
32
  @property
25
33
  def has_default(self) -> bool:
26
34
  """Does this parameter have a default value?"""
27
35
  return self.default != NO_DEFAULT
28
36
 
37
+ @property
38
+ def is_pure_positional(self) -> bool:
39
+ """Is this a plain or varargs positional parameter, with no default value?"""
40
+ return self.position is not None and not self.has_default
41
+
42
+ @property
43
+ def is_positional_only(self) -> bool:
44
+ """Is a true positional parameter, with no default value."""
45
+ return self.kind == Parameter.POSITIONAL_ONLY
29
46
 
30
- def inspect_function_params(func: Callable[..., Any], unwrap: bool = True) -> list[FuncParam]:
47
+ @property
48
+ def is_positional_or_keyword(self) -> bool:
49
+ return self.kind == Parameter.POSITIONAL_OR_KEYWORD
50
+
51
+ @property
52
+ def is_varargs(self) -> bool:
53
+ return self.kind == Parameter.VAR_POSITIONAL or self.kind == Parameter.VAR_KEYWORD
54
+
55
+ @property
56
+ def is_keyword_only(self) -> bool:
57
+ return self.kind == Parameter.KEYWORD_ONLY
58
+
59
+
60
+ def _resolve_type_details(annotation: Any) -> tuple[type | None, type | None, bool]:
31
61
  """
32
- Get names and types of parameters on a Python function. Just a convenience wrapper
33
- around `inspect.signature` to give parameter names, types, and default values.
34
-
35
- It also parses `Optional` types to return the underlying type and unwraps
36
- decorated functions to get the underlying parameters. Returns a list of
37
- `FuncParam` values.
38
-
39
- Note that technically, parameters are of 5 types, positional-only,
40
- positional-or-keyword, keyword-only, varargs, or varargs-keyword, which is what
41
- `inspect` tells you, but in practice you typically just want to know
42
- `FuncParam.is_positional()`, i.e. if it is a pure positional parameter (no default)
43
- and not a keyword (with a default).
62
+ Resolves an annotation into (effective_type, inner_type, is_explicitly_optional).
63
+
64
+ - effective_type: The main type (e.g., str from Optional[str], list from list[int]).
65
+ - inner_type: The type of elements if effective_type is a collection
66
+ (e.g., int from list[int]). For non-collection or unparameterized
67
+ collection, this is None.
68
+ - is_explicitly_optional: True if the annotation was X | None or Optional[X].
44
69
  """
45
- unwrapped = inspect.unwrap(func) if unwrap else func
46
- signature = inspect.signature(unwrapped)
47
- params: list[FuncParam] = []
70
+ if annotation is Parameter.empty or annotation is Any:
71
+ return (None, None, False)
48
72
 
49
- for i, param in enumerate(signature.parameters.values()):
50
- is_positional = param.kind in (
51
- Parameter.POSITIONAL_ONLY,
52
- Parameter.POSITIONAL_OR_KEYWORD,
53
- Parameter.VAR_POSITIONAL,
54
- )
55
- is_varargs = param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD)
56
- has_default = param.default != NO_DEFAULT
57
-
58
- # Get type from type annotation or default value.
59
- param_type: type | None = None
60
- if param.annotation != Parameter.empty:
61
- param_type = _extract_simple_type(param.annotation)
62
- elif param.default is not Parameter.empty:
63
- param_type = type(param.default)
64
-
65
- func_param = FuncParam(
66
- name=param.name,
67
- type=param_type,
68
- default=param.default if has_default else NO_DEFAULT,
69
- position=i + 1 if is_positional else None,
70
- is_varargs=is_varargs,
71
- )
72
- params.append(func_param)
73
+ current_annotation = annotation
74
+ is_optional_flag = False
73
75
 
74
- return params
76
+ # Unwrap Optional[T] or T | None (from Python 3.10+ UnionType)
77
+ origin: type | None = get_origin(current_annotation)
78
+ args = get_args(current_annotation)
79
+
80
+ if origin is Union or ( # pyright: ignore[reportDeprecated]
81
+ hasattr(types, "UnionType") and isinstance(current_annotation, types.UnionType)
82
+ ):
83
+ non_none_args = [arg for arg in args if arg is not type(None)]
84
+ if len(non_none_args) == 1:
85
+ is_optional_flag = True
86
+ current_annotation = non_none_args[0]
87
+ origin = get_origin(current_annotation) # Re-evaluate for the unwrapped type
88
+ args = get_args(current_annotation)
89
+ elif not non_none_args: # Handles Union[NoneType] or just NoneType
90
+ return (type(None), None, True)
91
+ # If multiple non_none_args (e.g., int | str), current_annotation remains the Union for now.
92
+
93
+ # Determine effective_type and inner_type from (potentially unwrapped) current_annotation
94
+ final_effective_type: type | None = None
95
+ final_inner_type: type | None = None
96
+
97
+ if isinstance(
98
+ current_annotation, type
99
+ ): # Covers simple types (int, str) and resolved Union types (int | str)
100
+ final_effective_type = current_annotation
101
+ elif origin and isinstance(origin, type): # Generics like list, dict, tuple (e.g., list[str])
102
+ final_effective_type = cast(type, origin) # This would be `list` for `list[str]`
103
+ if args and _is_type_tuple(args) and args[0] is not Any:
104
+ # For simplicity, take the first type argument as inner_type.
105
+ # E.g., for list[str], inner_type is str. For dict[str, int], inner_type is str.
106
+ final_inner_type = args[0]
107
+ # A more sophisticated approach might handle all args for tuples like tuple[str, int]
108
+
109
+ return final_effective_type, final_inner_type, is_optional_flag
110
+
111
+
112
+ def _is_type_tuple(args: tuple[Any, ...]) -> bool:
113
+ """Are all args types?"""
114
+ if not args:
115
+ return False
116
+ return all(isinstance(arg, type) for arg in args)
75
117
 
76
118
 
77
- def _extract_simple_type(annotation: Any) -> type | None:
119
+ def inspect_function_params(func: Callable[..., Any], unwrap: bool = True) -> list[FuncParam]:
78
120
  """
79
- Extract a single Type from an annotation that is an explicit simple type (like `str` or
80
- an enum) or a simple Union (such as `str` from `Optional[str]`). Return None if it's not
81
- clear.
121
+ Inspects a Python function's signature and returns a list of `ParamInfo` objects.
122
+ A convenience wrapper for `inspect.signature` that provides a detailed, structured
123
+ representation of each parameter, making it easier to build tools like CLI argument
124
+ parsers. By default, it unwraps decorated functions to get to the original signature.
82
125
  """
83
- if isinstance(annotation, type):
84
- return annotation
126
+ unwrapped_func = inspect.unwrap(func) if unwrap else func
127
+ signature = inspect.signature(unwrapped_func)
85
128
 
86
- # Handle pipe syntax (str | None) from Python 3.10+
87
- if hasattr(types, "UnionType") and isinstance(annotation, types.UnionType):
88
- args = get_args(annotation)
89
- non_none_args = [arg for arg in args if arg is not type(None)]
90
- if len(non_none_args) == 1 and isinstance(non_none_args[0], type):
91
- return non_none_args[0]
129
+ param_infos: list[FuncParam] = []
92
130
 
93
- origin = get_origin(annotation)
94
- if origin is Union: # pyright: ignore
95
- args = get_args(annotation)
96
- non_none_args = [arg for arg in args if arg is not type(None)]
97
- if len(non_none_args) == 1 and isinstance(non_none_args[0], type):
98
- return non_none_args[0]
99
- elif origin and isinstance(origin, type):
100
- # Cast origin to type to satisfy the type checker
101
- return cast(type, origin) # pyright: ignore
131
+ for i, (param_name, param_obj) in enumerate(signature.parameters.items()):
132
+ effective_type, inner_type, is_optional = _resolve_type_details(param_obj.annotation)
133
+
134
+ # Fallback: if type is not resolved from annotation, try to infer from default value.
135
+ if (
136
+ effective_type is None
137
+ and param_obj.default is not NO_DEFAULT
138
+ and param_obj.default is not None
139
+ ):
140
+ if not is_optional: # Avoid setting NoneType if it was Optional[SomethingElse]
141
+ effective_type = type(param_obj.default)
142
+
143
+ # Determine position
144
+ is_positional = param_obj.kind in (
145
+ Parameter.POSITIONAL_ONLY,
146
+ Parameter.POSITIONAL_OR_KEYWORD,
147
+ Parameter.VAR_POSITIONAL,
148
+ )
149
+ position = i + 1 if is_positional else None
150
+
151
+ info = FuncParam(
152
+ name=param_name,
153
+ kind=param_obj.kind,
154
+ annotation=param_obj.annotation, # Store raw annotation
155
+ default=param_obj.default, # Store raw default
156
+ position=position,
157
+ effective_type=effective_type,
158
+ inner_type=inner_type,
159
+ is_explicitly_optional=is_optional,
160
+ )
161
+ param_infos.append(info)
102
162
 
103
- return None
163
+ return param_infos
104
164
 
105
165
 
106
166
  ## Tests
107
167
 
108
168
 
109
- def test_inspect_function_params():
169
+ def test_inspect_function_parameters_updated():
170
+ # Test functions from your original example
110
171
  def func0(path: str | None = None) -> list:
111
172
  return [path]
112
173
 
@@ -124,55 +185,244 @@ def test_inspect_function_params():
124
185
  def func4() -> list:
125
186
  return []
126
187
 
127
- def func5(x: int, y: int = 3, *, z: int = 4, **kwargs): # pyright: ignore[reportUnusedParameter]
128
- pass
188
+ def func5(x: int, y: int = 3, *, z: int = 4, **kwargs):
189
+ return [x, y, z, kwargs]
129
190
 
130
- params0 = inspect_function_params(func0)
131
- params1 = inspect_function_params(func1)
132
- params2 = inspect_function_params(func2)
133
- params3 = inspect_function_params(func3)
134
- params4 = inspect_function_params(func4)
135
- params5 = inspect_function_params(func5)
191
+ class MyEnum(Enum):
192
+ ITEM1 = "item1"
193
+ ITEM2 = "item2"
194
+
195
+ def func6(opt_enum: MyEnum | None = MyEnum.ITEM1):
196
+ return [opt_enum]
136
197
 
137
- print("\ninspect:")
138
- print(repr(params0))
139
- print()
140
- print(repr(params1))
141
- print()
142
- print(repr(params2))
143
- print()
144
- print(repr(params3))
145
- print()
146
- print(repr(params4))
147
- print()
148
- print(repr(params5))
149
-
150
- assert params0 == [FuncParam(name="path", type=str, default=None, position=1, is_varargs=False)]
198
+ def func7(numbers: list[int]):
199
+ return [numbers]
151
200
 
201
+ def func8(maybe_list: list[str] | None = None):
202
+ return [maybe_list]
203
+
204
+ params0 = inspect_function_params(func0)
205
+ print("\ninspect_function_parameters results:")
206
+ print(f"func0: {params0}")
207
+ assert params0 == [
208
+ FuncParam(
209
+ name="path",
210
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
211
+ annotation=(str | None),
212
+ default=None,
213
+ position=1,
214
+ effective_type=str,
215
+ inner_type=None,
216
+ is_explicitly_optional=True,
217
+ )
218
+ ]
219
+
220
+ params1 = inspect_function_params(func1)
221
+ print(f"func1: {params1}")
152
222
  assert params1 == [
153
- FuncParam(name="arg1", type=str, default=NO_DEFAULT, position=1, is_varargs=False),
154
- FuncParam(name="arg2", type=str, default=NO_DEFAULT, position=2, is_varargs=False),
155
- FuncParam(name="arg3", type=int, default=NO_DEFAULT, position=3, is_varargs=False),
156
- FuncParam(name="option_one", type=bool, default=False, position=4, is_varargs=False),
157
- FuncParam(name="option_two", type=str, default=None, position=5, is_varargs=False),
223
+ FuncParam(
224
+ name="arg1",
225
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
226
+ annotation=str,
227
+ default=NO_DEFAULT,
228
+ position=1,
229
+ effective_type=str,
230
+ inner_type=None,
231
+ is_explicitly_optional=False,
232
+ ),
233
+ FuncParam(
234
+ name="arg2",
235
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
236
+ annotation=str,
237
+ default=NO_DEFAULT,
238
+ position=2,
239
+ effective_type=str,
240
+ inner_type=None,
241
+ is_explicitly_optional=False,
242
+ ),
243
+ FuncParam(
244
+ name="arg3",
245
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
246
+ annotation=int,
247
+ default=NO_DEFAULT,
248
+ position=3,
249
+ effective_type=int,
250
+ inner_type=None,
251
+ is_explicitly_optional=False,
252
+ ),
253
+ FuncParam(
254
+ name="option_one",
255
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
256
+ annotation=bool,
257
+ default=False,
258
+ position=4,
259
+ effective_type=bool,
260
+ inner_type=None,
261
+ is_explicitly_optional=False,
262
+ ), # bool default makes it not explicitly Optional from type
263
+ FuncParam(
264
+ name="option_two",
265
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
266
+ annotation=(str | None),
267
+ default=None,
268
+ position=5,
269
+ effective_type=str,
270
+ inner_type=None,
271
+ is_explicitly_optional=True,
272
+ ),
158
273
  ]
159
274
 
275
+ params2 = inspect_function_params(func2)
276
+ print(f"func2: {params2}")
160
277
  assert params2 == [
161
- FuncParam(name="paths", type=str, default=NO_DEFAULT, position=1, is_varargs=True),
162
- FuncParam(name="summary", type=bool, default=False, position=None, is_varargs=False),
163
- FuncParam(name="iso_time", type=bool, default=False, position=None, is_varargs=False),
278
+ FuncParam(
279
+ name="paths",
280
+ kind=Parameter.VAR_POSITIONAL,
281
+ annotation=str,
282
+ default=NO_DEFAULT,
283
+ position=1,
284
+ effective_type=str,
285
+ inner_type=None,
286
+ is_explicitly_optional=False,
287
+ ), # For *args: T, effective_type is T
288
+ FuncParam(
289
+ name="summary",
290
+ kind=Parameter.KEYWORD_ONLY,
291
+ annotation=(bool | None),
292
+ default=False,
293
+ position=None,
294
+ effective_type=bool,
295
+ inner_type=None,
296
+ is_explicitly_optional=True,
297
+ ),
298
+ FuncParam(
299
+ name="iso_time",
300
+ kind=Parameter.KEYWORD_ONLY,
301
+ annotation=bool,
302
+ default=False,
303
+ position=None,
304
+ effective_type=bool,
305
+ inner_type=None,
306
+ is_explicitly_optional=False,
307
+ ),
164
308
  ]
165
309
 
310
+ params3 = inspect_function_params(func3)
311
+ print(f"func3: {params3}")
166
312
  assert params3 == [
167
- FuncParam(name="arg1", type=str, default=NO_DEFAULT, position=1, is_varargs=False),
168
- FuncParam(name="keywords", type=None, default=NO_DEFAULT, position=None, is_varargs=True),
313
+ FuncParam(
314
+ name="arg1",
315
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
316
+ annotation=str,
317
+ default=NO_DEFAULT,
318
+ position=1,
319
+ effective_type=str,
320
+ inner_type=None,
321
+ is_explicitly_optional=False,
322
+ ),
323
+ FuncParam(
324
+ name="keywords",
325
+ kind=Parameter.VAR_KEYWORD,
326
+ annotation=Parameter.empty,
327
+ default=NO_DEFAULT,
328
+ position=None,
329
+ effective_type=None,
330
+ inner_type=None,
331
+ is_explicitly_optional=False,
332
+ ),
169
333
  ]
170
334
 
335
+ params4 = inspect_function_params(func4)
336
+ print(f"func4: {params4}")
171
337
  assert params4 == []
172
338
 
339
+ params5 = inspect_function_params(func5)
340
+ print(f"func5: {params5}")
173
341
  assert params5 == [
174
- FuncParam(name="x", type=int, default=NO_DEFAULT, position=1, is_varargs=False),
175
- FuncParam(name="y", type=int, default=3, position=2, is_varargs=False),
176
- FuncParam(name="z", type=int, default=4, position=None, is_varargs=False),
177
- FuncParam(name="kwargs", type=None, default=NO_DEFAULT, position=None, is_varargs=True),
342
+ FuncParam(
343
+ name="x",
344
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
345
+ annotation=int,
346
+ default=NO_DEFAULT,
347
+ position=1,
348
+ effective_type=int,
349
+ inner_type=None,
350
+ is_explicitly_optional=False,
351
+ ),
352
+ FuncParam(
353
+ name="y",
354
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
355
+ annotation=int,
356
+ default=3,
357
+ position=2,
358
+ effective_type=int,
359
+ inner_type=None,
360
+ is_explicitly_optional=False,
361
+ ),
362
+ FuncParam(
363
+ name="z",
364
+ kind=Parameter.KEYWORD_ONLY,
365
+ annotation=int,
366
+ default=4,
367
+ position=None,
368
+ effective_type=int,
369
+ inner_type=None,
370
+ is_explicitly_optional=False,
371
+ ),
372
+ FuncParam(
373
+ name="kwargs",
374
+ kind=Parameter.VAR_KEYWORD,
375
+ annotation=Parameter.empty,
376
+ default=NO_DEFAULT,
377
+ position=None,
378
+ effective_type=None,
379
+ inner_type=None,
380
+ is_explicitly_optional=False,
381
+ ),
382
+ ]
383
+
384
+ params6 = inspect_function_params(func6)
385
+ print(f"func6: {params6}")
386
+
387
+ assert params6 == [
388
+ FuncParam(
389
+ name="opt_enum",
390
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
391
+ annotation=(MyEnum | None),
392
+ default=MyEnum.ITEM1,
393
+ position=1,
394
+ effective_type=MyEnum,
395
+ inner_type=None,
396
+ is_explicitly_optional=True,
397
+ )
398
+ ]
399
+
400
+ params7 = inspect_function_params(func7)
401
+ print(f"func7: {params7}")
402
+ assert params7 == [
403
+ FuncParam(
404
+ name="numbers",
405
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
406
+ annotation=list[int],
407
+ default=NO_DEFAULT,
408
+ position=1,
409
+ effective_type=list,
410
+ inner_type=int,
411
+ is_explicitly_optional=False,
412
+ )
413
+ ]
414
+
415
+ params8 = inspect_function_params(func8)
416
+ print(f"func8: {params8}")
417
+ assert params8 == [
418
+ FuncParam(
419
+ name="maybe_list",
420
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
421
+ annotation=(list[str] | None),
422
+ default=None,
423
+ position=1,
424
+ effective_type=list,
425
+ inner_type=str,
426
+ is_explicitly_optional=True,
427
+ )
178
428
  ]
@@ -20,6 +20,7 @@ class FileExt(Enum):
20
20
  diff = "diff"
21
21
  json = "json"
22
22
  csv = "csv"
23
+ xlsx = "xlsx"
23
24
  npz = "npz"
24
25
  log = "log"
25
26
  py = "py"
@@ -34,6 +35,8 @@ class FileExt(Enum):
34
35
  mp3 = "mp3"
35
36
  m4a = "m4a"
36
37
  mp4 = "mp4"
38
+ pptx = "pptx"
39
+ epub = "epub"
37
40
 
38
41
  @property
39
42
  def dot_ext(self) -> str:
@@ -50,6 +53,7 @@ class FileExt(Enum):
50
53
  self.py,
51
54
  self.sh,
52
55
  self.xsh,
56
+ self.epub,
53
57
  ]
54
58
 
55
59
  @property
@@ -48,6 +48,7 @@ class Format(Enum):
48
48
  """`md_html` is Markdown with HTML, used for example when we structure Markdown with divs."""
49
49
  html = "html"
50
50
  """`markdown` should be simple and clean Markdown that we can use with LLMs."""
51
+ epub = "epub"
51
52
  yaml = "yaml"
52
53
  diff = "diff"
53
54
  python = "python"
@@ -56,12 +57,14 @@ class Format(Enum):
56
57
  xonsh = "xonsh"
57
58
  json = "json"
58
59
  csv = "csv"
60
+ xlsx = "xlsx"
59
61
  npz = "npz"
60
62
  log = "log"
61
63
 
62
64
  # Media formats.
63
65
  pdf = "pdf"
64
66
  docx = "docx"
67
+ pptx = "pptx"
65
68
  jpeg = "jpeg"
66
69
  png = "png"
67
70
  gif = "gif"
@@ -108,6 +111,7 @@ class Format(Enum):
108
111
  self.html,
109
112
  self.pdf,
110
113
  self.docx,
114
+ self.pptx,
111
115
  ]
112
116
 
113
117
  @property
@@ -128,7 +132,7 @@ class Format(Enum):
128
132
 
129
133
  @property
130
134
  def is_data(self) -> bool:
131
- return self in [self.csv, self.npz]
135
+ return self in [self.csv, self.xlsx, self.npz]
132
136
 
133
137
  @property
134
138
  def is_binary(self) -> bool:
@@ -166,6 +170,7 @@ class Format(Enum):
166
170
  Format.markdown: MediaType.text,
167
171
  Format.md_html: MediaType.text,
168
172
  Format.html: MediaType.webpage,
173
+ Format.epub: MediaType.text,
169
174
  Format.yaml: MediaType.text,
170
175
  Format.diff: MediaType.text,
171
176
  Format.python: MediaType.text,
@@ -175,11 +180,13 @@ class Format(Enum):
175
180
  Format.csv: MediaType.text,
176
181
  Format.log: MediaType.text,
177
182
  Format.pdf: MediaType.text,
183
+ Format.xlsx: MediaType.text,
178
184
  Format.jpeg: MediaType.image,
179
185
  Format.png: MediaType.image,
180
186
  Format.gif: MediaType.image,
181
187
  Format.svg: MediaType.image,
182
188
  Format.docx: MediaType.text,
189
+ Format.pptx: MediaType.text,
183
190
  Format.mp3: MediaType.audio,
184
191
  Format.m4a: MediaType.audio,
185
192
  Format.mp4: MediaType.video,
@@ -200,6 +207,7 @@ class Format(Enum):
200
207
  FileExt.diff.value: Format.diff,
201
208
  FileExt.json.value: Format.json,
202
209
  FileExt.csv.value: Format.csv,
210
+ FileExt.xlsx.value: Format.xlsx,
203
211
  FileExt.npz.value: Format.npz,
204
212
  FileExt.log.value: Format.log,
205
213
  FileExt.py.value: Format.python,
@@ -207,6 +215,7 @@ class Format(Enum):
207
215
  FileExt.xsh.value: Format.xonsh,
208
216
  FileExt.pdf.value: Format.pdf,
209
217
  FileExt.docx.value: Format.docx,
218
+ FileExt.pptx.value: Format.pptx,
210
219
  FileExt.jpg.value: Format.jpeg,
211
220
  FileExt.png.value: Format.png,
212
221
  FileExt.gif.value: Format.gif,
@@ -214,6 +223,7 @@ class Format(Enum):
214
223
  FileExt.mp3.value: Format.mp3,
215
224
  FileExt.m4a.value: Format.m4a,
216
225
  FileExt.mp4.value: Format.mp4,
226
+ FileExt.epub.value: Format.epub,
217
227
  }
218
228
  return ext_to_format.get(file_ext.value, None)
219
229
 
@@ -228,10 +238,12 @@ class Format(Enum):
228
238
  Format.md_html: FileExt.md,
229
239
  Format.html: FileExt.html,
230
240
  Format.plaintext: FileExt.txt,
241
+ Format.epub: FileExt.epub,
231
242
  Format.yaml: FileExt.yml,
232
243
  Format.diff: FileExt.diff,
233
244
  Format.json: FileExt.json,
234
245
  Format.csv: FileExt.csv,
246
+ Format.xlsx: FileExt.xlsx,
235
247
  Format.npz: FileExt.npz,
236
248
  Format.log: FileExt.log,
237
249
  Format.python: FileExt.py,
@@ -239,6 +251,7 @@ class Format(Enum):
239
251
  Format.xonsh: FileExt.xsh,
240
252
  Format.pdf: FileExt.pdf,
241
253
  Format.docx: FileExt.docx,
254
+ Format.pptx: FileExt.pptx,
242
255
  Format.jpeg: FileExt.jpg,
243
256
  Format.png: FileExt.png,
244
257
  Format.gif: FileExt.gif,
@@ -260,6 +273,7 @@ class Format(Enum):
260
273
  "text/html": Format.html,
261
274
  "text/diff": Format.diff,
262
275
  "text/x-diff": Format.diff,
276
+ "application/epub+zip": Format.epub,
263
277
  "application/yaml": Format.yaml,
264
278
  "application/x-yaml": Format.yaml,
265
279
  "text/x-python": Format.python,
@@ -269,9 +283,11 @@ class Format(Enum):
269
283
  "text/x-xonsh": Format.xonsh,
270
284
  "application/json": Format.json,
271
285
  "text/csv": Format.csv,
286
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": Format.xlsx,
272
287
  "application/x-npz": Format.npz,
273
288
  "application/pdf": Format.pdf,
274
289
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document": Format.docx,
290
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation": Format.pptx,
275
291
  "image/jpeg": Format.jpeg,
276
292
  "image/png": Format.png,
277
293
  "image/gif": Format.gif,
kash/web_gen/__init__.py CHANGED
@@ -1,4 +0,0 @@
1
- from pathlib import Path
2
-
3
- base_templates_dir = Path(__file__).parent / "templates"
4
- """Common base web page templates."""