omdev 0.0.0.dev440__py3-none-any.whl → 0.0.0.dev486__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.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

Files changed (132) hide show
  1. omdev/.omlish-manifests.json +17 -29
  2. omdev/__about__.py +9 -7
  3. omdev/amalg/gen/gen.py +49 -6
  4. omdev/amalg/gen/imports.py +1 -1
  5. omdev/amalg/gen/manifests.py +1 -1
  6. omdev/amalg/gen/resources.py +1 -1
  7. omdev/amalg/gen/srcfiles.py +13 -3
  8. omdev/amalg/gen/strip.py +1 -1
  9. omdev/amalg/gen/types.py +1 -1
  10. omdev/amalg/gen/typing.py +1 -1
  11. omdev/amalg/info.py +32 -0
  12. omdev/cache/data/actions.py +1 -1
  13. omdev/cache/data/specs.py +1 -1
  14. omdev/cexts/_boilerplate.cc +2 -3
  15. omdev/cexts/cmake.py +4 -1
  16. omdev/ci/cli.py +1 -2
  17. omdev/cmdlog/cli.py +1 -2
  18. omdev/dataclasses/_dumping.py +1960 -0
  19. omdev/dataclasses/_template.py +22 -0
  20. omdev/dataclasses/cli.py +6 -1
  21. omdev/dataclasses/codegen.py +340 -60
  22. omdev/dataclasses/dumping.py +200 -0
  23. omdev/interp/uv/provider.py +1 -0
  24. omdev/interp/venvs.py +1 -0
  25. omdev/irc/messages/base.py +50 -0
  26. omdev/irc/messages/formats.py +92 -0
  27. omdev/irc/messages/messages.py +775 -0
  28. omdev/irc/messages/parsing.py +99 -0
  29. omdev/irc/numerics/__init__.py +0 -0
  30. omdev/irc/numerics/formats.py +97 -0
  31. omdev/irc/numerics/numerics.py +865 -0
  32. omdev/irc/numerics/types.py +59 -0
  33. omdev/irc/protocol/LICENSE +11 -0
  34. omdev/irc/protocol/__init__.py +61 -0
  35. omdev/irc/protocol/consts.py +6 -0
  36. omdev/irc/protocol/errors.py +30 -0
  37. omdev/irc/protocol/message.py +21 -0
  38. omdev/irc/protocol/nuh.py +55 -0
  39. omdev/irc/protocol/parsing.py +158 -0
  40. omdev/irc/protocol/rendering.py +153 -0
  41. omdev/irc/protocol/tags.py +102 -0
  42. omdev/irc/protocol/utils.py +30 -0
  43. omdev/manifests/_dumping.py +125 -25
  44. omdev/markdown/__init__.py +0 -0
  45. omdev/markdown/incparse.py +116 -0
  46. omdev/markdown/tokens.py +51 -0
  47. omdev/packaging/marshal.py +8 -8
  48. omdev/packaging/requires.py +6 -6
  49. omdev/packaging/specifiers.py +2 -1
  50. omdev/packaging/versions.py +4 -4
  51. omdev/packaging/wheelfile.py +2 -0
  52. omdev/precheck/blanklines.py +66 -0
  53. omdev/precheck/caches.py +1 -1
  54. omdev/precheck/imports.py +14 -1
  55. omdev/precheck/main.py +4 -3
  56. omdev/precheck/unicode.py +39 -15
  57. omdev/py/asts/__init__.py +0 -0
  58. omdev/py/asts/parents.py +28 -0
  59. omdev/py/asts/toplevel.py +123 -0
  60. omdev/py/asts/visitors.py +18 -0
  61. omdev/py/attrdocs.py +1 -1
  62. omdev/py/bracepy.py +12 -4
  63. omdev/py/reprs.py +32 -0
  64. omdev/py/srcheaders.py +1 -1
  65. omdev/py/tokens/__init__.py +0 -0
  66. omdev/py/tools/mkrelimp.py +1 -1
  67. omdev/py/tools/pipdepup.py +629 -0
  68. omdev/pyproject/pkg.py +190 -45
  69. omdev/pyproject/reqs.py +31 -9
  70. omdev/pyproject/tools/__init__.py +0 -0
  71. omdev/pyproject/tools/aboutdeps.py +55 -0
  72. omdev/pyproject/venvs.py +8 -1
  73. omdev/rs/__init__.py +0 -0
  74. omdev/scripts/ci.py +398 -80
  75. omdev/scripts/interp.py +193 -35
  76. omdev/scripts/lib/inject.py +74 -27
  77. omdev/scripts/lib/logs.py +75 -27
  78. omdev/scripts/lib/marshal.py +67 -25
  79. omdev/scripts/pyproject.py +941 -90
  80. omdev/tools/git/cli.py +10 -0
  81. omdev/tools/json/processing.py +5 -2
  82. omdev/tools/jsonview/cli.py +31 -5
  83. omdev/tools/pawk/pawk.py +2 -2
  84. omdev/tools/pip.py +8 -0
  85. omdev/tui/__init__.py +0 -0
  86. omdev/tui/apps/__init__.py +0 -0
  87. omdev/tui/apps/edit/__init__.py +0 -0
  88. omdev/tui/apps/edit/main.py +163 -0
  89. omdev/tui/apps/irc/__init__.py +0 -0
  90. omdev/tui/apps/irc/__main__.py +4 -0
  91. omdev/tui/apps/irc/app.py +278 -0
  92. omdev/tui/apps/irc/client.py +187 -0
  93. omdev/tui/apps/irc/commands.py +175 -0
  94. omdev/tui/apps/irc/main.py +26 -0
  95. omdev/tui/apps/markdown/__init__.py +0 -0
  96. omdev/tui/apps/markdown/__main__.py +11 -0
  97. omdev/{ptk → tui/apps}/markdown/cli.py +5 -7
  98. omdev/tui/rich/__init__.py +34 -0
  99. omdev/tui/rich/console2.py +20 -0
  100. omdev/tui/rich/markdown2.py +186 -0
  101. omdev/tui/textual/__init__.py +226 -0
  102. omdev/tui/textual/app2.py +11 -0
  103. omdev/tui/textual/autocomplete/LICENSE +21 -0
  104. omdev/tui/textual/autocomplete/__init__.py +33 -0
  105. omdev/tui/textual/autocomplete/matching.py +226 -0
  106. omdev/tui/textual/autocomplete/paths.py +202 -0
  107. omdev/tui/textual/autocomplete/widget.py +612 -0
  108. omdev/tui/textual/drivers2.py +55 -0
  109. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev486.dist-info}/METADATA +11 -9
  110. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev486.dist-info}/RECORD +119 -73
  111. omdev/ptk/__init__.py +0 -103
  112. omdev/ptk/apps/ncdu.py +0 -167
  113. omdev/ptk/confirm.py +0 -60
  114. omdev/ptk/markdown/LICENSE +0 -22
  115. omdev/ptk/markdown/__init__.py +0 -10
  116. omdev/ptk/markdown/__main__.py +0 -11
  117. omdev/ptk/markdown/border.py +0 -94
  118. omdev/ptk/markdown/markdown.py +0 -390
  119. omdev/ptk/markdown/parser.py +0 -42
  120. omdev/ptk/markdown/styles.py +0 -29
  121. omdev/ptk/markdown/tags.py +0 -299
  122. omdev/ptk/markdown/utils.py +0 -366
  123. omdev/pyproject/cexts.py +0 -110
  124. /omdev/{ptk/apps → irc}/__init__.py +0 -0
  125. /omdev/{tokens → irc/messages}/__init__.py +0 -0
  126. /omdev/{tokens → py/tokens}/all.py +0 -0
  127. /omdev/{tokens → py/tokens}/tokenizert.py +0 -0
  128. /omdev/{tokens → py/tokens}/utils.py +0 -0
  129. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev486.dist-info}/WHEEL +0 -0
  130. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev486.dist-info}/entry_points.txt +0 -0
  131. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev486.dist-info}/licenses/LICENSE +0 -0
  132. {omdev-0.0.0.dev440.dist-info → omdev-0.0.0.dev486.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,202 @@
1
+ import os
2
+ import pathlib
3
+ import typing as ta
4
+
5
+ from textual.cache import LRUCache
6
+ from textual.content import Content
7
+ from textual.widgets import Input
8
+
9
+ from .widget import AutoComplete
10
+ from .widget import AutoCompleteItem
11
+
12
+
13
+ ##
14
+
15
+
16
+ class PathAutoCompleteItem(AutoCompleteItem):
17
+ def __init__(self, completion: str, path: pathlib.Path) -> None:
18
+ super().__init__(completion)
19
+
20
+ self.path = path
21
+
22
+
23
+ def default_path_input_sort_key(item: PathAutoCompleteItem) -> tuple[bool, bool, str]:
24
+ """
25
+ Sort key function for results within the dropdown.
26
+
27
+ Args:
28
+ item: The PathAutoCompleteItem to get a sort key for.
29
+
30
+ Returns:
31
+ A tuple of (is_dotfile, is_file, lowercase_name) for sorting.
32
+ """
33
+
34
+ name = item.path.name
35
+ is_dotfile = name.startswith('.')
36
+ return (not item.path.is_dir(), not is_dotfile, name.lower())
37
+
38
+
39
+ class PathAutoComplete(AutoComplete):
40
+ def __init__(
41
+ self,
42
+ target: Input | str,
43
+ path: str | pathlib.Path = '.',
44
+ *,
45
+ show_dotfiles: bool = True,
46
+ sort_key: ta.Callable[[PathAutoCompleteItem], ta.Any] = default_path_input_sort_key,
47
+ folder_prefix: Content = Content('📂'),
48
+ file_prefix: Content = Content('📄'),
49
+ prevent_default_enter: bool = True,
50
+ prevent_default_tab: bool = True,
51
+ cache_size: int = 100,
52
+ name: str | None = None,
53
+ id: str | None = None, # noqa
54
+ classes: str | None = None,
55
+ disabled: bool = False,
56
+ ) -> None:
57
+ """
58
+ An autocomplete widget for filesystem paths.
59
+
60
+ Args:
61
+ target: The target input widget to autocomplete.
62
+ path: The base path to autocomplete from.
63
+ show_dotfiles: Whether to show dotfiles (files/dirs starting with ".").
64
+ sort_key: Function to sort the dropdown items.
65
+ folder_prefix: The prefix for folder items (e.g. 📂).
66
+ file_prefix: The prefix for file items (e.g. 📄).
67
+ prevent_default_enter: Whether to prevent the default enter behavior.
68
+ prevent_default_tab: Whether to prevent the default tab behavior.
69
+ cache_size: The number of directories to cache.
70
+ name: The name of the widget.
71
+ id: The DOM node id of the widget.
72
+ classes: The CSS classes of the widget.
73
+ disabled: Whether the widget is disabled.
74
+ """
75
+
76
+ super().__init__(
77
+ target,
78
+ None,
79
+ prevent_default_enter=prevent_default_enter,
80
+ prevent_default_tab=prevent_default_tab,
81
+ name=name,
82
+ id=id,
83
+ classes=classes,
84
+ disabled=disabled,
85
+ )
86
+
87
+ self.path = pathlib.Path(path) if isinstance(path, str) else path
88
+ self.show_dotfiles = show_dotfiles
89
+ self.sort_key = sort_key
90
+ self.folder_prefix = folder_prefix
91
+ self.file_prefix = file_prefix
92
+ self._directory_cache: LRUCache[str, list[os.DirEntry[str]]] = LRUCache(cache_size)
93
+
94
+ def get_candidates(self, target_state: AutoComplete.TargetState) -> list[AutoCompleteItem]:
95
+ """
96
+ Get the candidates for the current path segment.
97
+
98
+ This is called each time the input changes or the cursor position changes/
99
+ """
100
+
101
+ current_input = target_state.text[: target_state.cursor_position]
102
+
103
+ if '/' in current_input:
104
+ last_slash_index = current_input.rindex('/')
105
+ path_segment = current_input[:last_slash_index] or '/'
106
+ directory = self.path / path_segment if path_segment != '/' else self.path
107
+ else:
108
+ directory = self.path
109
+
110
+ # Use the directory path as the cache key
111
+ cache_key = str(directory)
112
+ cached_entries = self._directory_cache.get(cache_key)
113
+
114
+ if cached_entries is not None:
115
+ entries = cached_entries
116
+ else:
117
+ try:
118
+ entries = list(os.scandir(directory))
119
+ self._directory_cache[cache_key] = entries
120
+ except OSError:
121
+ return []
122
+
123
+ results: list[PathAutoCompleteItem] = []
124
+ for entry in entries:
125
+ # Only include the entry name, not the full path
126
+ completion = entry.name
127
+ if not self.show_dotfiles and completion.startswith('.'):
128
+ continue
129
+ if entry.is_dir():
130
+ completion += '/'
131
+ results.append(PathAutoCompleteItem(completion, path=pathlib.Path(entry.path)))
132
+
133
+ results.sort(key=self.sort_key)
134
+ folder_prefix = self.folder_prefix
135
+ file_prefix = self.file_prefix
136
+ return [
137
+ AutoCompleteItem(
138
+ item.main,
139
+ prefix=folder_prefix if item.path.is_dir() else file_prefix,
140
+ )
141
+ for item in results
142
+ ]
143
+
144
+ def get_search_string(self, target_state: AutoComplete.TargetState) -> str:
145
+ """Return only the current path segment for searching in the dropdown."""
146
+
147
+ current_input = target_state.text[: target_state.cursor_position]
148
+
149
+ if '/' in current_input:
150
+ last_slash_index = current_input.rindex('/')
151
+ search_string = current_input[last_slash_index + 1:]
152
+ return search_string
153
+ else:
154
+ return current_input
155
+
156
+ def apply_completion(self, value: str, state: AutoComplete.TargetState) -> None:
157
+ """Apply the completion by replacing only the current path segment."""
158
+
159
+ target = self.target
160
+ current_input = state.text
161
+ cursor_position = state.cursor_position
162
+
163
+ # There's a slash before the cursor, so we only want to replace the text after the last slash with the selected
164
+ # value
165
+ try:
166
+ replace_start_index = current_input.rindex('/', 0, cursor_position)
167
+ except ValueError:
168
+ # No slashes, so we do a full replacement
169
+ new_value = value
170
+ new_cursor_position = len(value)
171
+ else:
172
+ # Keep everything before and including the slash before the cursor.
173
+ path_prefix = current_input[: replace_start_index + 1]
174
+ new_value = path_prefix + value
175
+ new_cursor_position = len(path_prefix) + len(value)
176
+
177
+ with self.prevent(Input.Changed):
178
+ target.value = new_value
179
+ target.cursor_position = new_cursor_position
180
+
181
+ def post_completion(self) -> None:
182
+ if not self.target.value.endswith('/'):
183
+ self.action_hide()
184
+
185
+ def should_show_dropdown(self, search_string: str) -> bool:
186
+ return (
187
+ super().should_show_dropdown(search_string) or
188
+ (
189
+ (search_string == '' and self.target.value != '') and
190
+ self.option_list.option_count > 1
191
+ )
192
+ )
193
+
194
+ def clear_directory_cache(self) -> None:
195
+ """
196
+ Clear the directory cache. If you know that the contents of the directory have changed, you can call this method
197
+ to invalidate the cache.
198
+ """
199
+
200
+ self._directory_cache.clear()
201
+ target_state = self._get_target_state()
202
+ self._rebuild_options(target_state, self.get_search_string(target_state))