simplibs-extractor 0.1.0__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.
- simplibs/extractor/__init__.py +121 -0
- simplibs/extractor/__main__.py +12 -0
- simplibs/extractor/annotations/FileStatus.py +16 -0
- simplibs/extractor/annotations/OutputFormat.py +25 -0
- simplibs/extractor/annotations/OverwritePolicy.py +24 -0
- simplibs/extractor/annotations/StepsSpec.py +26 -0
- simplibs/extractor/annotations/__init__.py +26 -0
- simplibs/extractor/annotations/_helpers/__init__.py +15 -0
- simplibs/extractor/annotations/_helpers/is_output_format.py +36 -0
- simplibs/extractor/cli/__init__.py +32 -0
- simplibs/extractor/cli/_helpers/__init__.py +22 -0
- simplibs/extractor/cli/_helpers/describe_result.py +34 -0
- simplibs/extractor/cli/_helpers/is_profile_reference.py +34 -0
- simplibs/extractor/cli/_helpers/print_error.py +25 -0
- simplibs/extractor/cli/_helpers/profile_options.py +25 -0
- simplibs/extractor/cli/_helpers/system_options.py +22 -0
- simplibs/extractor/cli/_validations/__init__.py +22 -0
- simplibs/extractor/cli/_validations/raise_duplicate_output_error.py +32 -0
- simplibs/extractor/cli/_validations/raise_list_unknown_system_error.py +34 -0
- simplibs/extractor/cli/_validations/raise_output_is_file_error.py +30 -0
- simplibs/extractor/cli/_validations/raise_too_many_positionals_error.py +37 -0
- simplibs/extractor/cli/_validations/raise_unknown_system_error.py +32 -0
- simplibs/extractor/cli/build_configs.py +77 -0
- simplibs/extractor/cli/build_parser.py +68 -0
- simplibs/extractor/cli/collect_dry_run.py +39 -0
- simplibs/extractor/cli/collect_profile_keys.py +53 -0
- simplibs/extractor/cli/collect_source.py +44 -0
- simplibs/extractor/cli/handle_list_options.py +47 -0
- simplibs/extractor/cli/normalize_positionals.py +56 -0
- simplibs/extractor/cli/run_cli.py +101 -0
- simplibs/extractor/cli/run_init.py +65 -0
- simplibs/extractor/constants/DEFAULT_MAX_FILE_SIZE.py +18 -0
- simplibs/extractor/constants/DEFAULT_OUTPUT_NAME.py +14 -0
- simplibs/extractor/constants/DEFAULT_PROFILE_KEY.py +14 -0
- simplibs/extractor/constants/DEFAULT_SCRIPT_NAME.py +15 -0
- simplibs/extractor/constants/GENERATED_MARKER.py +20 -0
- simplibs/extractor/constants/LANGUAGE_BY_SUFFIX.py +34 -0
- simplibs/extractor/constants/MARKER_SCAN_LINES.py +18 -0
- simplibs/extractor/constants/PLUGIN_GROUP.py +20 -0
- simplibs/extractor/constants/SCRIPT_TEMPLATE.py +32 -0
- simplibs/extractor/constants/__init__.py +30 -0
- simplibs/extractor/definitions/Entry.py +26 -0
- simplibs/extractor/definitions/ExtractionConfig.py +75 -0
- simplibs/extractor/definitions/ExtractionResult.py +74 -0
- simplibs/extractor/definitions/FileReport.py +39 -0
- simplibs/extractor/definitions/Profile.py +95 -0
- simplibs/extractor/definitions/Resolved.py +28 -0
- simplibs/extractor/definitions/__init__.py +24 -0
- simplibs/extractor/definitions/_helpers/__init__.py +16 -0
- simplibs/extractor/definitions/_helpers/is_key_part.py +31 -0
- simplibs/extractor/definitions/_helpers/is_suffix_key.py +35 -0
- simplibs/extractor/definitions/_validations/__init__.py +18 -0
- simplibs/extractor/definitions/_validations/raise_profile_key_part_error.py +34 -0
- simplibs/extractor/definitions/_validations/raise_profile_output_name_error.py +33 -0
- simplibs/extractor/definitions/_validations/raise_profile_steps_key_error.py +33 -0
- simplibs/extractor/logic/__init__.py +90 -0
- simplibs/extractor/logic/_common/__init__.py +15 -0
- simplibs/extractor/logic/_common/describe_failure.py +39 -0
- simplibs/extractor/logic/_helpers/RunLog.py +57 -0
- simplibs/extractor/logic/_helpers/__init__.py +18 -0
- simplibs/extractor/logic/_helpers/build_title.py +28 -0
- simplibs/extractor/logic/_helpers/process_source.py +59 -0
- simplibs/extractor/logic/extract.py +119 -0
- simplibs/extractor/logic/extract_many.py +60 -0
- simplibs/extractor/logic/failure_result.py +47 -0
- simplibs/extractor/logic/read/__init__.py +16 -0
- simplibs/extractor/logic/read/is_generated_file.py +45 -0
- simplibs/extractor/logic/read/read_source.py +93 -0
- simplibs/extractor/logic/render/RenderContext.py +34 -0
- simplibs/extractor/logic/render/__init__.py +31 -0
- simplibs/extractor/logic/render/_helpers/__init__.py +14 -0
- simplibs/extractor/logic/render/_helpers/format_tree_lines.py +35 -0
- simplibs/extractor/logic/render/build_tree.py +36 -0
- simplibs/extractor/logic/render/register_builtin_renderers.py +26 -0
- simplibs/extractor/logic/render/render_markdown.py +84 -0
- simplibs/extractor/logic/render/render_plain.py +57 -0
- simplibs/extractor/logic/report/__init__.py +16 -0
- simplibs/extractor/logic/report/_validations/__init__.py +14 -0
- simplibs/extractor/logic/report/_validations/raise_chars_per_token_error.py +29 -0
- simplibs/extractor/logic/report/estimate_tokens.py +47 -0
- simplibs/extractor/logic/report/format_summary.py +67 -0
- simplibs/extractor/logic/select/__init__.py +24 -0
- simplibs/extractor/logic/select/_validations/__init__.py +18 -0
- simplibs/extractor/logic/select/_validations/raise_missing_source_error.py +26 -0
- simplibs/extractor/logic/select/_validations/raise_source_not_found_error.py +37 -0
- simplibs/extractor/logic/select/_validations/raise_unknown_parameter_error.py +35 -0
- simplibs/extractor/logic/select/all_of.py +32 -0
- simplibs/extractor/logic/select/any_of.py +29 -0
- simplibs/extractor/logic/select/resolve_config.py +85 -0
- simplibs/extractor/logic/select/resolve_ignore_names.py +36 -0
- simplibs/extractor/logic/select/resolve_profile.py +38 -0
- simplibs/extractor/logic/select/select_sources.py +85 -0
- simplibs/extractor/logic/transform/__init__.py +16 -0
- simplibs/extractor/logic/transform/_validations/__init__.py +14 -0
- simplibs/extractor/logic/transform/_validations/raise_step_result_error.py +42 -0
- simplibs/extractor/logic/transform/apply_steps.py +57 -0
- simplibs/extractor/logic/transform/steps_for.py +40 -0
- simplibs/extractor/logic/write/__init__.py +14 -0
- simplibs/extractor/logic/write/_validations/__init__.py +16 -0
- simplibs/extractor/logic/write/_validations/raise_target_exists_error.py +34 -0
- simplibs/extractor/logic/write/_validations/raise_target_is_dir_error.py +34 -0
- simplibs/extractor/logic/write/write_output.py +77 -0
- simplibs/extractor/plugins/__init__.py +14 -0
- simplibs/extractor/plugins/load_plugins.py +64 -0
- simplibs/extractor/py.typed +1 -0
- simplibs/extractor/registry/__init__.py +37 -0
- simplibs/extractor/registry/_helpers/GENERIC_ALL.py +24 -0
- simplibs/extractor/registry/_helpers/PROFILE_REGISTRY.py +21 -0
- simplibs/extractor/registry/_helpers/RENDERER_REGISTRY.py +21 -0
- simplibs/extractor/registry/_helpers/__init__.py +18 -0
- simplibs/extractor/registry/_validations/__init__.py +24 -0
- simplibs/extractor/registry/_validations/raise_missing_system_error.py +33 -0
- simplibs/extractor/registry/_validations/raise_no_profile_given_error.py +33 -0
- simplibs/extractor/registry/_validations/raise_profile_exists_error.py +33 -0
- simplibs/extractor/registry/_validations/raise_profile_not_found_error.py +45 -0
- simplibs/extractor/registry/_validations/raise_renderer_exists_error.py +30 -0
- simplibs/extractor/registry/_validations/raise_unknown_renderer_error.py +37 -0
- simplibs/extractor/registry/expand_profile_keys.py +60 -0
- simplibs/extractor/registry/get_profile.py +39 -0
- simplibs/extractor/registry/get_renderer.py +40 -0
- simplibs/extractor/registry/list_formats.py +20 -0
- simplibs/extractor/registry/list_profiles.py +32 -0
- simplibs/extractor/registry/list_systems.py +20 -0
- simplibs/extractor/registry/register_profile.py +46 -0
- simplibs/extractor/registry/register_renderer.py +47 -0
- simplibs/extractor/scripts/__init__.py +18 -0
- simplibs/extractor/scripts/_helpers/__init__.py +14 -0
- simplibs/extractor/scripts/_helpers/print_result.py +32 -0
- simplibs/extractor/scripts/_validations/__init__.py +16 -0
- simplibs/extractor/scripts/_validations/raise_invalid_output_name_error.py +30 -0
- simplibs/extractor/scripts/_validations/raise_not_python_script_error.py +30 -0
- simplibs/extractor/scripts/extract_here.py +93 -0
- simplibs/extractor/scripts/generate_local_script.py +65 -0
- simplibs/extractor/scripts/render_script.py +26 -0
- simplibs_extractor-0.1.0.dist-info/METADATA +605 -0
- simplibs_extractor-0.1.0.dist-info/RECORD +140 -0
- simplibs_extractor-0.1.0.dist-info/WHEEL +5 -0
- simplibs_extractor-0.1.0.dist-info/entry_points.txt +2 -0
- simplibs_extractor-0.1.0.dist-info/licenses/LICENSE +21 -0
- simplibs_extractor-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Running an extraction
|
|
2
|
+
from .logic import extract, extract_many
|
|
3
|
+
from .scripts import extract_here, generate_local_script
|
|
4
|
+
|
|
5
|
+
# Profiles and formats
|
|
6
|
+
from .registry import (
|
|
7
|
+
expand_profile_keys,
|
|
8
|
+
get_profile,
|
|
9
|
+
get_renderer,
|
|
10
|
+
list_formats,
|
|
11
|
+
list_profiles,
|
|
12
|
+
list_systems,
|
|
13
|
+
register_profile,
|
|
14
|
+
register_renderer,
|
|
15
|
+
)
|
|
16
|
+
from .plugins import load_plugins
|
|
17
|
+
|
|
18
|
+
# Data objects
|
|
19
|
+
from .definitions import Entry, ExtractionConfig, ExtractionResult, FileReport, Profile
|
|
20
|
+
from .logic import RenderContext
|
|
21
|
+
|
|
22
|
+
# Reporting
|
|
23
|
+
from .logic import estimate_tokens, format_summary
|
|
24
|
+
|
|
25
|
+
# Annotations and constants
|
|
26
|
+
from .annotations import FileStatus, OutputFormat, OverwritePolicy, StepsSpec
|
|
27
|
+
from .constants import DEFAULT_PROFILE_KEY, GENERATED_MARKER
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
# Running an extraction
|
|
32
|
+
"extract",
|
|
33
|
+
"extract_many",
|
|
34
|
+
"extract_here",
|
|
35
|
+
"generate_local_script",
|
|
36
|
+
# Profiles and formats
|
|
37
|
+
"expand_profile_keys",
|
|
38
|
+
"get_profile",
|
|
39
|
+
"get_renderer",
|
|
40
|
+
"list_formats",
|
|
41
|
+
"list_profiles",
|
|
42
|
+
"list_systems",
|
|
43
|
+
"register_profile",
|
|
44
|
+
"register_renderer",
|
|
45
|
+
"load_plugins",
|
|
46
|
+
# Data objects
|
|
47
|
+
"Entry",
|
|
48
|
+
"ExtractionConfig",
|
|
49
|
+
"ExtractionResult",
|
|
50
|
+
"FileReport",
|
|
51
|
+
"Profile",
|
|
52
|
+
"RenderContext",
|
|
53
|
+
# Reporting
|
|
54
|
+
"estimate_tokens",
|
|
55
|
+
"format_summary",
|
|
56
|
+
# Annotations and constants
|
|
57
|
+
"FileStatus",
|
|
58
|
+
"OutputFormat",
|
|
59
|
+
"OverwritePolicy",
|
|
60
|
+
"StepsSpec",
|
|
61
|
+
"DEFAULT_PROFILE_KEY",
|
|
62
|
+
"GENERATED_MARKER",
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
_DESIGN_NOTES = """
|
|
67
|
+
# simplibs.extractor — Universal File Extractor
|
|
68
|
+
|
|
69
|
+
Gathers the text files of a folder into one document: select, read,
|
|
70
|
+
transform, render, write, report. The core knows no language; add-ons bring
|
|
71
|
+
profiles (`simplibs-code-extractor` is the first).
|
|
72
|
+
|
|
73
|
+
## Layers (each one looks only to the left)
|
|
74
|
+
|
|
75
|
+
`annotations` <- `constants` <- `exceptions` <- `definitions` <- `registry`
|
|
76
|
+
<- `logic` <- `plugins` <- `scripts` <- `cli`
|
|
77
|
+
|
|
78
|
+
## The Public Surface
|
|
79
|
+
|
|
80
|
+
Everything a program needs is in this module. The command line is the one
|
|
81
|
+
exception: `from simplibs.extractor.cli import run_cli`. It is not imported
|
|
82
|
+
here, so a program that only calls `extract()` does not load the prompt and
|
|
83
|
+
logger libraries it would never use.
|
|
84
|
+
|
|
85
|
+
## Internal Components Registry
|
|
86
|
+
|
|
87
|
+
| Component | Type | Description |
|
|
88
|
+
| :----------------------- | :-------- | :------------------------------------------------------------- |
|
|
89
|
+
| `extract` | Function | One extraction through all six phases. |
|
|
90
|
+
| `extract_many` | Function | Several extractions; a failed one does not stop the rest. |
|
|
91
|
+
| `extract_here` | Function | Extracts the folder of a script into a file next to it. |
|
|
92
|
+
| `generate_local_script` | Function | Writes a ready-to-run local extraction script. |
|
|
93
|
+
| `register_profile` | Function | Adds a profile to the registry. |
|
|
94
|
+
| `get_profile` | Function | Returns a profile by its `system/name` key. |
|
|
95
|
+
| `list_profiles` | Function | Lists the registered profiles. |
|
|
96
|
+
| `list_systems` | Function | Lists the systems that have a profile. |
|
|
97
|
+
| `expand_profile_keys` | Function | Turns typed names into full profile keys. |
|
|
98
|
+
| `register_renderer` | Function | Adds an output format. |
|
|
99
|
+
| `get_renderer` | Function | Returns the renderer of a format. |
|
|
100
|
+
| `list_formats` | Function | Lists the registered output formats. |
|
|
101
|
+
| `load_plugins` | Function | Lets every installed add-on register its profiles. |
|
|
102
|
+
| `Profile` | Dataclass | A named set of selection rules and transformation steps. |
|
|
103
|
+
| `ExtractionConfig` | Dataclass | Everything one run needs to know. |
|
|
104
|
+
| `ExtractionResult` | Dataclass | The outcome of one run; the source of every report. |
|
|
105
|
+
| `FileReport` | Dataclass | What happened to one file. |
|
|
106
|
+
| `Entry` | Dataclass | One file ready to be rendered. |
|
|
107
|
+
| `RenderContext` | Dataclass | What a renderer knows besides the entries. |
|
|
108
|
+
| `format_summary` | Function | Readable text from an `ExtractionResult`. |
|
|
109
|
+
| `estimate_tokens` | Function | A rough token count of a text. |
|
|
110
|
+
| `FileStatus` | Type | `ok`, `skipped` or `error`. |
|
|
111
|
+
| `OutputFormat` | Type | The name of an output format. |
|
|
112
|
+
| `OverwritePolicy` | Type | `overwrite`, `unique`, `backup` or `error`. |
|
|
113
|
+
| `StepsSpec` | Type | One action, a suffix -> action mapping, or nothing. |
|
|
114
|
+
| `DEFAULT_PROFILE_KEY` | Constant | The built-in profile `generic/all`. |
|
|
115
|
+
| `GENERATED_MARKER` | Constant | The text that marks a file as an output of this library. |
|
|
116
|
+
|
|
117
|
+
## Importing Has Two Side Effects
|
|
118
|
+
The built-in profile `generic/all` and the formats `markdown` and `plain` are
|
|
119
|
+
registered. Nothing else happens until a function is called; add-ons are only
|
|
120
|
+
looked up by `load_plugins()` (the command line calls it).
|
|
121
|
+
"""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .cli import run_cli
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
_DESIGN_NOTES = """
|
|
5
|
+
# __main__ — `python -m simplibs.extractor`
|
|
6
|
+
|
|
7
|
+
The same command as the console script `simplibs-extractor`, for
|
|
8
|
+
environments where the script's folder is not on the path.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
if __name__ == "__main__":
|
|
12
|
+
raise SystemExit(run_cli())
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
FileStatus = Literal["ok", "skipped", "error"]
|
|
5
|
+
"""The outcome of one file: included in the output, left out on purpose, or failed."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_DESIGN_NOTES = """
|
|
9
|
+
# FileStatus — Three Outcomes, Nothing Else
|
|
10
|
+
|
|
11
|
+
## Purpose
|
|
12
|
+
`skipped` is not a failure (binary file, too large, generated by this
|
|
13
|
+
library) and `error` is not a stop (the run continues). Keeping the two apart
|
|
14
|
+
is what lets the report and the exit code tell "nothing wrong" from "something
|
|
15
|
+
needs a look".
|
|
16
|
+
"""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
# Inners
|
|
3
|
+
from ._helpers import is_output_format
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
OutputFormat = Annotated[str, is_output_format]
|
|
7
|
+
"""The name of an output format (`"markdown"`, `"plain"`, or a registered one).
|
|
8
|
+
|
|
9
|
+
A lowercase name that starts with a letter. Whether a renderer with that
|
|
10
|
+
name exists is checked when the configuration is resolved.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
_DESIGN_NOTES = """
|
|
15
|
+
# OutputFormat — a Name, Not a Closed List
|
|
16
|
+
|
|
17
|
+
## Purpose
|
|
18
|
+
Renderers are registered by name, so the set of formats is open. A `Literal`
|
|
19
|
+
would freeze it at the two built-in formats and make every added renderer a
|
|
20
|
+
typing error.
|
|
21
|
+
|
|
22
|
+
## Why `Annotated`
|
|
23
|
+
An editor sees a plain `str`; `@validate_call` reads the predicate from the
|
|
24
|
+
metadata and rejects `"Markdown"`, `""` or `"md file"` at the signature.
|
|
25
|
+
"""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
OverwritePolicy = Literal["overwrite", "unique", "backup", "error"]
|
|
5
|
+
"""What happens when the output file already exists.
|
|
6
|
+
|
|
7
|
+
`"overwrite"` replaces it atomically, `"unique"` writes `name (1).ext` next to
|
|
8
|
+
it, `"backup"` keeps the old file as a backup and then replaces it, `"error"`
|
|
9
|
+
refuses to write.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_DESIGN_NOTES = """
|
|
14
|
+
# OverwritePolicy — Four Answers to "It Already Exists"
|
|
15
|
+
|
|
16
|
+
## Purpose
|
|
17
|
+
The old code knew only `"error"`. The ecosystem already has the building
|
|
18
|
+
blocks for the other three (`SafeFileWriter`, `make_unique_path`,
|
|
19
|
+
`backup_file`), so the policy is a closed, readable choice.
|
|
20
|
+
|
|
21
|
+
## Default
|
|
22
|
+
`"overwrite"` — an extractor is re-run constantly and its output is
|
|
23
|
+
generated, never hand-edited. The write itself is atomic.
|
|
24
|
+
"""
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import TypeAlias
|
|
3
|
+
from simplibs.actions import Action
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
StepsSpec: TypeAlias = Action | Mapping[str, Action] | None
|
|
7
|
+
"""How a profile transforms file text.
|
|
8
|
+
|
|
9
|
+
`None` keeps the text as it is. One `Action` is applied to every file. A
|
|
10
|
+
mapping `suffix -> Action` applies the action of the file's own suffix
|
|
11
|
+
(`".py"`); the key `"*"` is the fallback for every other file.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_DESIGN_NOTES = """
|
|
16
|
+
# StepsSpec — One Pipeline or One Pipeline per Language
|
|
17
|
+
|
|
18
|
+
## Purpose
|
|
19
|
+
A step receives only the text, never the path, so it cannot know which
|
|
20
|
+
language it is cleaning. A profile that cleans Python and JavaScript
|
|
21
|
+
differently therefore needs a mapping from suffix to action.
|
|
22
|
+
|
|
23
|
+
## Why Not Always a Mapping
|
|
24
|
+
Most profiles apply one action to everything; forcing `{"*": action}` on them
|
|
25
|
+
would be noise.
|
|
26
|
+
"""
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .FileStatus import FileStatus
|
|
2
|
+
from .OutputFormat import OutputFormat
|
|
3
|
+
from .OverwritePolicy import OverwritePolicy
|
|
4
|
+
from .StepsSpec import StepsSpec
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_DESIGN_NOTES = """
|
|
8
|
+
# annotations — Shared Types of simplibs-extractor
|
|
9
|
+
|
|
10
|
+
Plain imports, no `__all__` (the package root decides what is public).
|
|
11
|
+
`PathLike` and `ByteCount` are not redefined here — they come from
|
|
12
|
+
`simplibs.path` and `simplibs.file`.
|
|
13
|
+
|
|
14
|
+
## Internal Components Registry
|
|
15
|
+
|
|
16
|
+
| Component | Type | Description |
|
|
17
|
+
| :---------------- | :-------------- | :------------------------------------------------------ |
|
|
18
|
+
| `FileStatus` | Literal Type | Outcome of one file: `ok`, `skipped`, `error`. |
|
|
19
|
+
| `OutputFormat` | Annotated Type | The name of an output format. |
|
|
20
|
+
| `OverwritePolicy` | Literal Type | What to do when the output exists. |
|
|
21
|
+
| `StepsSpec` | Union Type | One action, a suffix -> action mapping, or nothing. |
|
|
22
|
+
|
|
23
|
+
## Changed in Batch 2
|
|
24
|
+
`is_output_format` is no longer re-exported: `Profile` is now checked by
|
|
25
|
+
`@validate_dataclass`, which reads the `OutputFormat` annotation itself.
|
|
26
|
+
"""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .is_output_format import is_output_format
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
_DESIGN_NOTES = """
|
|
5
|
+
# annotations/_helpers — Private Helpers of the Annotations
|
|
6
|
+
|
|
7
|
+
Plain imports only. Used by the annotations of this folder, not exported
|
|
8
|
+
from the package root.
|
|
9
|
+
|
|
10
|
+
## Internal Components Registry
|
|
11
|
+
|
|
12
|
+
| Component | Type | Description |
|
|
13
|
+
| :----------------- | :------- | :------------------------------------------------------- |
|
|
14
|
+
| `is_output_format` | Function | Validates whether a value has the shape of a format name. |
|
|
15
|
+
"""
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
_FORMAT_NAME = re.compile(r"[a-z][a-z0-9_-]*")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def is_output_format(name: str) -> bool:
|
|
8
|
+
"""Check whether a value has the shape of an output format name.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
name: The candidate (`"markdown"`, `"plain"`, `"json-manifest"`).
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
True for a lowercase name made of letters, digits, `_` and `-` that
|
|
15
|
+
starts with a letter; False for anything else, including non-strings.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# 1. Only a string can name a format
|
|
19
|
+
if not isinstance(name, str):
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
# 2. Lowercase word characters only — no spaces, no path separators
|
|
23
|
+
return _FORMAT_NAME.fullmatch(name) is not None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_DESIGN_NOTES = """
|
|
27
|
+
# is_output_format — "Does This Look Like a Format Name?"
|
|
28
|
+
|
|
29
|
+
## Purpose
|
|
30
|
+
The single rule behind the `OutputFormat` annotation.
|
|
31
|
+
|
|
32
|
+
## Shape, Not Existence
|
|
33
|
+
Whether a renderer with that name is registered is known only to the
|
|
34
|
+
registry, a layer further to the right. This helper checks the shape; the
|
|
35
|
+
registry check happens when the configuration is resolved.
|
|
36
|
+
"""
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from .build_configs import build_configs
|
|
2
|
+
from .build_parser import build_parser
|
|
3
|
+
from .collect_dry_run import collect_dry_run
|
|
4
|
+
from .collect_profile_keys import collect_profile_keys
|
|
5
|
+
from .collect_source import collect_source
|
|
6
|
+
from .handle_list_options import handle_list_options
|
|
7
|
+
from .normalize_positionals import normalize_positionals
|
|
8
|
+
from .run_cli import run_cli
|
|
9
|
+
from .run_init import run_init
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_DESIGN_NOTES = """
|
|
13
|
+
# cli — The Command Line of simplibs-extractor
|
|
14
|
+
|
|
15
|
+
Plain imports, no `__all__`. `run_cli` is the console-script entry point
|
|
16
|
+
(`simplibs-extractor = simplibs.extractor.cli:run_cli`); it returns the exit
|
|
17
|
+
code, which a console script passes to `sys.exit`.
|
|
18
|
+
|
|
19
|
+
## Internal Components Registry
|
|
20
|
+
|
|
21
|
+
| Component | Type | Description |
|
|
22
|
+
| :---------------------- | :------- | :------------------------------------------------------- |
|
|
23
|
+
| `build_configs` | Function | One config per profile key. |
|
|
24
|
+
| `build_parser` | Function | The argument parser. |
|
|
25
|
+
| `collect_dry_run` | Function | The dry-run flag, asked for only inside the wizard. |
|
|
26
|
+
| `collect_profile_keys` | Function | Profile keys from the command line or from questions. |
|
|
27
|
+
| `collect_source` | Function | The source folder from the command line or a question. |
|
|
28
|
+
| `handle_list_options` | Function | Answers `--list-systems` and `--list-profiles`. |
|
|
29
|
+
| `normalize_positionals` | Function | Sorts the three positional words into their roles. |
|
|
30
|
+
| `run_cli` | Function | The command; returns the exit code. |
|
|
31
|
+
| `run_init` | Function | The `init` subcommand. |
|
|
32
|
+
"""
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .describe_result import describe_result
|
|
2
|
+
from .is_profile_reference import is_profile_reference
|
|
3
|
+
from .print_error import print_error
|
|
4
|
+
from .profile_options import profile_options
|
|
5
|
+
from .system_options import system_options
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_DESIGN_NOTES = """
|
|
9
|
+
# cli/_helpers — Private Helpers of the Command Line
|
|
10
|
+
|
|
11
|
+
Plain imports only.
|
|
12
|
+
|
|
13
|
+
## Internal Components Registry
|
|
14
|
+
|
|
15
|
+
| Component | Type | Description |
|
|
16
|
+
| :--------------------- | :------- | :------------------------------------------------------- |
|
|
17
|
+
| `describe_result` | Function | One line saying why an extraction failed. |
|
|
18
|
+
| `is_profile_reference` | Function | Whether a word is a registered system or profile keys. |
|
|
19
|
+
| `print_error` | Function | Prints an error card to standard error. |
|
|
20
|
+
| `profile_options` | Function | `{name: description}` of one system's profiles. |
|
|
21
|
+
| `system_options` | Function | `{system: "N profile(s)"}` of every registered system. |
|
|
22
|
+
"""
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Outers
|
|
2
|
+
from ...definitions import ExtractionResult
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def describe_result(result: ExtractionResult) -> str:
|
|
6
|
+
"""Describe a failed extraction in one line for the batch report.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
result: A result that did not succeed.
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
`"<profile key>: <why>"`: the fatal reason, the number of failed
|
|
13
|
+
files, or a note that nothing was selected.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# 1. The whole run stopped
|
|
17
|
+
if result.fatal:
|
|
18
|
+
return f"{result.profile_key}: {result.fatal}"
|
|
19
|
+
|
|
20
|
+
# 2. Some files failed
|
|
21
|
+
if result.error_files:
|
|
22
|
+
return f"{result.profile_key}: {len(result.error_files)} file(s) failed"
|
|
23
|
+
|
|
24
|
+
# 3. Nothing failed, so nothing was selected
|
|
25
|
+
return f"{result.profile_key}: no file was selected"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_DESIGN_NOTES = """
|
|
29
|
+
# describe_result — Why a Run Counts as Failed
|
|
30
|
+
|
|
31
|
+
## Purpose
|
|
32
|
+
`ExtractionResult.success` is False for three different reasons. The report
|
|
33
|
+
at the end of a batch should say which.
|
|
34
|
+
"""
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Outers
|
|
2
|
+
from ...registry import list_profiles, list_systems
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def is_profile_reference(value: str) -> bool:
|
|
6
|
+
"""Check whether a command-line word names a registered system or registered profile keys.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
value: The word (`"python"`, `"generic/all"`, `"python/a,python/b"`, `"./src"`).
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
True if it is a system, or a comma list made only of registered full keys.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# 1. A system
|
|
16
|
+
if value in list_systems():
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
# 2. Full keys, one or several; every part must be registered
|
|
20
|
+
keys = {profile.key for profile in list_profiles()}
|
|
21
|
+
return all(part.strip() in keys for part in value.split(","))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_DESIGN_NOTES = """
|
|
25
|
+
# is_profile_reference — Profile or Folder?
|
|
26
|
+
|
|
27
|
+
## Purpose
|
|
28
|
+
`simplibs-extractor src` could mean "the system `src`" or "the folder
|
|
29
|
+
`src`". A word that is a registered system or key is a profile reference;
|
|
30
|
+
anything else may be a folder.
|
|
31
|
+
|
|
32
|
+
## Why the Registry Decides
|
|
33
|
+
Not the shape of the word: `generic/all` and `./src` both contain a slash.
|
|
34
|
+
"""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def print_error(error: BaseException) -> None:
|
|
5
|
+
"""Print an error card to standard error.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
error: The exception; a `simplibs` card prints as its framed block.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# 1. Diagnostics go to standard error, so they never mix with a piped result
|
|
12
|
+
print(error, file=sys.stderr)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_DESIGN_NOTES = """
|
|
16
|
+
# print_error — One Way to Show a Refused Command
|
|
17
|
+
|
|
18
|
+
## Purpose
|
|
19
|
+
`run_cli` and `run_init` end a refused command the same way. The card is
|
|
20
|
+
printed as it is: it already says what, why and how to fix it.
|
|
21
|
+
|
|
22
|
+
## Not the Logger
|
|
23
|
+
A logger prefixes the first line of a multi-line card with time and level,
|
|
24
|
+
which breaks its frame.
|
|
25
|
+
"""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Outers
|
|
2
|
+
from ...registry import list_profiles
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def profile_options(system: str) -> dict[str, str]:
|
|
6
|
+
"""Describe the profiles of one system for a list or a choice.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
system: The system (`"python"`).
|
|
10
|
+
|
|
11
|
+
Returns:
|
|
12
|
+
`{profile name: description}`, sorted by name; empty for an unknown system.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# 1. The names are the system's own (`code-clean`), without the `system/` part
|
|
16
|
+
return {profile.name: profile.description for profile in list_profiles(system)}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_DESIGN_NOTES = """
|
|
20
|
+
# profile_options — Names and Descriptions of One System
|
|
21
|
+
|
|
22
|
+
## Purpose
|
|
23
|
+
Feeds `--list-profiles` and the question "Which profiles?". An empty result
|
|
24
|
+
is how the caller learns that a system does not exist.
|
|
25
|
+
"""
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Outers
|
|
2
|
+
from ...registry import list_profiles, list_systems
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def system_options() -> dict[str, str]:
|
|
6
|
+
"""Describe the registered systems for a list or a choice.
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
`{system: "N profile(s)"}`, sorted by system.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# 1. One entry per system, with the number of its profiles
|
|
13
|
+
return {system: f"{len(list_profiles(system))} profile(s)" for system in list_systems()}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_DESIGN_NOTES = """
|
|
17
|
+
# system_options — What `--list-systems` and the First Question Show
|
|
18
|
+
|
|
19
|
+
## Purpose
|
|
20
|
+
One function for both, so a list and a question never disagree about which
|
|
21
|
+
systems exist.
|
|
22
|
+
"""
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .raise_duplicate_output_error import raise_duplicate_output_error
|
|
2
|
+
from .raise_list_unknown_system_error import raise_list_unknown_system_error
|
|
3
|
+
from .raise_output_is_file_error import raise_output_is_file_error
|
|
4
|
+
from .raise_too_many_positionals_error import raise_too_many_positionals_error
|
|
5
|
+
from .raise_unknown_system_error import raise_unknown_system_error
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_DESIGN_NOTES = """
|
|
9
|
+
# cli/_validations — Private Diagnostics of the Command Line
|
|
10
|
+
|
|
11
|
+
Plain imports only. Every function raises one `ParamError` and returns nothing.
|
|
12
|
+
|
|
13
|
+
## Internal Components Registry
|
|
14
|
+
|
|
15
|
+
| Component | Type | Description |
|
|
16
|
+
| :-------------------------------- | :------- | :------------------------------------------------------------ |
|
|
17
|
+
| `raise_duplicate_output_error` | Function | Two profiles would write the same output file. |
|
|
18
|
+
| `raise_list_unknown_system_error` | Function | `--list-profiles` names a system without profiles. |
|
|
19
|
+
| `raise_output_is_file_error` | Function | `-o` points to a file, not a folder. |
|
|
20
|
+
| `raise_too_many_positionals_error`| Function | Full profile keys were followed by two more words. |
|
|
21
|
+
| `raise_unknown_system_error` | Function | The system named (or chosen) has no registered profile. |
|
|
22
|
+
"""
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import NoReturn
|
|
3
|
+
from simplibs.exception import ParamError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def raise_duplicate_output_error(keys: Sequence[str]) -> NoReturn:
|
|
7
|
+
"""Raise ParamError when two or more profiles would write the same output file.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
keys: The profile keys of the run.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# 1. Raise structured ParamError wrapping a ValueError
|
|
14
|
+
raise ParamError(
|
|
15
|
+
error_name="EXTRACTOR_CONFIG_INVALID",
|
|
16
|
+
label="profiles",
|
|
17
|
+
value=list(keys),
|
|
18
|
+
problem="Two or more of these profiles write the same output file.",
|
|
19
|
+
expected="Profiles with different output names, or one profile per run.",
|
|
20
|
+
how_to_fix="Run the profiles separately, or give them different output_name values.",
|
|
21
|
+
exception=ValueError,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_DESIGN_NOTES = """
|
|
26
|
+
# raise_duplicate_output_error — Two Profiles, One File
|
|
27
|
+
|
|
28
|
+
## Purpose
|
|
29
|
+
The profiles of one run would overwrite each other. Found before anything
|
|
30
|
+
runs, it costs nothing; found by `extract_many` it would cost the files the
|
|
31
|
+
earlier profiles had already written.
|
|
32
|
+
"""
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import NoReturn
|
|
2
|
+
from simplibs.exception import ParamError
|
|
3
|
+
# Outers
|
|
4
|
+
from .._helpers import system_options
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def raise_list_unknown_system_error(system: str) -> NoReturn:
|
|
8
|
+
"""Raise ParamError when `--list-profiles` names a system without profiles.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
system: The system that was asked for.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# 1. Raise structured ParamError wrapping a ValueError
|
|
15
|
+
raise ParamError(
|
|
16
|
+
error_name="EXTRACTOR_CONFIG_INVALID",
|
|
17
|
+
label="option '--list-profiles'",
|
|
18
|
+
value=system,
|
|
19
|
+
problem=f"No profile is registered for the system {system!r}.",
|
|
20
|
+
expected=f"One of the registered systems: {', '.join(system_options()) or '(none)'}.",
|
|
21
|
+
how_to_fix="Run --list-systems to see what is installed.",
|
|
22
|
+
exception=ValueError,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_DESIGN_NOTES = """
|
|
27
|
+
# raise_list_unknown_system_error — A Typo Is Not an Empty List
|
|
28
|
+
|
|
29
|
+
## Purpose
|
|
30
|
+
An empty list would look like "no profiles exist" and hide the typo.
|
|
31
|
+
|
|
32
|
+
## Close Relative of `raise_unknown_system_error`
|
|
33
|
+
Same problem, other option: the label and the advice differ.
|
|
34
|
+
"""
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import NoReturn
|
|
3
|
+
from simplibs.exception import ParamError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def raise_output_is_file_error(out_dir: Path) -> NoReturn:
|
|
7
|
+
"""Raise ParamError when the output folder given with `-o` is a file.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
out_dir: The path that was meant to be a folder.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# 1. Raise structured ParamError wrapping a ValueError
|
|
14
|
+
raise ParamError(
|
|
15
|
+
error_name="EXTRACTOR_CONFIG_INVALID",
|
|
16
|
+
label="option '-o'",
|
|
17
|
+
value=str(out_dir),
|
|
18
|
+
problem=f"'{out_dir}' is a file, but -o takes a folder.",
|
|
19
|
+
expected="A folder (existing or to be created).",
|
|
20
|
+
how_to_fix="Pass a folder; the file name comes from the profile.",
|
|
21
|
+
exception=ValueError,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_DESIGN_NOTES = """
|
|
26
|
+
# raise_output_is_file_error — `-o` Takes a Folder
|
|
27
|
+
|
|
28
|
+
## Purpose
|
|
29
|
+
The file name always comes from the profile, so `-o` can only be a folder.
|
|
30
|
+
"""
|