backlogops-gui 0.9__tar.gz → 1.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.
Files changed (43) hide show
  1. {backlogops_gui-0.9 → backlogops_gui-1.0}/PKG-INFO +5 -5
  2. {backlogops_gui-0.9 → backlogops_gui-1.0}/README_pypi.md +2 -2
  3. {backlogops_gui-0.9 → backlogops_gui-1.0}/backlogops_gui.egg-info/PKG-INFO +5 -5
  4. {backlogops_gui-0.9 → backlogops_gui-1.0}/backlogops_gui.egg-info/requires.txt +2 -2
  5. {backlogops_gui-0.9 → backlogops_gui-1.0}/setup.py +3 -3
  6. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/config_edit.py +112 -77
  7. {backlogops_gui-0.9 → backlogops_gui-1.0}/LICENSE.txt +0 -0
  8. {backlogops_gui-0.9 → backlogops_gui-1.0}/backlogops_gui.egg-info/SOURCES.txt +0 -0
  9. {backlogops_gui-0.9 → backlogops_gui-1.0}/backlogops_gui.egg-info/dependency_links.txt +0 -0
  10. {backlogops_gui-0.9 → backlogops_gui-1.0}/backlogops_gui.egg-info/entry_points.txt +0 -0
  11. {backlogops_gui-0.9 → backlogops_gui-1.0}/backlogops_gui.egg-info/top_level.txt +0 -0
  12. {backlogops_gui-0.9 → backlogops_gui-1.0}/pyproject.toml +0 -0
  13. {backlogops_gui-0.9 → backlogops_gui-1.0}/setup.cfg +0 -0
  14. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/__init__.py +0 -0
  15. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/__main__.py +0 -0
  16. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/_migrate_warn.py +0 -0
  17. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/application.py +0 -0
  18. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/backlog_actions.py +0 -0
  19. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/backlog_dialogs.py +0 -0
  20. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/backlog_io.py +0 -0
  21. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/backlog_window.py +0 -0
  22. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/blog_version_reporter.py +0 -0
  23. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/choice_dialogs.py +0 -0
  24. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/file_choosers.py +0 -0
  25. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/format_dialogs.py +0 -0
  26. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_actions.py +0 -0
  27. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_base.py +0 -0
  28. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_dialogs.py +0 -0
  29. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_order.py +0 -0
  30. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_rank.py +0 -0
  31. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_read.py +0 -0
  32. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_rename.py +0 -0
  33. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_update.py +0 -0
  34. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/jira_write.py +0 -0
  35. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/key_list_box.py +0 -0
  36. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/log_buffer.py +0 -0
  37. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/modal_dialog.py +0 -0
  38. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/py.typed +0 -0
  39. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/python_version.py +0 -0
  40. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/report_windows.py +0 -0
  41. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/table_view.py +0 -0
  42. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/tcltk_version.py +0 -0
  43. {backlogops_gui-0.9 → backlogops_gui-1.0}/src/backlogops_gui/token_dialog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops-gui
3
- Version: 0.9
3
+ Version: 1.0
4
4
  Summary: Graphical user interface for backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -21,11 +21,11 @@ Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.12
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
- Requires-Dist: backlogops>=0.9
24
+ Requires-Dist: backlogops>=1.0
25
25
  Requires-Dist: argcomplete>=3.7.2
26
26
  Requires-Dist: wizard-ui-bridge>=1.3
27
27
  Requires-Dist: wizard-tk-bridge>=1.3
28
- Requires-Dist: edit-cfg-json-tk==0.0.2
28
+ Requires-Dist: edit-cfg-json-tk==0.0.4
29
29
  Requires-Dist: versionreporter>=0.4
30
30
  Dynamic: author
31
31
  Dynamic: license-file
@@ -362,10 +362,10 @@ one with the list of releases. You will want to use the menus.
362
362
 
363
363
  ## Test summary
364
364
 
365
- - Test result: 2232 passed, 1 deselected in 44s
365
+ - Test result: 2234 passed, 1 deselected in 45s
366
366
  - No flake8 warnings.
367
367
  - No mypy errors found.
368
368
  - No pylint warnings.
369
369
  - No python layout warnings.
370
- - Built version(s): 0.9
370
+ - Built version(s): 1.0
371
371
  - Build and test using Python 3.14.7
@@ -328,10 +328,10 @@ one with the list of releases. You will want to use the menus.
328
328
 
329
329
  ## Test summary
330
330
 
331
- - Test result: 2232 passed, 1 deselected in 44s
331
+ - Test result: 2234 passed, 1 deselected in 45s
332
332
  - No flake8 warnings.
333
333
  - No mypy errors found.
334
334
  - No pylint warnings.
335
335
  - No python layout warnings.
336
- - Built version(s): 0.9
336
+ - Built version(s): 1.0
337
337
  - Build and test using Python 3.14.7
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops-gui
3
- Version: 0.9
3
+ Version: 1.0
4
4
  Summary: Graphical user interface for backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -21,11 +21,11 @@ Classifier: Typing :: Typed
21
21
  Requires-Python: >=3.12
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE.txt
24
- Requires-Dist: backlogops>=0.9
24
+ Requires-Dist: backlogops>=1.0
25
25
  Requires-Dist: argcomplete>=3.7.2
26
26
  Requires-Dist: wizard-ui-bridge>=1.3
27
27
  Requires-Dist: wizard-tk-bridge>=1.3
28
- Requires-Dist: edit-cfg-json-tk==0.0.2
28
+ Requires-Dist: edit-cfg-json-tk==0.0.4
29
29
  Requires-Dist: versionreporter>=0.4
30
30
  Dynamic: author
31
31
  Dynamic: license-file
@@ -362,10 +362,10 @@ one with the list of releases. You will want to use the menus.
362
362
 
363
363
  ## Test summary
364
364
 
365
- - Test result: 2232 passed, 1 deselected in 44s
365
+ - Test result: 2234 passed, 1 deselected in 45s
366
366
  - No flake8 warnings.
367
367
  - No mypy errors found.
368
368
  - No pylint warnings.
369
369
  - No python layout warnings.
370
- - Built version(s): 0.9
370
+ - Built version(s): 1.0
371
371
  - Build and test using Python 3.14.7
@@ -1,6 +1,6 @@
1
- backlogops>=0.9
1
+ backlogops>=1.0
2
2
  argcomplete>=3.7.2
3
3
  wizard-ui-bridge>=1.3
4
4
  wizard-tk-bridge>=1.3
5
- edit-cfg-json-tk==0.0.2
5
+ edit-cfg-json-tk==0.0.4
6
6
  versionreporter>=0.4
@@ -5,7 +5,7 @@ from setuptools import setup
5
5
 
6
6
  setup(
7
7
  name='backlogops-gui',
8
- version='0.9',
8
+ version='1.0',
9
9
  description='Graphical user interface for backlog operations.',
10
10
  author='Tom Björkholm',
11
11
  author_email='klausuler_linnet0q@icloud.com',
@@ -19,11 +19,11 @@ setup(
19
19
  ]
20
20
  },
21
21
  install_requires=[
22
- 'backlogops >= 0.9',
22
+ 'backlogops >= 1.0',
23
23
  'argcomplete >= 3.7.2',
24
24
  'wizard-ui-bridge >= 1.3',
25
25
  'wizard-tk-bridge >= 1.3',
26
- 'edit-cfg-json-tk == 0.0.2',
26
+ 'edit-cfg-json-tk == 0.0.4',
27
27
  'versionreporter >= 0.4'
28
28
  ]
29
29
  )
@@ -11,7 +11,7 @@ interface shows exactly the same configuration.
11
11
 
12
12
  These are functions taking the application rather than a collaborator
13
13
  object, because an editing session keeps nothing between two of them: the
14
- model belongs to the session and everything else is the application's.
14
+ session belongs to the window and everything else is the application's.
15
15
 
16
16
  The editor is mounted in a :class:`tkinter.Toplevel` this module creates,
17
17
  rather than started through ``edit_cfg_json_tk.edit``. That entry point
@@ -19,9 +19,10 @@ creates a ``tkinter.Tk`` and an event loop of its own, which is for an
19
19
  application that runs neither yet: a second Tcl interpreter shares nothing
20
20
  with the first, and a nested loop would not end when the editor window
21
21
  closed, because Tcl runs its loop while any window of the process lives.
22
- ``EditorWidgets`` is what the library offers for a window an application
23
- owns, and it takes the close action as an argument so that the editor never
24
- destroys a window it did not create.
22
+ ``TkEditorPanel`` is what the library offers for an application that already
23
+ runs Tk. It is given the window as its ``area``, so that the window stays
24
+ this module's — its title names the file being edited, and the editor
25
+ destroys only what it built itself.
25
26
 
26
27
  The window is not made modal. The editor opens dialogs of its own — a file
27
28
  chooser for Save as…, a question before it overwrites a file, and one asking
@@ -34,12 +35,13 @@ their clicks and keys from reaching them.
34
35
 
35
36
  import tkinter as tk
36
37
  from pathlib import Path
37
- from typing import Callable, NamedTuple, Optional, TYPE_CHECKING
38
- from edit_cfg_json import EditModel
39
- from edit_cfg_json_tk.tk_editor import EditorWidgets
38
+ from typing import NamedTuple, Optional, TYPE_CHECKING
39
+ from config_as_json import Config, PathOrStr
40
+ from edit_cfg_json import ConfigLoadError, EditModel, default_config
41
+ from edit_cfg_json_tk import TkEditorPanel
40
42
  from wizard_tk_bridge.close_binding import bind_close
41
43
  from backlogops import (
42
- BacklogOpsConfig, default_edit_config, edit_model_for, io_preset_class)
44
+ BacklogOpsConfig, EDIT_SETTINGS, descriptions_for, io_preset_class)
43
45
  from backlogops_gui.choice_dialogs import EditTargetChoice, ask_edit_target
44
46
  from backlogops_gui.file_choosers import (
45
47
  choose_config_to_edit, choose_preset_to_edit)
@@ -47,7 +49,7 @@ from backlogops_gui.file_choosers import (
47
49
  if TYPE_CHECKING:
48
50
  from backlogops_gui.application import BacklogApp
49
51
 
50
- EDIT_ERRORS = (ValueError, TypeError, KeyError, OSError)
52
+ EDIT_ERRORS = (ConfigLoadError, ValueError, TypeError, KeyError, OSError)
51
53
  """Errors raised when a configuration cannot be opened for editing."""
52
54
 
53
55
  OPEN_FAIL_TITLE = 'Could not open the configuration'
@@ -58,58 +60,97 @@ ADOPTED = 'The saved configuration is now the one in use.'
58
60
 
59
61
 
60
62
  class EditorWindow(NamedTuple):
61
- """One editor window and the widgets that have to be kept with it.
62
-
63
- The widgets are carried beside the window because a ``StringVar`` unsets
64
- its Tcl variable when it is collected, and the field it belongs to would
65
- then lose both its text and the callback that writes into the model.
66
- """
63
+ """One editor window and the panel running the session inside it."""
67
64
 
68
65
  window: tk.Toplevel
69
- widgets: EditorWidgets
66
+ panel: TkEditorPanel
70
67
 
71
68
 
72
- def editor_window(parent: tk.Misc, model: EditModel,
73
- title: str) -> EditorWindow:
69
+ def editor_window(app: 'BacklogApp', config: Config, title: str, *,
70
+ in_file: Optional[PathOrStr] = None,
71
+ out_file: Optional[PathOrStr] = None) -> EditorWindow:
74
72
  """Create a window of the application with the editor mounted in it.
75
73
 
76
74
  Every way out of the editor — its Close button, its key, the close
77
75
  button of the window and the platform close key — goes through the
78
76
  editor's own close action, so none of them can drop an unsaved change
79
- without asking. Closing destroys this window and nothing else.
77
+ without asking. Closing takes the editor off this window, and the
78
+ window then goes with it.
80
79
 
81
80
  Args:
82
- parent: Widget the window belongs to, which is the main window.
83
- model: Model of the editing session to show.
81
+ app: The application, whose window this one belongs to.
82
+ config: Configuration object of the class to edit, holding the
83
+ values to start from when there is no input file.
84
84
  title: Title of the window, saying what is being edited.
85
+ in_file: Configuration file to read, or None to edit the values
86
+ that ``config`` holds.
87
+ out_file: Configuration file a save writes, or None to write the
88
+ input file. With neither, the editor asks the user for one.
85
89
 
86
90
  Returns:
87
- The window and the widgets mounted in it.
91
+ The window and the panel that the editor runs in.
92
+
93
+ Raises:
94
+ ConfigLoadError: The input file cannot be opened for editing. The
95
+ window is taken away again before this is raised.
88
96
  """
89
- window = tk.Toplevel(parent)
97
+ window = tk.Toplevel(app.root)
90
98
  window.title(title)
91
- if isinstance(parent, tk.Wm):
92
- window.transient(parent)
93
- widgets = EditorWidgets(parent=window, model=model,
94
- on_close=window.destroy)
95
- window.protocol('WM_DELETE_WINDOW', widgets.close_editor)
96
- bind_close(window, widgets.close_editor)
97
- return EditorWindow(window=window, widgets=widgets)
99
+ window.transient(app.root)
100
+ try:
101
+ panel = TkEditorPanel(config, area=window, modal=False,
102
+ on_close=window.destroy,
103
+ descriptions=descriptions_for(config),
104
+ in_file=in_file, out_file=out_file,
105
+ settings=EDIT_SETTINGS, stderr_file=app.log)
106
+ except EDIT_ERRORS:
107
+ window.destroy()
108
+ raise
109
+ window.protocol('WM_DELETE_WINDOW', panel.close)
110
+ bind_close(window, panel.close)
111
+ return EditorWindow(window=window, panel=panel)
112
+
113
+
114
+ def edit_in_window(app: 'BacklogApp', config: Config, title: str, *,
115
+ in_file: Optional[PathOrStr] = None,
116
+ out_file: Optional[PathOrStr] = None) -> EditModel:
117
+ """Show one configuration in a window of its own until it is closed.
98
118
 
119
+ Args:
120
+ app, config, title, in_file, out_file: As of :func:`editor_window`.
99
121
 
100
- def open_editor_window(parent: tk.Misc, model: EditModel, title: str) -> None:
101
- """Show one edit model in a window of its own until it is closed.
122
+ Returns:
123
+ The model of the session that has just ended, which says what it
124
+ saved.
102
125
 
103
- The window and its widgets are held by the local name for as long as
104
- this call waits for the window, which is as long as they are needed.
126
+ Raises:
127
+ ConfigLoadError: The input file cannot be opened for editing.
128
+ """
129
+ mounted = editor_window(app, config, title, in_file=in_file,
130
+ out_file=out_file)
131
+ mounted.window.wait_window()
132
+ return mounted.panel.model
133
+
134
+
135
+ def open_editor_window(app: 'BacklogApp', config: Config, title: str, *,
136
+ in_file: Optional[PathOrStr] = None,
137
+ out_file: Optional[PathOrStr] = None
138
+ ) -> Optional[EditModel]:
139
+ """Run one editing session in a window, or report why it cannot run.
105
140
 
106
141
  Args:
107
- parent: Widget the window belongs to, which is the main window.
108
- model: Model of the editing session to show.
109
- title: Title of the window, saying what is being edited.
142
+ app, config, title, in_file, out_file: As of :func:`editor_window`.
143
+
144
+ Returns:
145
+ The model of the session that ran, or None when the configuration
146
+ could not be opened for editing, which is then reported.
110
147
  """
111
- mounted = editor_window(parent, model, title)
112
- mounted.window.wait_window()
148
+ try:
149
+ return edit_in_window(app, config, title, in_file=in_file,
150
+ out_file=out_file)
151
+ except EDIT_ERRORS as error:
152
+ _report_failure(app, error)
153
+ return None
113
154
 
114
155
 
115
156
  def edit_config(app: 'BacklogApp') -> None:
@@ -123,19 +164,10 @@ def edit_config(app: 'BacklogApp') -> None:
123
164
  choice = ask_edit_target(app.root)
124
165
  if choice is EditTargetChoice.CANCEL:
125
166
  return
126
- model = (_in_use_model(app) if choice is EditTargetChoice.IN_USE
127
- else _config_file_model(app))
128
- if model is None:
129
- return
130
- open_editor_window(app.root, model, _config_title(model))
131
- _adopt_saved(app, model)
132
-
133
-
134
- def _config_title(model: EditModel) -> str:
135
- """Return the window title, naming the file when a save has one."""
136
- if model.out_file is None:
137
- return 'Edit configuration'
138
- return f'Edit configuration {Path(model.out_file).name}'
167
+ model = (_edit_in_use(app) if choice is EditTargetChoice.IN_USE
168
+ else _edit_config_file(app))
169
+ if model is not None:
170
+ _adopt_saved(app, model)
139
171
 
140
172
 
141
173
  def edit_preset_file(app: 'BacklogApp') -> None:
@@ -148,17 +180,19 @@ def edit_preset_file(app: 'BacklogApp') -> None:
148
180
  path = choose_preset_to_edit(app.root)
149
181
  if path is None:
150
182
  return
151
- model = _built(app, lambda: edit_model_for(
152
- default_edit_config(io_preset_class(path)), in_file=path,
153
- stderr_file=app.log))
154
- if model is None:
183
+ try:
184
+ config = default_config(io_preset_class(path))
185
+ except EDIT_ERRORS as error:
186
+ _report_failure(app, error)
155
187
  return
156
- open_editor_window(app.root, model, f'Edit preset {Path(path).name}')
157
- _report_saved(app, model, 'Preset saved')
188
+ model = open_editor_window(app, config, f'Edit preset {Path(path).name}',
189
+ in_file=path)
190
+ if model is not None:
191
+ _report_saved(app, model, 'Preset saved')
158
192
 
159
193
 
160
- def _in_use_model(app: 'BacklogApp') -> Optional[EditModel]:
161
- """Return the model editing the configuration the application uses.
194
+ def _edit_in_use(app: 'BacklogApp') -> Optional[EditModel]:
195
+ """Edit the configuration the application uses, in a window.
162
196
 
163
197
  A save writes the file the configuration was loaded from, when it came
164
198
  from one; a configuration that came from the wizard has no file yet and
@@ -168,19 +202,25 @@ def _in_use_model(app: 'BacklogApp') -> Optional[EditModel]:
168
202
  if config is None:
169
203
  app.show_error('No configuration', NO_CONFIG_TEXT)
170
204
  return None
171
- return _built(app, lambda: edit_model_for(config,
172
- out_file=_loaded_file(app),
173
- stderr_file=app.log))
205
+ out_file = _loaded_file(app)
206
+ return open_editor_window(app, config, _config_title(out_file),
207
+ out_file=out_file)
174
208
 
175
209
 
176
- def _config_file_model(app: 'BacklogApp') -> Optional[EditModel]:
177
- """Return the model editing a configuration file the user picks."""
210
+ def _edit_config_file(app: 'BacklogApp') -> Optional[EditModel]:
211
+ """Edit a configuration file the user picks, in a window."""
178
212
  path = choose_config_to_edit(app.root)
179
213
  if path is None:
180
214
  return None
181
- return _built(app, lambda: edit_model_for(
182
- default_edit_config(BacklogOpsConfig), in_file=path,
183
- stderr_file=app.log))
215
+ return open_editor_window(app, default_config(BacklogOpsConfig),
216
+ _config_title(path), in_file=path)
217
+
218
+
219
+ def _config_title(name: Optional[str]) -> str:
220
+ """Return the window title, naming the file when the session has one."""
221
+ if name is None:
222
+ return 'Edit configuration'
223
+ return f'Edit configuration {Path(name).name}'
184
224
 
185
225
 
186
226
  def _loaded_file(app: 'BacklogApp') -> Optional[str]:
@@ -196,15 +236,10 @@ def _loaded_file(app: 'BacklogApp') -> Optional[str]:
196
236
  return source
197
237
 
198
238
 
199
- def _built(app: 'BacklogApp',
200
- build: Callable[[], EditModel]) -> Optional[EditModel]:
201
- """Return the model that ``build`` makes, reporting a refusal."""
202
- try:
203
- return build()
204
- except EDIT_ERRORS as error:
205
- app.log.write(f'{OPEN_FAIL_TITLE}: {error}\n')
206
- app.show_error(OPEN_FAIL_TITLE, str(error))
207
- return None
239
+ def _report_failure(app: 'BacklogApp', error: Exception) -> None:
240
+ """Log and show why a configuration cannot be opened for editing."""
241
+ app.log.write(f'{OPEN_FAIL_TITLE}: {error}\n')
242
+ app.show_error(OPEN_FAIL_TITLE, str(error))
208
243
 
209
244
 
210
245
  def _adopt_saved(app: 'BacklogApp', model: EditModel) -> None:
File without changes
File without changes