entangled-cli 2.3.2__py3-none-any.whl → 2.4.2__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.
- entangled/commands/__init__.py +5 -11
- entangled/commands/brei.py +17 -10
- entangled/commands/main.py +23 -0
- entangled/commands/new.py +35 -41
- entangled/commands/reset.py +7 -49
- entangled/commands/status.py +13 -7
- entangled/commands/stitch.py +12 -49
- entangled/commands/sync.py +57 -27
- entangled/commands/tangle.py +30 -56
- entangled/commands/watch.py +20 -39
- entangled/config/__init__.py +48 -153
- entangled/config/annotation_method.py +17 -0
- entangled/config/config_data.py +105 -0
- entangled/config/config_update.py +60 -0
- entangled/config/document_style.py +18 -0
- entangled/config/language.py +2 -1
- entangled/config/markers.py +21 -0
- entangled/config/namespace_default.py +14 -0
- entangled/config/version.py +5 -6
- entangled/errors/user.py +20 -21
- entangled/hooks/__init__.py +20 -21
- entangled/hooks/base.py +9 -2
- entangled/hooks/build.py +22 -19
- entangled/hooks/quarto_attributes.py +25 -17
- entangled/hooks/repl.py +18 -8
- entangled/hooks/shebang.py +5 -4
- entangled/hooks/spdx_license.py +5 -4
- entangled/hooks/task.py +36 -45
- entangled/interface/__init__.py +4 -0
- entangled/interface/context.py +63 -0
- entangled/interface/document.py +106 -0
- entangled/io/__init__.py +2 -2
- entangled/io/filedb.py +38 -18
- entangled/io/stat.py +2 -3
- entangled/io/transaction.py +26 -20
- entangled/io/virtual.py +93 -6
- entangled/iterators/__init__.py +6 -0
- entangled/iterators/lines.py +28 -0
- entangled/iterators/peekable.py +43 -0
- entangled/iterators/run_generator.py +14 -0
- entangled/main.py +4 -27
- entangled/model/__init__.py +23 -0
- entangled/model/code_block.py +79 -0
- entangled/model/content.py +34 -0
- entangled/{properties.py → model/properties.py} +21 -1
- entangled/model/reference_id.py +31 -0
- entangled/model/reference_map.py +102 -0
- entangled/model/reference_name.py +32 -0
- entangled/model/tangle.py +158 -0
- entangled/readers/__init__.py +16 -0
- entangled/readers/code.py +107 -0
- entangled/readers/delimiters.py +91 -0
- entangled/readers/markdown.py +179 -0
- entangled/readers/types.py +14 -0
- entangled/readers/yaml_header.py +56 -0
- entangled/status.py +12 -17
- entangled/text_location.py +11 -2
- entangled/utility.py +0 -4
- {entangled_cli-2.3.2.dist-info → entangled_cli-2.4.2.dist-info}/METADATA +8 -8
- entangled_cli-2.4.2.dist-info/RECORD +71 -0
- {entangled_cli-2.3.2.dist-info → entangled_cli-2.4.2.dist-info}/WHEEL +1 -1
- entangled/code_reader.py +0 -81
- entangled/document.py +0 -162
- entangled/from_str.py +0 -27
- entangled/markdown_reader.py +0 -229
- entangled/tangle.py +0 -153
- entangled_cli-2.3.2.dist-info/RECORD +0 -49
- {entangled_cli-2.3.2.dist-info → entangled_cli-2.4.2.dist-info}/entry_points.txt +0 -0
- {entangled_cli-2.3.2.dist-info → entangled_cli-2.4.2.dist-info}/licenses/LICENSE +0 -0
entangled/commands/__init__.py
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from .status import status
|
|
1
|
+
from .main import main
|
|
3
2
|
from .stitch import stitch
|
|
4
|
-
from .sync import sync
|
|
5
3
|
from .tangle import tangle
|
|
4
|
+
from .new import new
|
|
6
5
|
from .reset import reset
|
|
6
|
+
from .sync import sync
|
|
7
7
|
from .watch import watch
|
|
8
|
+
from .status import status
|
|
8
9
|
from .brei import brei
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
|
-
"new",
|
|
12
|
-
"brei",
|
|
13
|
-
"reset",
|
|
14
|
-
"status",
|
|
15
|
-
"stitch",
|
|
16
|
-
"sync",
|
|
17
|
-
"tangle",
|
|
18
|
-
"watch",
|
|
12
|
+
"main", "stitch", "tangle", "new", "reset", "sync", "watch", "status", "brei"
|
|
19
13
|
]
|
entangled/commands/brei.py
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from collections.abc import Awaitable
|
|
3
3
|
from typing import Any
|
|
4
|
-
import argh # type: ignore
|
|
5
4
|
import asyncio
|
|
6
5
|
import textwrap
|
|
7
6
|
|
|
8
|
-
from ..config import
|
|
7
|
+
from ..config import Config, read_config
|
|
8
|
+
from ..io import FileCache
|
|
9
9
|
from brei import resolve_tasks, Phony
|
|
10
10
|
from ..logging import logger
|
|
11
|
+
from .main import main
|
|
12
|
+
|
|
13
|
+
import click
|
|
11
14
|
|
|
12
15
|
log = logger()
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
async def
|
|
18
|
+
async def brei_main(target_strs: list[str], force_run: bool, throttle: int | None):
|
|
16
19
|
if not Path(".entangled").exists():
|
|
17
20
|
Path(".entangled").mkdir()
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
cfg = Config() | read_config(FileCache())
|
|
23
|
+
db = await resolve_tasks(cfg.brei, Path(".entangled/brei_history"))
|
|
20
24
|
if throttle:
|
|
21
25
|
db.throttle = asyncio.Semaphore(throttle)
|
|
22
26
|
db.force_run = force_run
|
|
@@ -33,10 +37,13 @@ async def main(target_strs: list[str], force_run: bool, throttle: int | None):
|
|
|
33
37
|
log.error(msg)
|
|
34
38
|
|
|
35
39
|
|
|
36
|
-
@
|
|
37
|
-
@
|
|
38
|
-
@
|
|
40
|
+
@main.command()
|
|
41
|
+
@click.argument("targets", nargs=-1)
|
|
42
|
+
@click.option("-B", "--force-run", is_flag=True, help="rebuild all dependencies")
|
|
43
|
+
@click.option("-j", "--throttle", is_flag=True, help="limit number of concurrent jobs")
|
|
39
44
|
def brei(targets: list[str], *, force_run: bool = False, throttle: int | None = None):
|
|
40
|
-
"""Build one of the configured targets.
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
"""Build one of the configured targets.
|
|
46
|
+
|
|
47
|
+
TARGETS Names of the targets to run.
|
|
48
|
+
"""
|
|
49
|
+
asyncio.run(brei_main(targets, force_run, throttle))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from ..logging import configure, logger
|
|
2
|
+
from ..version import __version__
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import rich_click as click
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group(
|
|
9
|
+
invoke_without_command=True,
|
|
10
|
+
epilog="See https://entangled.github.io/ for more help and tutorials."
|
|
11
|
+
)
|
|
12
|
+
@click.rich_config({"commands_before_options": True, "theme": "nord-modern"})
|
|
13
|
+
@click.option("-v", "--version", is_flag=True, help="Show version.")
|
|
14
|
+
@click.option("-d", "--debug", is_flag=True, help="Enable debugging.")
|
|
15
|
+
def main(version: bool = False, debug: bool = False):
|
|
16
|
+
"""Your literate programming toolkit.
|
|
17
|
+
"""
|
|
18
|
+
if version:
|
|
19
|
+
print(f"Entangled {__version__}")
|
|
20
|
+
sys.exit(0)
|
|
21
|
+
|
|
22
|
+
configure(debug)
|
|
23
|
+
logger().info(f"Welcome to Entangled v{__version__}!")
|
entangled/commands/new.py
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
from argh.utils import get_subparsers
|
|
3
|
-
import argparse
|
|
1
|
+
import click
|
|
4
2
|
|
|
5
3
|
from pathlib import Path
|
|
6
|
-
from rich_argparse import RichHelpFormatter
|
|
7
4
|
from rich.console import Console
|
|
8
5
|
from rich.table import Table
|
|
9
6
|
|
|
10
7
|
from ..errors.user import HelpfulUserError
|
|
8
|
+
from .main import main
|
|
11
9
|
|
|
12
10
|
description = """Create a new entangled project from a template.
|
|
13
11
|
|
|
@@ -42,61 +40,52 @@ def print_help() -> None:
|
|
|
42
40
|
For this we first have to create a parser and attach this `new` function
|
|
43
41
|
to it. Then we have a parser object that we can `print_help()` from.
|
|
44
42
|
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
ctx = click.get_current_context()
|
|
44
|
+
click.echo(ctx.get_help())
|
|
45
|
+
ctx.exit()
|
|
48
46
|
|
|
49
47
|
|
|
50
|
-
@
|
|
51
|
-
"-
|
|
52
|
-
|
|
48
|
+
@main.command(
|
|
49
|
+
epilog="This command, and the options provided are a front-end for `copier`. " +
|
|
50
|
+
"See https://copier.readthedocs.io/en/stable/ for more information."
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"-a", "--answers-file",
|
|
53
54
|
help="Update using this path (relative to [project_path]) to find the answers file for `copier`",
|
|
54
55
|
)
|
|
55
|
-
@
|
|
56
|
-
"-d",
|
|
57
|
-
"--data",
|
|
56
|
+
@click.option(
|
|
57
|
+
"-d", "--data",
|
|
58
58
|
help='"VARIABLE1=VALUE1;VARIABLE1=VALUE2" Make VARIABLEs available as VALUEs when rendering the template; make sure to use quotation marks for multiple variables/values',
|
|
59
59
|
)
|
|
60
|
-
@
|
|
61
|
-
"-D",
|
|
62
|
-
"--defaults",
|
|
60
|
+
@click.option(
|
|
61
|
+
"-D", "--defaults", is_flag=True,
|
|
63
62
|
help="Use default answers to questions, which might be null if not specified; overwrites when combined with -d!",
|
|
64
63
|
)
|
|
65
|
-
@
|
|
66
|
-
"-p",
|
|
67
|
-
"--pretend",
|
|
64
|
+
@click.option(
|
|
65
|
+
"-p", "--pretend", is_flag=True,
|
|
68
66
|
help="Run but do not make any changes",
|
|
69
67
|
)
|
|
70
|
-
@
|
|
71
|
-
"-o",
|
|
72
|
-
"--overwrite",
|
|
68
|
+
@click.option(
|
|
69
|
+
"-o", "--overwrite", is_flag=True,
|
|
73
70
|
help="Overwrite files that already exist, without asking.",
|
|
74
71
|
)
|
|
75
|
-
@
|
|
76
|
-
"-l",
|
|
77
|
-
"--list-templates",
|
|
72
|
+
@click.option(
|
|
73
|
+
"-l", "--list-templates", is_flag=True,
|
|
78
74
|
help="List all official templates and exit",
|
|
79
75
|
)
|
|
80
|
-
@
|
|
81
|
-
"-t",
|
|
82
|
-
"--trust",
|
|
76
|
+
@click.option(
|
|
77
|
+
"-t", "--trust", is_flag=True,
|
|
83
78
|
help='Allow templates with unsafe features (Jinja extensions, migrations, tasks); "True" for officially supported templates',
|
|
84
79
|
)
|
|
85
|
-
@
|
|
86
|
-
"template"
|
|
87
|
-
# default=None,
|
|
88
|
-
nargs="?",
|
|
89
|
-
help="Template handle or URL; initialize a new project from this template",
|
|
80
|
+
@click.argument(
|
|
81
|
+
"template"
|
|
90
82
|
)
|
|
91
|
-
@
|
|
92
|
-
"project-path"
|
|
93
|
-
# default=None,
|
|
94
|
-
nargs="?",
|
|
95
|
-
help="Initialize a new project at this path",
|
|
83
|
+
@click.argument(
|
|
84
|
+
"project-path"
|
|
96
85
|
)
|
|
97
86
|
def new(
|
|
98
|
-
template: str
|
|
99
|
-
project_path: Path
|
|
87
|
+
template: str,
|
|
88
|
+
project_path: Path, *,
|
|
100
89
|
answers_file: str | None = None,
|
|
101
90
|
data: str = "",
|
|
102
91
|
defaults: bool = False,
|
|
@@ -105,7 +94,12 @@ def new(
|
|
|
105
94
|
list_templates: bool = False,
|
|
106
95
|
trust: bool = False,
|
|
107
96
|
) -> None:
|
|
108
|
-
"""Create a new entangled project from a template.
|
|
97
|
+
"""Create a new entangled project from a template.
|
|
98
|
+
|
|
99
|
+
TEMPLATE Template handle or URL; initialize a new project from this template.
|
|
100
|
+
|
|
101
|
+
PROJECT_PATH Initialize a new project at this path.
|
|
102
|
+
"""
|
|
109
103
|
|
|
110
104
|
from ..config.templates import templates as AVAILABLE_TEMPLATES
|
|
111
105
|
from ..config.templates import Template
|
entangled/commands/reset.py
CHANGED
|
@@ -6,57 +6,15 @@ This command will read the markdown sources, then pretend to be tangling
|
|
|
6
6
|
without actually writing out to source files.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from ..io import TransactionMode
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from ..document import ReferenceMap
|
|
13
|
-
from ..errors.user import UserError
|
|
14
|
-
|
|
15
|
-
import logging
|
|
16
|
-
from pathlib import Path
|
|
9
|
+
from ..io import TransactionMode
|
|
10
|
+
from .main import main
|
|
11
|
+
from .tangle import do_tangle
|
|
17
12
|
|
|
18
13
|
|
|
14
|
+
@main.command(short_help="Reset the file database.")
|
|
19
15
|
def reset():
|
|
20
16
|
"""
|
|
21
|
-
Resets the database. This performs a tangle without actually
|
|
22
|
-
output to the files, but updating the database as if we were.
|
|
17
|
+
Resets the file database. This performs a tangle without actually
|
|
18
|
+
writing output to the files, but updating the database as if we were.
|
|
23
19
|
"""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# these imports depend on config being read
|
|
27
|
-
from ..markdown_reader import read_markdown_file
|
|
28
|
-
from ..tangle import tangle_ref
|
|
29
|
-
|
|
30
|
-
input_file_list = get_input_files()
|
|
31
|
-
|
|
32
|
-
refs = ReferenceMap()
|
|
33
|
-
hooks = get_hooks()
|
|
34
|
-
logging.debug("tangling with hooks: %s", [h.__module__ for h in hooks])
|
|
35
|
-
mode = TransactionMode.RESETDB
|
|
36
|
-
annotation_method = config.get.annotation
|
|
37
|
-
|
|
38
|
-
try:
|
|
39
|
-
with transaction(mode) as t:
|
|
40
|
-
for path in input_file_list:
|
|
41
|
-
logging.debug("reading `%s`", path)
|
|
42
|
-
t.update(path)
|
|
43
|
-
_, _ = read_markdown_file(t, path, refs=refs, hooks=hooks)
|
|
44
|
-
|
|
45
|
-
for h in hooks:
|
|
46
|
-
h.pre_tangle(refs)
|
|
47
|
-
|
|
48
|
-
for tgt in refs.targets:
|
|
49
|
-
result, deps = tangle_ref(refs, tgt, annotation_method)
|
|
50
|
-
mask = next(iter(refs.by_name(tgt))).mode
|
|
51
|
-
t.write(Path(tgt), result, list(map(Path, deps)), mask)
|
|
52
|
-
|
|
53
|
-
for h in hooks:
|
|
54
|
-
h.on_tangle(t, refs)
|
|
55
|
-
|
|
56
|
-
t.clear_orphans()
|
|
57
|
-
|
|
58
|
-
for h in hooks:
|
|
59
|
-
h.post_tangle(refs)
|
|
60
|
-
|
|
61
|
-
except UserError as e:
|
|
62
|
-
logging.error(str(e))
|
|
20
|
+
do_tangle(mode=TransactionMode.RESETDB)
|
entangled/commands/status.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from collections.abc import Iterable
|
|
3
|
-
from ..status import
|
|
4
|
-
from ..config import
|
|
3
|
+
from ..status import list_dependent_files
|
|
4
|
+
from ..config import Config, read_config, get_input_files
|
|
5
|
+
from ..io import FileCache
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
from rich.console import Console, Group
|
|
@@ -10,6 +11,8 @@ from rich.table import Table
|
|
|
10
11
|
from rich.panel import Panel
|
|
11
12
|
from rich.tree import Tree
|
|
12
13
|
|
|
14
|
+
from .main import main
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
def tree_from_files(files: Iterable[Path]):
|
|
15
18
|
tree = Tree(label=".")
|
|
@@ -28,23 +31,25 @@ def files_panel(file_list: Iterable[Path], title: str) -> Panel:
|
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
def rich_status():
|
|
34
|
+
fs = FileCache()
|
|
35
|
+
cfg = Config() | read_config(fs)
|
|
31
36
|
config_table = Table()
|
|
32
37
|
config_table.add_column("name")
|
|
33
38
|
config_table.add_column("value")
|
|
34
39
|
config_table.add_row(
|
|
35
|
-
"Watch list", ", ".join(f"'{pat}'" for pat in
|
|
40
|
+
"Watch list", ", ".join(f"'{pat}'" for pat in cfg.watch_list)
|
|
36
41
|
)
|
|
37
42
|
config_table.add_row(
|
|
38
|
-
"Ignore list", ", ".join(f"'{pat}'" for pat in
|
|
43
|
+
"Ignore list", ", ".join(f"'{pat}'" for pat in cfg.ignore_list)
|
|
39
44
|
)
|
|
40
|
-
config_table.add_row("Hooks enabled", ", ".join(
|
|
45
|
+
config_table.add_row("Hooks enabled", ", ".join(cfg.hooks))
|
|
41
46
|
|
|
42
47
|
console = Console(color_system="auto")
|
|
43
48
|
group = Group(
|
|
44
49
|
Panel(config_table, title="config", border_style="dark_cyan"),
|
|
45
50
|
Columns(
|
|
46
51
|
[
|
|
47
|
-
files_panel(
|
|
52
|
+
files_panel(get_input_files(fs, cfg), "input files"),
|
|
48
53
|
files_panel(list_dependent_files(), "dependent files"),
|
|
49
54
|
]
|
|
50
55
|
),
|
|
@@ -53,6 +58,7 @@ def rich_status():
|
|
|
53
58
|
console.print(group)
|
|
54
59
|
|
|
55
60
|
|
|
61
|
+
@main.command()
|
|
56
62
|
def status():
|
|
57
|
-
|
|
63
|
+
"""Print a status overview."""
|
|
58
64
|
rich_status()
|
entangled/commands/stitch.py
CHANGED
|
@@ -1,41 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
from pathlib import Path
|
|
1
|
+
import rich_click as click
|
|
3
2
|
|
|
4
|
-
import
|
|
5
|
-
import argh # type: ignore
|
|
3
|
+
from .main import main
|
|
6
4
|
|
|
7
|
-
from ..
|
|
8
|
-
from ..document import ReferenceMap, Content, PlainText, ReferenceId
|
|
5
|
+
from ..interface import Document
|
|
9
6
|
from ..io import transaction, TransactionMode
|
|
10
7
|
from ..errors.user import UserError
|
|
11
|
-
from ..config import get_input_files
|
|
12
8
|
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
case PlainText(s):
|
|
18
|
-
return s
|
|
19
|
-
case ReferenceId():
|
|
20
|
-
return reference_map.get_codeblock(item).indented_text
|
|
21
|
-
|
|
22
|
-
return "\n".join(get(i) for i in content) + "\n"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@argh.arg("--force", help="force overwrite on conflict")
|
|
26
|
-
@argh.arg("-s", "--show", help="only show, don't act")
|
|
10
|
+
@main.command()
|
|
11
|
+
@click.option("-f", "--force", is_flag=True, help="force overwrite on conflict")
|
|
12
|
+
@click.option("-s", "--show", is_flag=True, help="only show, don't act")
|
|
27
13
|
def stitch(*, force: bool = False, show: bool = False):
|
|
28
|
-
"""Stitch code changes back into the
|
|
29
|
-
config.read()
|
|
30
|
-
|
|
31
|
-
# these imports depend on config being read
|
|
32
|
-
from ..markdown_reader import read_markdown_file
|
|
33
|
-
from ..code_reader import CodeReader
|
|
34
|
-
from ..hooks import get_hooks
|
|
35
|
-
|
|
36
|
-
input_file_list = get_input_files()
|
|
37
|
-
hooks = get_hooks()
|
|
38
|
-
|
|
14
|
+
"""Stitch code changes back into the documentation."""
|
|
39
15
|
if show:
|
|
40
16
|
mode = TransactionMode.SHOW
|
|
41
17
|
elif force:
|
|
@@ -43,22 +19,9 @@ def stitch(*, force: bool = False, show: bool = False):
|
|
|
43
19
|
else:
|
|
44
20
|
mode = TransactionMode.FAIL
|
|
45
21
|
|
|
46
|
-
|
|
47
|
-
content: dict[Path, list[Content]] = {}
|
|
48
|
-
try:
|
|
49
|
-
with transaction(mode) as t:
|
|
50
|
-
for path in input_file_list:
|
|
51
|
-
logging.debug("reading `%s`", path)
|
|
52
|
-
_, c = read_markdown_file(t, path, refs=refs, hooks=hooks)
|
|
53
|
-
content[path] = c
|
|
54
|
-
|
|
55
|
-
for path in t.db.managed_files:
|
|
56
|
-
logging.debug("reading `%s`", path)
|
|
57
|
-
t.update(path)
|
|
58
|
-
_ = CodeReader(path, refs).run(t.read(path))
|
|
59
|
-
|
|
60
|
-
for path in input_file_list:
|
|
61
|
-
t.write(path, stitch_markdown(refs, content[path]), [])
|
|
22
|
+
doc = Document()
|
|
62
23
|
|
|
63
|
-
|
|
64
|
-
|
|
24
|
+
with transaction(mode) as t:
|
|
25
|
+
doc.load(t)
|
|
26
|
+
doc.load_all_code(t)
|
|
27
|
+
doc.stitch(t)
|
entangled/commands/sync.py
CHANGED
|
@@ -1,51 +1,81 @@
|
|
|
1
|
-
from
|
|
2
|
-
from itertools import chain
|
|
3
|
-
from pathlib import Path
|
|
1
|
+
from enum import Enum
|
|
4
2
|
|
|
5
|
-
import
|
|
3
|
+
from ..io import filedb, FileCache, transaction
|
|
4
|
+
from ..interface import Document
|
|
5
|
+
from ..errors.user import UserError
|
|
6
|
+
|
|
7
|
+
from .main import main
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
from ..config import config
|
|
9
|
-
from .stitch import stitch, get_input_files
|
|
10
|
-
from .tangle import tangle
|
|
9
|
+
import logging
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
class Action(Enum):
|
|
13
|
+
NOTHING = 0
|
|
14
|
+
TANGLE = 1
|
|
15
|
+
STITCH = 2
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def sync_action() ->
|
|
19
|
-
input_file_list =
|
|
20
|
-
fs = FileCache()
|
|
18
|
+
def sync_action(doc: Document) -> Action:
|
|
19
|
+
input_file_list = doc.input_files()
|
|
21
20
|
|
|
22
|
-
with filedb(readonly=True) as db:
|
|
23
|
-
changed = set(db.changed_files(fs))
|
|
21
|
+
with filedb(readonly=True, fs=doc.context.fs) as db:
|
|
22
|
+
changed = set(db.changed_files(doc.context.fs))
|
|
24
23
|
|
|
25
24
|
if not all(f in db for f in input_file_list):
|
|
26
|
-
return
|
|
25
|
+
return Action.TANGLE
|
|
27
26
|
|
|
28
27
|
if not changed:
|
|
29
|
-
return
|
|
28
|
+
return Action.NOTHING
|
|
30
29
|
|
|
31
30
|
if changed.isdisjoint(db.managed_files):
|
|
32
|
-
|
|
33
|
-
return tangle
|
|
31
|
+
return Action.TANGLE
|
|
34
32
|
|
|
35
33
|
if changed.issubset(db.managed_files):
|
|
36
|
-
|
|
37
|
-
return _stitch_then_tangle
|
|
34
|
+
return Action.STITCH
|
|
38
35
|
|
|
39
36
|
logging.error("changed: %s", [str(p) for p in changed])
|
|
40
37
|
logging.error(
|
|
41
38
|
"Both markdown and code seem to have changed, don't know what to do now."
|
|
42
39
|
)
|
|
43
|
-
return
|
|
40
|
+
return Action.NOTHING
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def tangle(doc: Document):
|
|
44
|
+
with transaction() as t:
|
|
45
|
+
doc.load(t)
|
|
46
|
+
doc.tangle(t)
|
|
47
|
+
t.clear_orphans()
|
|
48
|
+
for h in doc.context.all_hooks:
|
|
49
|
+
h.post_tangle(doc.reference_map)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def stitch(doc: Document):
|
|
53
|
+
with transaction() as t:
|
|
54
|
+
doc.load(t)
|
|
55
|
+
doc.load_all_code(t)
|
|
56
|
+
doc.stitch(t)
|
|
57
|
+
with transaction() as t:
|
|
58
|
+
doc.tangle(t)
|
|
59
|
+
for h in doc.context.all_hooks:
|
|
60
|
+
h.post_tangle(doc.reference_map)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def run_sync():
|
|
64
|
+
doc = Document()
|
|
65
|
+
match sync_action(doc):
|
|
66
|
+
case Action.TANGLE:
|
|
67
|
+
logging.info("Tangling.")
|
|
68
|
+
tangle(doc)
|
|
69
|
+
|
|
70
|
+
case Action.STITCH:
|
|
71
|
+
logging.info("Stitching.")
|
|
72
|
+
stitch(doc)
|
|
73
|
+
|
|
74
|
+
case Action.NOTHING:
|
|
75
|
+
pass
|
|
44
76
|
|
|
45
77
|
|
|
78
|
+
@main.command()
|
|
46
79
|
def sync():
|
|
47
80
|
"""Be smart wether to tangle or stich"""
|
|
48
|
-
|
|
49
|
-
action = sync_action()
|
|
50
|
-
if action is not None:
|
|
51
|
-
action()
|
|
81
|
+
run_sync()
|
entangled/commands/tangle.py
CHANGED
|
@@ -1,74 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
import rich_click as click
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
import logging
|
|
3
|
+
from .main import main
|
|
5
4
|
|
|
6
|
-
from ..
|
|
7
|
-
from ..
|
|
8
|
-
from ..io import transaction, TransactionMode
|
|
9
|
-
from ..hooks import get_hooks
|
|
5
|
+
from ..config import AnnotationMethod, Config
|
|
6
|
+
from ..io import AbstractFileCache, FileCache, transaction, TransactionMode
|
|
10
7
|
from ..errors.user import UserError
|
|
8
|
+
from ..interface import Context, Document
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
@
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
@argh.arg("--force", help="force overwrite on conflict")
|
|
20
|
-
@argh.arg("-s", "--show", help="only show, don't act")
|
|
21
|
-
@argh.arg("-r", "--reset-db", help="resets database")
|
|
22
|
-
def tangle(*, annotate: str | None = None, force: bool = False, show: bool = False, reset_db: bool = False):
|
|
23
|
-
"""Tangle codes from Markdown"""
|
|
24
|
-
config.read()
|
|
25
|
-
|
|
26
|
-
# these imports depend on config being read
|
|
27
|
-
from ..markdown_reader import read_markdown_file
|
|
28
|
-
from ..tangle import tangle_ref
|
|
29
|
-
|
|
30
|
-
if annotate is None:
|
|
31
|
-
annotation_method = config.get.annotation
|
|
32
|
-
else:
|
|
33
|
-
annotation_method = AnnotationMethod[annotate.upper()]
|
|
34
|
-
|
|
35
|
-
input_file_list = get_input_files()
|
|
36
|
-
|
|
37
|
-
refs = ReferenceMap()
|
|
38
|
-
hooks = get_hooks()
|
|
39
|
-
logging.debug("tangling with hooks: %s", [h.__module__ for h in hooks])
|
|
40
|
-
|
|
11
|
+
@main.command()
|
|
12
|
+
@click.option("-a", "--annotate", type=click.Choice(AnnotationMethod, case_sensitive=False),
|
|
13
|
+
help="annotation method")
|
|
14
|
+
@click.option("-f", "--force", is_flag=True, help="force overwriting existing files")
|
|
15
|
+
@click.option("-s", "--show", is_flag=True, help="only show what would happen")
|
|
16
|
+
def tangle(*, annotate: AnnotationMethod | None = None, force: bool = False, show: bool = False):
|
|
41
17
|
if show:
|
|
42
18
|
mode = TransactionMode.SHOW
|
|
43
|
-
elif reset_db:
|
|
44
|
-
mode = TransactionMode.RESETDB
|
|
45
19
|
elif force:
|
|
46
20
|
mode = TransactionMode.FORCE
|
|
47
21
|
else:
|
|
48
22
|
mode = TransactionMode.FAIL
|
|
49
23
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
for path in input_file_list:
|
|
53
|
-
logging.debug("reading `%s`", path)
|
|
54
|
-
t.update(path)
|
|
55
|
-
_, _ = read_markdown_file(t, path, refs=refs, hooks=hooks)
|
|
24
|
+
do_tangle(annotate=annotate, mode=mode, skip_post_tangle=False)
|
|
25
|
+
|
|
56
26
|
|
|
57
|
-
|
|
58
|
-
|
|
27
|
+
def do_tangle(*,
|
|
28
|
+
annotate: AnnotationMethod | None = None,
|
|
29
|
+
mode: TransactionMode = TransactionMode.FAIL,
|
|
30
|
+
fs: AbstractFileCache | None = None,
|
|
31
|
+
skip_post_tangle: bool = True):
|
|
32
|
+
"""Tangle codes from the documentation."""
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
mask = next(iter(refs.by_name(tgt))).mode
|
|
63
|
-
t.write(Path(tgt), result, list(map(Path, deps)), mask)
|
|
34
|
+
if fs is None:
|
|
35
|
+
fs = FileCache()
|
|
64
36
|
|
|
65
|
-
|
|
66
|
-
h.on_tangle(t, refs)
|
|
37
|
+
doc = Document(context=Context(fs=fs))
|
|
67
38
|
|
|
68
|
-
|
|
39
|
+
with transaction(mode, fs=fs) as t:
|
|
40
|
+
doc.load(t)
|
|
41
|
+
doc.tangle(t, annotate)
|
|
42
|
+
t.clear_orphans()
|
|
69
43
|
|
|
70
|
-
|
|
71
|
-
|
|
44
|
+
if skip_post_tangle:
|
|
45
|
+
return
|
|
72
46
|
|
|
73
|
-
|
|
74
|
-
|
|
47
|
+
for h in doc.context.all_hooks:
|
|
48
|
+
h.post_tangle(doc.reference_map)
|