backlogops 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 (69) hide show
  1. {backlogops-0.9 → backlogops-1.0}/PKG-INFO +7 -10
  2. {backlogops-0.9 → backlogops-1.0}/README_pypi.md +4 -7
  3. {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/PKG-INFO +7 -10
  4. {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/requires.txt +2 -2
  5. {backlogops-0.9 → backlogops-1.0}/setup.py +4 -4
  6. {backlogops-0.9 → backlogops-1.0}/src/backlogops/__init__.py +3 -4
  7. {backlogops-0.9 → backlogops-1.0}/src/backlogops/config_descriptions.py +23 -13
  8. backlogops-1.0/src/backlogops/config_editing.py +90 -0
  9. backlogops-0.9/src/backlogops/config_editing.py +0 -149
  10. {backlogops-0.9 → backlogops-1.0}/LICENSE.txt +0 -0
  11. {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/SOURCES.txt +0 -0
  12. {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/dependency_links.txt +0 -0
  13. {backlogops-0.9 → backlogops-1.0}/backlogops.egg-info/top_level.txt +0 -0
  14. {backlogops-0.9 → backlogops-1.0}/pyproject.toml +0 -0
  15. {backlogops-0.9 → backlogops-1.0}/setup.cfg +0 -0
  16. {backlogops-0.9 → backlogops-1.0}/src/backlogops/apply_format_rules.py +0 -0
  17. {backlogops-0.9 → backlogops-1.0}/src/backlogops/available_teams.py +0 -0
  18. {backlogops-0.9 → backlogops-1.0}/src/backlogops/available_teams_config.py +0 -0
  19. {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog.py +0 -0
  20. {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_helpers.py +0 -0
  21. {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_in_release_order.py +0 -0
  22. {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_ops_config.py +0 -0
  23. {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_ops_wizard.py +0 -0
  24. {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_releases.py +0 -0
  25. {backlogops-0.9 → backlogops-1.0}/src/backlogops/backlog_releases_io.py +0 -0
  26. {backlogops-0.9 → backlogops-1.0}/src/backlogops/blo_version_reporter.py +0 -0
  27. {backlogops-0.9 → backlogops-1.0}/src/backlogops/config_file_io.py +0 -0
  28. {backlogops-0.9 → backlogops-1.0}/src/backlogops/date_ranges.py +0 -0
  29. {backlogops-0.9 → backlogops-1.0}/src/backlogops/demo_backlog.py +0 -0
  30. {backlogops-0.9 → backlogops-1.0}/src/backlogops/estimate_ready_date.py +0 -0
  31. {backlogops-0.9 → backlogops-1.0}/src/backlogops/format_rules.py +0 -0
  32. {backlogops-0.9 → backlogops-1.0}/src/backlogops/io_config.py +0 -0
  33. {backlogops-0.9 → backlogops-1.0}/src/backlogops/io_preset_wizard.py +0 -0
  34. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_connect.py +0 -0
  35. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_io_config.py +0 -0
  36. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_order_releases.py +0 -0
  37. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rank_backlog.py +0 -0
  38. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rank_by_keys.py +0 -0
  39. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rank_move_keys.py +0 -0
  40. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_read.py +0 -0
  41. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_rename_releases.py +0 -0
  42. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_search.py +0 -0
  43. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_token.py +0 -0
  44. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_update_backlog.py +0 -0
  45. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_update_releases.py +0 -0
  46. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_wizard.py +0 -0
  47. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write.py +0 -0
  48. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write_fields.py +0 -0
  49. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write_format.py +0 -0
  50. {backlogops-0.9 → backlogops-1.0}/src/backlogops/jira_write_releases.py +0 -0
  51. {backlogops-0.9 → backlogops-1.0}/src/backlogops/key_list_io.py +0 -0
  52. {backlogops-0.9 → backlogops-1.0}/src/backlogops/levels.py +0 -0
  53. {backlogops-0.9 → backlogops-1.0}/src/backlogops/move_keys_first.py +0 -0
  54. {backlogops-0.9 → backlogops-1.0}/src/backlogops/name_list_io.py +0 -0
  55. {backlogops-0.9 → backlogops-1.0}/src/backlogops/no_text_io.py +0 -0
  56. {backlogops-0.9 → backlogops-1.0}/src/backlogops/order_by_dependencies.py +0 -0
  57. {backlogops-0.9 → backlogops-1.0}/src/backlogops/person.py +0 -0
  58. {backlogops-0.9 → backlogops-1.0}/src/backlogops/py.typed +0 -0
  59. {backlogops-0.9 → backlogops-1.0}/src/backlogops/release_backlog_updates.py +0 -0
  60. {backlogops-0.9 → backlogops-1.0}/src/backlogops/release_change_io.py +0 -0
  61. {backlogops-0.9 → backlogops-1.0}/src/backlogops/releases.py +0 -0
  62. {backlogops-0.9 → backlogops-1.0}/src/backlogops/rename_list_io.py +0 -0
  63. {backlogops-0.9 → backlogops-1.0}/src/backlogops/table_create.py +0 -0
  64. {backlogops-0.9 → backlogops-1.0}/src/backlogops/table_rows.py +0 -0
  65. {backlogops-0.9 → backlogops-1.0}/src/backlogops/team.py +0 -0
  66. {backlogops-0.9 → backlogops-1.0}/src/backlogops/wizard_forms.py +0 -0
  67. {backlogops-0.9 → backlogops-1.0}/src/backlogops/wizard_helpers.py +0 -0
  68. {backlogops-0.9 → backlogops-1.0}/src/backlogops/wizard_navigator.py +0 -0
  69. {backlogops-0.9 → backlogops-1.0}/src/backlogops/work_hours.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops
3
- Version: 0.9
3
+ Version: 1.0
4
4
  Summary: Library with backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -25,10 +25,10 @@ Requires-Dist: config-as-json>=1.5
25
25
  Requires-Dist: cryptography>=50.0.0
26
26
  Requires-Dist: jira[cli,opt]>=3.10.5
27
27
  Requires-Dist: tableio>=1.1
28
- Requires-Dist: tableio-cfg-json>=1.2
28
+ Requires-Dist: tableio-cfg-json>=1.3
29
29
  Requires-Dist: wizard-ui-bridge[textual]>=1.3
30
- Requires-Dist: edit-cfg-json==0.0.2
31
30
  Requires-Dist: versionreporter>=0.4
31
+ Requires-Dist: edit-cfg-json==0.0.4
32
32
  Dynamic: author
33
33
  Dynamic: license-file
34
34
  Dynamic: requires-dist
@@ -271,15 +271,12 @@ from backlogops import (
271
271
 
272
272
  ### Editing a configuration
273
273
 
274
- - `edit_model_for`, `default_edit_config`: build the `edit-cfg-json` model
275
- of an editing session over a `BacklogOpsConfig` or a stand-alone input
276
- or output preset, for the Tkinter editor of the graphical interface and
277
- the Textual editor of the command line.
278
-
279
274
  - `CONFIG_DESCRIPTIONS`, `INPUT_DESCRIPTIONS`, `OUTPUT_DESCRIPTIONS`:
280
275
  what each configuration member is for, shown below it in the editor.
281
276
  A member has no docstring at runtime, so this is what the editor is
282
277
  told; `descriptions_for` picks the mapping of one configuration class.
278
+ The nested TableIO endpoint is described by `tableio-cfg-json`, which
279
+ is asked for that text rather than having it repeated here.
283
280
 
284
281
  - `EDIT_SETTINGS`: what the editor may do to a configuration file of this
285
282
  application, including keeping what it writes over as a `.bak` file.
@@ -328,10 +325,10 @@ For the full set of public names see the API documentation linked above.
328
325
 
329
326
  ## Test summary
330
327
 
331
- - Test result: 2232 passed, 1 deselected in 44s
328
+ - Test result: 2234 passed, 1 deselected in 45s
332
329
  - No flake8 warnings.
333
330
  - No mypy errors found.
334
331
  - No pylint warnings.
335
332
  - No python layout warnings.
336
- - Built version(s): 0.9
333
+ - Built version(s): 1.0
337
334
  - Build and test using Python 3.14.7
@@ -235,15 +235,12 @@ from backlogops import (
235
235
 
236
236
  ### Editing a configuration
237
237
 
238
- - `edit_model_for`, `default_edit_config`: build the `edit-cfg-json` model
239
- of an editing session over a `BacklogOpsConfig` or a stand-alone input
240
- or output preset, for the Tkinter editor of the graphical interface and
241
- the Textual editor of the command line.
242
-
243
238
  - `CONFIG_DESCRIPTIONS`, `INPUT_DESCRIPTIONS`, `OUTPUT_DESCRIPTIONS`:
244
239
  what each configuration member is for, shown below it in the editor.
245
240
  A member has no docstring at runtime, so this is what the editor is
246
241
  told; `descriptions_for` picks the mapping of one configuration class.
242
+ The nested TableIO endpoint is described by `tableio-cfg-json`, which
243
+ is asked for that text rather than having it repeated here.
247
244
 
248
245
  - `EDIT_SETTINGS`: what the editor may do to a configuration file of this
249
246
  application, including keeping what it writes over as a `.bak` file.
@@ -292,10 +289,10 @@ For the full set of public names see the API documentation linked above.
292
289
 
293
290
  ## Test summary
294
291
 
295
- - Test result: 2232 passed, 1 deselected in 44s
292
+ - Test result: 2234 passed, 1 deselected in 45s
296
293
  - No flake8 warnings.
297
294
  - No mypy errors found.
298
295
  - No pylint warnings.
299
296
  - No python layout warnings.
300
- - Built version(s): 0.9
297
+ - Built version(s): 1.0
301
298
  - Build and test using Python 3.14.7
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backlogops
3
- Version: 0.9
3
+ Version: 1.0
4
4
  Summary: Library with backlog operations.
5
5
  Author: Tom Björkholm
6
6
  Author-email: Tom Björkholm <klausuler_linnet0q@icloud.com>
@@ -25,10 +25,10 @@ Requires-Dist: config-as-json>=1.5
25
25
  Requires-Dist: cryptography>=50.0.0
26
26
  Requires-Dist: jira[cli,opt]>=3.10.5
27
27
  Requires-Dist: tableio>=1.1
28
- Requires-Dist: tableio-cfg-json>=1.2
28
+ Requires-Dist: tableio-cfg-json>=1.3
29
29
  Requires-Dist: wizard-ui-bridge[textual]>=1.3
30
- Requires-Dist: edit-cfg-json==0.0.2
31
30
  Requires-Dist: versionreporter>=0.4
31
+ Requires-Dist: edit-cfg-json==0.0.4
32
32
  Dynamic: author
33
33
  Dynamic: license-file
34
34
  Dynamic: requires-dist
@@ -271,15 +271,12 @@ from backlogops import (
271
271
 
272
272
  ### Editing a configuration
273
273
 
274
- - `edit_model_for`, `default_edit_config`: build the `edit-cfg-json` model
275
- of an editing session over a `BacklogOpsConfig` or a stand-alone input
276
- or output preset, for the Tkinter editor of the graphical interface and
277
- the Textual editor of the command line.
278
-
279
274
  - `CONFIG_DESCRIPTIONS`, `INPUT_DESCRIPTIONS`, `OUTPUT_DESCRIPTIONS`:
280
275
  what each configuration member is for, shown below it in the editor.
281
276
  A member has no docstring at runtime, so this is what the editor is
282
277
  told; `descriptions_for` picks the mapping of one configuration class.
278
+ The nested TableIO endpoint is described by `tableio-cfg-json`, which
279
+ is asked for that text rather than having it repeated here.
283
280
 
284
281
  - `EDIT_SETTINGS`: what the editor may do to a configuration file of this
285
282
  application, including keeping what it writes over as a `.bak` file.
@@ -328,10 +325,10 @@ For the full set of public names see the API documentation linked above.
328
325
 
329
326
  ## Test summary
330
327
 
331
- - Test result: 2232 passed, 1 deselected in 44s
328
+ - Test result: 2234 passed, 1 deselected in 45s
332
329
  - No flake8 warnings.
333
330
  - No mypy errors found.
334
331
  - No pylint warnings.
335
332
  - No python layout warnings.
336
- - Built version(s): 0.9
333
+ - Built version(s): 1.0
337
334
  - Build and test using Python 3.14.7
@@ -2,7 +2,7 @@ config-as-json>=1.5
2
2
  cryptography>=50.0.0
3
3
  jira[cli,opt]>=3.10.5
4
4
  tableio>=1.1
5
- tableio-cfg-json>=1.2
5
+ tableio-cfg-json>=1.3
6
6
  wizard-ui-bridge[textual]>=1.3
7
- edit-cfg-json==0.0.2
8
7
  versionreporter>=0.4
8
+ edit-cfg-json==0.0.4
@@ -5,7 +5,7 @@ from setuptools import setup
5
5
 
6
6
  setup(
7
7
  name='backlogops',
8
- version='0.9',
8
+ version='1.0',
9
9
  description='Library with backlog operations.',
10
10
  author='Tom Björkholm',
11
11
  author_email='klausuler_linnet0q@icloud.com',
@@ -18,9 +18,9 @@ setup(
18
18
  'cryptography >= 50.0.0',
19
19
  'jira[cli,opt] >= 3.10.5',
20
20
  'tableio >= 1.1',
21
- 'tableio-cfg-json >= 1.2',
21
+ 'tableio-cfg-json >= 1.3',
22
22
  'wizard-ui-bridge[textual] >= 1.3',
23
- 'edit-cfg-json == 0.0.2',
24
- 'versionreporter >= 0.4'
23
+ 'versionreporter >= 0.4',
24
+ 'edit-cfg-json == 0.0.4'
25
25
  ]
26
26
  )
@@ -32,8 +32,7 @@ from backlogops.backlog_releases_io import (
32
32
  from backlogops.config_descriptions import (
33
33
  CONFIG_DESCRIPTIONS, GUI_DESCRIPTIONS, INPUT_DESCRIPTIONS,
34
34
  JIRA_DESCRIPTIONS, OUTPUT_DESCRIPTIONS, WORKFORCE_DESCRIPTIONS)
35
- from backlogops.config_editing import (
36
- EDIT_SETTINGS, default_edit_config, descriptions_for, edit_model_for)
35
+ from backlogops.config_editing import EDIT_SETTINGS, descriptions_for
37
36
  from backlogops.config_file_io import (
38
37
  CONFIG_EXTENSION, io_preset_class, read_io_preset, safe_write_config)
39
38
  from backlogops.date_ranges import check_date_range, check_no_overlap
@@ -141,9 +140,9 @@ __all__ = [
141
140
  'apply_jira_keys', 'available_teams_wizard', 'backlog_in_release_order',
142
141
  'backlog_ops_wizard', 'build_backlog_releases', 'build_dependency_graph',
143
142
  'check_backlog_consistency', 'check_date_range',
144
- 'check_levels_consistency', 'check_no_overlap', 'default_edit_config',
143
+ 'check_levels_consistency', 'check_no_overlap',
145
144
  'default_jira_filter', 'descriptions_for',
146
- 'display_level_order', 'display_level_rows', 'edit_model_for',
145
+ 'display_level_order', 'display_level_rows',
147
146
  'encrypt_token_file',
148
147
  'encrypt_token_to_file', 'estimate_ready_date', 'estimate_release_dates',
149
148
  'event_finish', 'event_start', 'find_cycle', 'fold_level_name',
@@ -40,11 +40,15 @@ more names than fit in one such line has the meaning of them said about the
40
40
  member instead, which is why ``token_storage`` explains its modes here and
41
41
  ``level_display`` does not.
42
42
 
43
- Two more things are deliberately left out. The nested TableIO endpoint is
44
- described by ``tableio_cfg_json``, which owns those members and documents
45
- them itself, so only the member holding it is described here. And a limit
46
- that lives inside a validator is not read by the editor and is stated in
47
- words where it matters.
43
+ The nested TableIO endpoint is described by ``tableio_cfg_json``, which
44
+ owns those members: :func:`tio_json_descriptions` is asked for their text
45
+ under the path of the member holding the endpoint, so the TableIO
46
+ documentation is neither repeated here nor able to drift from it. What is
47
+ written here about that endpoint is the one line about the member itself.
48
+
49
+ One more thing is deliberately left out: a limit that lives inside a
50
+ validator is not read by the editor and is stated in words where it
51
+ matters.
48
52
  """
49
53
 
50
54
  # Copyright (c) 2026, Tom Björkholm
@@ -52,6 +56,7 @@ words where it matters.
52
56
 
53
57
  from config_as_json import ConfigPath
54
58
  from edit_cfg_json import Descriptions
59
+ from tableio_cfg_json import tio_json_descriptions
55
60
 
56
61
  EVERY = '['
57
62
  """The path step meaning every element of a list or every value of a dict.
@@ -169,12 +174,17 @@ WORKFORCE_DESCRIPTIONS: Descriptions = {
169
174
  _FTE_EXCEPTION)}
170
175
  """What every member of an ``AvailableTeamsConfig`` is for."""
171
176
 
172
- _TABLEIO_MEMBER = ('How the file itself is read or written: its format, and '
173
- 'the settings of that format. These are the members of '
174
- 'tableio-cfg-json, which documents them; a setting that '
175
- 'is not in the file keeps its default and has no line '
176
- 'here.')
177
- """What the nested TableIO endpoint of one preset is for."""
177
+ _TABLEIO: Descriptions = {
178
+ ('tableio',): 'How the file itself is read or written: its format, and '
179
+ 'the settings of that format. A setting that is not in '
180
+ 'the file keeps its default and has no line here.',
181
+ **tio_json_descriptions(('tableio',))}
182
+ """What the nested TableIO endpoint of one preset and its settings are for.
183
+
184
+ Only the line about the member itself is written here. Everything below it
185
+ belongs to ``tableio_cfg_json``, which is asked for it, so the formats,
186
+ implementations and values named are the ones registered now.
187
+ """
178
188
 
179
189
  _MAPPED_COLUMN = ('Empty drops the column altogether. A column that is not '
180
190
  'named here keeps its own name.')
@@ -198,7 +208,7 @@ INPUT_DESCRIPTIONS: Descriptions = {
198
208
  'alone.',
199
209
  ('status_input_map', EVERY): 'Internal status this name is read as: one '
200
210
  'of TODO, IN_PROGRESS, DONE or REJECTED.',
201
- ('tableio',): _TABLEIO_MEMBER}
211
+ **_TABLEIO}
202
212
  """What every member of an ``InputFormatConfig`` is for."""
203
213
 
204
214
 
@@ -231,7 +241,7 @@ def _display_members(action: str) -> Descriptions:
231
241
 
232
242
 
233
243
  OUTPUT_DESCRIPTIONS: Descriptions = {**_display_members('written'),
234
- ('tableio',): _TABLEIO_MEMBER}
244
+ **_TABLEIO}
235
245
  """What every member of an ``OutputFormatConfig`` is for."""
236
246
 
237
247
  GUI_DESCRIPTIONS: Descriptions = _display_members('shown')
@@ -0,0 +1,90 @@
1
+ #! /usr/local/bin/python3
2
+ """What a configuration editor of a UI is told about this application.
3
+
4
+ The wizard asks one question after another, which is what building a
5
+ configuration from nothing wants. Editing an existing one wants the other
6
+ shape: the whole configuration on the screen at once, folded where it is
7
+ deep, so a single value can be changed without walking past everything
8
+ else. ``edit_cfg_json`` is that editor, and this module is the two answers
9
+ it asks the application for, so that the command line and the graphical
10
+ interface show one configuration the same way.
11
+
12
+ The first answer is :func:`descriptions_for`, which is what each editable
13
+ class says about its own members. Which class is edited is the class of the
14
+ configuration object the editor is given, so the caller decides whether a
15
+ complete backlog-ops configuration or a stand-alone preset is being edited,
16
+ and the descriptions of that class follow from it. A caller that has a
17
+ class rather than an object, such as a command line naming the kind of file
18
+ it edits, gets the object from ``edit_cfg_json.default_config``.
19
+
20
+ The second answer is :data:`EDIT_SETTINGS`, which is what the editor may do
21
+ to a file. Saving is the editor's own: it validates the whole configuration
22
+ through the configuration class and only then writes, keeping what it wrote
23
+ over as a ``.bak`` file, because an editor overwrites the file it read.
24
+
25
+ Nothing here opens an editor. A user interface that already runs its own
26
+ toolkit mounts the editor itself — ``edit_cfg_json_tk.TkEditorPanel`` in a
27
+ window of the application, ``edit_cfg_json_textual.edit`` in a terminal of
28
+ its own — and hands those two answers to it.
29
+
30
+ Three things the editor cannot do are worth knowing before it is offered
31
+ instead of the wizard, and all three are of ``edit_cfg_json`` itself rather
32
+ than of this configuration:
33
+
34
+ * A dict whose keys the application validates for itself cannot gain or
35
+ lose a key. That is the status map, the column-name maps of a preset and
36
+ of the display, and the Jira column and issue-type maps: their values are
37
+ editable, and a new entry is the wizard's to create.
38
+ * A member left out of the file has no row, so the levels can be edited
39
+ only in a configuration that already states them.
40
+ * A person is keyed by their own name in lower case, so renaming one means
41
+ adding an entry under the new key and removing the old one; editing the
42
+ name alone leaves a configuration the class refuses.
43
+ """
44
+
45
+ # Copyright (c) 2026, Tom Björkholm
46
+ # MIT License
47
+
48
+ from typing import Optional
49
+ from config_as_json import Config
50
+ from edit_cfg_json import Descriptions, Settings
51
+ from backlogops.backlog_ops_config import BacklogOpsConfig
52
+ from backlogops.config_descriptions import CONFIG_DESCRIPTIONS, \
53
+ INPUT_DESCRIPTIONS, OUTPUT_DESCRIPTIONS
54
+ from backlogops.config_file_io import CONFIG_EXTENSION
55
+ from backlogops.io_config import InputFormatConfig, OutputFormatConfig
56
+
57
+ EDIT_SETTINGS = Settings(file_extension=CONFIG_EXTENSION,
58
+ extension_enforced=False, backup_suffix='.bak',
59
+ backup_count=1, confirm_overwrite=True)
60
+ """What the editor may do to a configuration file of this application.
61
+
62
+ The extension is added to a destination that has none, and a name with
63
+ another extension is accepted, which is how the wizard commands complete a
64
+ file name too. Overwriting a file this session has not written is confirmed
65
+ first and the previous content is then kept as that name plus ``.bak``,
66
+ because the editor writes over the file it read and that file may hold a
67
+ configuration somebody else wrote.
68
+
69
+ The default key combinations apply: none of them is taken by the
70
+ application around the editor.
71
+ """
72
+
73
+ CLASS_DESCRIPTIONS: dict[type[Config], Descriptions] = {
74
+ BacklogOpsConfig: CONFIG_DESCRIPTIONS,
75
+ InputFormatConfig: INPUT_DESCRIPTIONS,
76
+ OutputFormatConfig: OUTPUT_DESCRIPTIONS}
77
+ """What each editable configuration class says about its own members."""
78
+
79
+
80
+ def descriptions_for(config: Config) -> Optional[Descriptions]:
81
+ """Return what the class of one configuration says about its members.
82
+
83
+ Args:
84
+ config: The configuration object that is to be edited.
85
+
86
+ Returns:
87
+ The descriptions of that class, or None for a class this library
88
+ says nothing about, which the editor shows without descriptions.
89
+ """
90
+ return CLASS_DESCRIPTIONS.get(type(config))
@@ -1,149 +0,0 @@
1
- #! /usr/local/bin/python3
2
- """Build the edit model that a configuration editor of a UI shows.
3
-
4
- The wizard asks one question after another, which is what building a
5
- configuration from nothing wants. Editing an existing one wants the other
6
- shape: the whole configuration on the screen at once, folded where it is
7
- deep, so a single value can be changed without walking past everything
8
- else. ``edit_cfg_json`` is that editor, and this module is everything of it
9
- that is not a widget, so that the command line and the graphical interface
10
- show one configuration the same way.
11
-
12
- :func:`edit_model_for` reads the input file and returns the model to show.
13
- Which class is edited is the class of the configuration object it is given,
14
- so the caller decides whether a complete backlog-ops configuration or a
15
- stand-alone preset is being edited, and the descriptions of that class
16
- follow from it. A caller that has a class rather than an object, such as a
17
- command line naming the kind of file it edits, gets the object from
18
- :func:`default_edit_config`.
19
-
20
- What the editor may do to a file is :data:`EDIT_SETTINGS`. Saving is the
21
- editor's own: it validates the whole configuration through the
22
- configuration class and only then writes, keeping what it wrote over as a
23
- ``.bak`` file, because an editor overwrites the file it read.
24
-
25
- Three things the editor cannot do are worth knowing before it is offered
26
- instead of the wizard, and all three are of ``edit_cfg_json`` itself rather
27
- than of this configuration:
28
-
29
- * A dict whose keys the application validates for itself cannot gain or
30
- lose a key. That is the status map, the column-name maps of a preset and
31
- of the display, and the Jira column and issue-type maps: their values are
32
- editable, and a new entry is the wizard's to create.
33
- * A member left out of the file has no row, so the levels can be edited
34
- only in a configuration that already states them.
35
- * A person is keyed by their own name in lower case, so renaming one means
36
- adding an entry under the new key and removing the old one; editing the
37
- name alone leaves a configuration the class refuses.
38
- """
39
-
40
- # Copyright (c) 2026, Tom Björkholm
41
- # MIT License
42
-
43
- import sys
44
- from typing import Optional, TextIO
45
- from config_as_json import Config, PathOrStr
46
- from edit_cfg_json import ConfigLoadError, Descriptions, EditModel, Settings, \
47
- default_config, load_config
48
- from backlogops.backlog_ops_config import BacklogOpsConfig
49
- from backlogops.config_descriptions import CONFIG_DESCRIPTIONS, \
50
- INPUT_DESCRIPTIONS, OUTPUT_DESCRIPTIONS
51
- from backlogops.config_file_io import CONFIG_EXTENSION
52
- from backlogops.io_config import InputFormatConfig, OutputFormatConfig
53
-
54
- EDIT_SETTINGS = Settings(file_extension=CONFIG_EXTENSION,
55
- extension_enforced=False, backup_suffix='.bak',
56
- backup_count=1, confirm_overwrite=True)
57
- """What the editor may do to a configuration file of this application.
58
-
59
- The extension is added to a destination that has none, and a name with
60
- another extension is accepted, which is how the wizard commands complete a
61
- file name too. Overwriting a file this session has not written is confirmed
62
- first and the previous content is then kept as that name plus ``.bak``,
63
- because the editor writes over the file it read and that file may hold a
64
- configuration somebody else wrote.
65
-
66
- The default key combinations apply: none of them is taken by the
67
- application around the editor.
68
- """
69
-
70
- CLASS_DESCRIPTIONS: dict[type[Config], Descriptions] = {
71
- BacklogOpsConfig: CONFIG_DESCRIPTIONS,
72
- InputFormatConfig: INPUT_DESCRIPTIONS,
73
- OutputFormatConfig: OUTPUT_DESCRIPTIONS}
74
- """What each editable configuration class says about its own members."""
75
-
76
-
77
- def descriptions_for(config: Config) -> Optional[Descriptions]:
78
- """Return what the class of one configuration says about its members.
79
-
80
- Args:
81
- config: The configuration object that is to be edited.
82
-
83
- Returns:
84
- The descriptions of that class, or None for a class this library
85
- says nothing about, which the editor shows without descriptions.
86
- """
87
- return CLASS_DESCRIPTIONS.get(type(config))
88
-
89
-
90
- def default_edit_config(config_type: type[Config]) -> Config:
91
- """Return a configuration holding the declared defaults of one class.
92
-
93
- It is the door for a caller that has a class rather than an object,
94
- which is what a command line naming the kind of file it edits has.
95
-
96
- Args:
97
- config_type: The configuration class to be edited.
98
-
99
- Returns:
100
- A configuration object holding only what that class declares.
101
-
102
- Raises:
103
- ValueError: The editor cannot construct that class on its own.
104
- """
105
- try:
106
- return default_config(config_type)
107
- except ConfigLoadError as error:
108
- raise ValueError(str(error)) from error
109
-
110
-
111
- def edit_model_for(config: Config, *, in_file: Optional[PathOrStr] = None,
112
- out_file: Optional[PathOrStr] = None,
113
- stderr_file: TextIO = sys.stderr) -> EditModel:
114
- """Read the configuration to edit and return the model of a session.
115
-
116
- The class of ``config`` decides which class is edited and supplies the
117
- values a member the input file leaves out falls back to. The object
118
- itself is never modified: what a save wrote is
119
- ``EditModel.saved_config``.
120
-
121
- Args:
122
- config: Configuration object of the class to edit, holding the
123
- values to start from when there is no input file.
124
- in_file: Configuration file to read, or None to edit the values
125
- that ``config`` holds.
126
- out_file: Configuration file a save writes, or None to write the
127
- input file. With neither, the editor asks the user for one
128
- before it can save. A destination named here is one this
129
- session chose, so it is given the configuration extension when
130
- it has none; the input file is inherited and taken as it is.
131
- stderr_file: Stream used for user-facing diagnostics.
132
-
133
- Returns:
134
- The model of one editing session, for a UI backend to show.
135
-
136
- Raises:
137
- ValueError: The input file cannot be opened for editing. The
138
- message holds what the configuration class said about it.
139
- """
140
- try:
141
- loaded = load_config(config, in_file=in_file, settings=EDIT_SETTINGS)
142
- except ConfigLoadError as error:
143
- raise ValueError(str(error)) from error
144
- model = EditModel(loaded.config, loaded.report,
145
- descriptions=descriptions_for(config), out_file=in_file,
146
- settings=EDIT_SETTINGS, stderr_file=stderr_file)
147
- if out_file is not None:
148
- model.set_out_file(out_file)
149
- return model
File without changes
File without changes
File without changes