entangled-cli 2.1.7__tar.gz → 2.1.9__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.
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/PKG-INFO +53 -3
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/README.md +52 -2
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/__init__.py +0 -1
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/tangle.py +16 -8
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/filedb.py +22 -6
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/hooks/task.py +4 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/transaction.py +36 -5
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/pyproject.toml +1 -1
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/LICENSE +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/__init__.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/code_reader.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/brei.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/new.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/status.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/stitch.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/sync.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/commands/watch.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/config/__init__.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/config/language.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/config/templates.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/config/version.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/construct.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/document.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/errors/__init__.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/errors/internal.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/errors/user.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/hooks/__init__.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/hooks/base.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/hooks/build.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/hooks/quarto_attributes.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/hooks/shebang.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/logging.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/main.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/markdown_reader.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/parsing.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/properties.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/py.typed +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/status.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/tangle.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/text_location.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/utility.py +0 -0
- {entangled_cli-2.1.7 → entangled_cli-2.1.9}/entangled/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: entangled-cli
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.9
|
|
4
4
|
Summary: Literate Programming toolbox
|
|
5
5
|
Home-page: https://entangled.github.io/
|
|
6
6
|
License: Apache 2
|
|
@@ -26,6 +26,7 @@ Project-URL: Repository, https://github.com/entangled/entangled.py
|
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
|
|
28
28
|
# Entangled
|
|
29
|
+
|
|
29
30
|
[](https://github.com/entangled/entangled.py/actions/workflows/deploy.yml)
|
|
30
31
|
[](https://github.com/entangled/entangled.py/actions/workflows/python-package.yml)
|
|
31
32
|
|
|
@@ -43,6 +44,7 @@ We’re trying to increase the visibility of Entangled. If you like Entangled, p
|
|
|
43
44
|
```
|
|
44
45
|
|
|
45
46
|
## Get started
|
|
47
|
+
|
|
46
48
|
To install Entangled, all you need is a Python (version ≥3.11) installation. If you use [`poetry`](https://python-poetry.org), and you start a new project,
|
|
47
49
|
|
|
48
50
|
```
|
|
@@ -59,6 +61,7 @@ pip install entangled-cli
|
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
## Use
|
|
64
|
+
|
|
62
65
|
Run the `entangled watch` daemon in the root of your project folder. By default all Markdown files are monitored for fenced code blocks like so:
|
|
63
66
|
|
|
64
67
|
~~~markdown
|
|
@@ -70,6 +73,7 @@ Run the `entangled watch` daemon in the root of your project folder. By default
|
|
|
70
73
|
The syntax of code block properties is the same as CSS properties: `#hello` gives the block the `hello` identifier, `.rust` adds the `rust` class and the `file` attribute is set to `src/world.rs` (quotes are optional). For Entangled to know how to tangle this block, you need to specify a language and a target file. However, now comes the cool stuff. We can split our code in meaningful components by cross-refrences.
|
|
71
74
|
|
|
72
75
|
### Hello World in C++
|
|
76
|
+
|
|
73
77
|
The combined code-blocks in this example compose a compilable source code for "Hello World". For didactic reasons we don't always give the listing of an entire source file in one go. In stead, we use a system of references known as *noweb* (after Ramsey 1994).
|
|
74
78
|
|
|
75
79
|
Inside source fragments you may encounter a line with `<<...>>` marks like,
|
|
@@ -113,6 +117,7 @@ return EXIT_SUCCESS;
|
|
|
113
117
|
These blocks of code can be *tangled* into source files.
|
|
114
118
|
|
|
115
119
|
### Configuring
|
|
120
|
+
|
|
116
121
|
Entangled is configured by putting a `entangled.toml` in the root of your project.
|
|
117
122
|
|
|
118
123
|
```toml
|
|
@@ -176,7 +181,38 @@ identifiers = ["java"]
|
|
|
176
181
|
comment = { open = "//" }
|
|
177
182
|
```
|
|
178
183
|
|
|
184
|
+
## Working with Git
|
|
185
|
+
|
|
186
|
+
When using Entangled in conjunction with Git, there are a few tricks that you may want to know about.
|
|
187
|
+
|
|
188
|
+
### Restoring files when both Markdown and code have changed
|
|
189
|
+
|
|
190
|
+
When you edited both Markdown and code without the daemon running, you may need to do some tricks to get back into a consistent state.
|
|
191
|
+
|
|
192
|
+
```shell
|
|
193
|
+
git add .
|
|
194
|
+
git commit -m 'fixed everything' # save everything you did
|
|
195
|
+
entangled tangle --force # overwrites some changes you made
|
|
196
|
+
git restore src/brilliant_code.c # retrieve from latest commit
|
|
197
|
+
entangled stitch # apply changes back to markdown
|
|
198
|
+
git add .
|
|
199
|
+
git commit --amend # amend your commit to perfection
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
There may be better/faster ways to do this.
|
|
203
|
+
|
|
204
|
+
### Entangled conflicts after merging branches
|
|
205
|
+
|
|
206
|
+
Entangled can get confused when you merge, and there is a conflict on `.entangled/filedb.json`. This file keeps track of which files are sources for Entangled and which ones are generated by Entangled. That way, Entangled will never overwrite files it isn't supposed to, and the other way around, when you rename a target, the old one gets removed. It is very hard to merge this file though. When you need to, you can regenerate this file using:
|
|
207
|
+
|
|
208
|
+
```shell
|
|
209
|
+
entangled tangle -r
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This will perform the tangle as if it is the first time, but it won't actually write files.
|
|
213
|
+
|
|
179
214
|
## Hooks
|
|
215
|
+
|
|
180
216
|
Entangled has a system of *hooks*: these add actions to the tangling process:
|
|
181
217
|
|
|
182
218
|
- `build` trigger actions in a generated `Makefile`
|
|
@@ -185,6 +221,7 @@ Entangled has a system of *hooks*: these add actions to the tangling process:
|
|
|
185
221
|
- `shebang` takes the first line if it starts with `#!` and puts it at the top of the file.
|
|
186
222
|
|
|
187
223
|
### `build` hook
|
|
224
|
+
|
|
188
225
|
You can enable this hook in `entangled.toml`:
|
|
189
226
|
|
|
190
227
|
```toml
|
|
@@ -253,6 +290,7 @@ plot.savefig("fig/plot.svg")
|
|
|
253
290
|
The snippet for generating the data is given as a dependency for that data; to generate the figure, both `result.csv` and the code snippet are dependencies.
|
|
254
291
|
|
|
255
292
|
### `quarto_attributes` hook
|
|
293
|
+
|
|
256
294
|
Sometimes using the `build` hook (or the `brei` hook, see below), leads to long header lines. It is then better to specify attributes in a header section of your code. The Quarto project came up with a syntax, having the header be indicated by a comment with a vertical bar, e.g. `#|` or `//|` etc. The `quarto_attributes` hook reads those attributes and adds them to the properties of the code block.
|
|
257
295
|
|
|
258
296
|
Example with the `brei` hook:
|
|
@@ -301,6 +339,7 @@ if __name__ == "__main__":
|
|
|
301
339
|
The `id` attribute is reserved for the code's identifier (normally indicated with `#`) and the `classes` attribute can be used to indicate a list of classes in addition to the language class already given.
|
|
302
340
|
|
|
303
341
|
## Brei
|
|
342
|
+
|
|
304
343
|
Entangled has a small build engine (similar to GNU Make) embedded, called Brei. You may give it a list of tasks (specified in TOML) that may depend on one another. Brei will run these when dependencies are newer than the target. Execution is lazy and in parallel. Brei supports:
|
|
305
344
|
|
|
306
345
|
- Running tasks by passing a script to any configured interpreter, e.g. Bash, Python, Lua etc.
|
|
@@ -313,6 +352,7 @@ Entangled has a small build engine (similar to GNU Make) embedded, called Brei.
|
|
|
313
352
|
Brei is available as a separate package, see [the Brei documentation](https://entangled.github.io/brei).
|
|
314
353
|
|
|
315
354
|
### Examples
|
|
355
|
+
|
|
316
356
|
To write out "Hello, World!" to a file `msg.txt`, we may do the following,
|
|
317
357
|
|
|
318
358
|
```toml
|
|
@@ -351,7 +391,8 @@ requires = ["msg.txt"]
|
|
|
351
391
|
```
|
|
352
392
|
|
|
353
393
|
### The `brei` hook
|
|
354
|
-
|
|
394
|
+
|
|
395
|
+
The following example uses both `brei` and `quatro_attributes` hooks. To add a Brei task, tag a code block with the `.task` class.
|
|
355
396
|
|
|
356
397
|
~~~markdown
|
|
357
398
|
First we generate some data.
|
|
@@ -403,9 +444,11 @@ entangled brei figures
|
|
|
403
444
|
You can use `${variable}` syntax inside Brei tasks just as you would in a stand-alone Brei script.
|
|
404
445
|
|
|
405
446
|
## Support for Document Generators
|
|
447
|
+
|
|
406
448
|
Entangled has been used successfully with the following document generators. Note that some of these examples were built using older versions of Entangled, but they should work just the same.
|
|
407
449
|
|
|
408
450
|
### Pandoc
|
|
451
|
+
|
|
409
452
|
[Pandoc](https://pandoc.org/) is a very versatile tool for converting documents in any format. It specifically has very wide support for different forms of Markdown syntax out in the wild, including a filter system that lets you extend the workings of Pandoc. Those filters can be written in any language through an API, for instance Python filters can be written using `panflute`, but there is also native support for Lua.
|
|
410
453
|
|
|
411
454
|
To work with Entangled style literate documents, there is a set of [Pandoc filters](https://github.com/entangled/filters) available. The major downside of Pandoc, is that it offers no help in making your output HTML look beautiful. One option is to use the [Bootstrap template](https://github.com/entangled/bootstrap), but you may wan't to try out others as well, or design your own. These days a lot can be done with a single well designed CSS file.
|
|
@@ -420,6 +463,7 @@ To work with Entangled style literate documents, there is a set of [Pandoc filte
|
|
|
420
463
|
Example: [Hello World in C++](https://entangled.github.io/examples/hello-world.html)
|
|
421
464
|
|
|
422
465
|
### MkDocs
|
|
466
|
+
|
|
423
467
|
[MkDocs](https://www.mkdocs.org/) is specifically taylored towards converting Markdown into good looking, easy to navigate HTML, especially when used in combination with the [`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/) theme. To use Entangled style code blocks with MkDocs, you'll need to install the [`mkdocs-entangled-plugin`](https://github.com/entangled/mkdocs-plugin) as well.
|
|
424
468
|
|
|
425
469
|
- :heavy_plus_sign: specifically designed for Markdown to HTML, i.e. software documentation
|
|
@@ -431,6 +475,7 @@ Example: [Hello World in C++](https://entangled.github.io/examples/hello-world.h
|
|
|
431
475
|
Example: TBD
|
|
432
476
|
|
|
433
477
|
### Typst
|
|
478
|
+
|
|
434
479
|
[Typst](https://typst.app/) has a syntax that is similar to Markdown when it comes to code blocks. Set the code block markers in `entangled.toml` like so:
|
|
435
480
|
|
|
436
481
|
```toml
|
|
@@ -444,27 +489,32 @@ close="^(?P<indent>\\s*)```\\s*$"
|
|
|
444
489
|
```
|
|
445
490
|
|
|
446
491
|
### Documenter.jl
|
|
492
|
+
|
|
447
493
|
[Documenter.jl]() is the standard tool to write Julia documention in. It has internal support for evaluating code block contents.
|
|
448
494
|
|
|
449
495
|
Example: [Intro to code generation in Julia](https://jhidding.github.io/MacroExercises.jl/dev/)
|
|
450
496
|
|
|
451
497
|
### PDoc
|
|
498
|
+
|
|
452
499
|
[PDoc]() is a tool for documenting smaller Python projects. It grabs all documentation from the doc-strings in your Python library and generates a page from that. To have it include its own literate source, I had to use some very ugly hacks.
|
|
453
500
|
|
|
454
501
|
Example: [check-deps, a Universal dependency checker in Python](https://jhidding.github.io/check-deps/checkdeps.html)
|
|
455
502
|
|
|
456
503
|
### Docsify
|
|
504
|
+
|
|
457
505
|
[Docsify](https://docsify.js.org/#/) serves the markdown files and does the conversion to HTML in a Javascript library (in browser).
|
|
458
506
|
|
|
459
507
|
Example: [Guide to C++ on the web through WASM](https://nlesc-jcer.github.io/cpp2wasm/#/)
|
|
460
508
|
|
|
461
509
|
## History
|
|
510
|
+
|
|
462
511
|
This is a rewrite of Entangled in Python. Older versions were written in Haskell. The rewrite in Python was motivated by ease of installation, larger community and quite frankly, a fit of mental derangement.
|
|
463
512
|
|
|
464
513
|
## Contributing
|
|
514
|
+
|
|
465
515
|
If you have an idea for improving Entangled, please file an issue before creating a pull request. Code in this repository is formatted using `black` and type checked using `mypy`.
|
|
466
516
|
|
|
467
517
|
## License
|
|
468
|
-
Copyright 2023 Netherlands eScience Center, written by Johan Hidding, licensed under the Apache 2 license, see LICENSE.
|
|
469
518
|
|
|
519
|
+
Copyright 2023 Netherlands eScience Center, written by Johan Hidding, licensed under the Apache 2 license, see LICENSE.
|
|
470
520
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Entangled
|
|
2
|
+
|
|
2
3
|
[](https://github.com/entangled/entangled.py/actions/workflows/deploy.yml)
|
|
3
4
|
[](https://github.com/entangled/entangled.py/actions/workflows/python-package.yml)
|
|
4
5
|
|
|
@@ -16,6 +17,7 @@ We’re trying to increase the visibility of Entangled. If you like Entangled, p
|
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
## Get started
|
|
20
|
+
|
|
19
21
|
To install Entangled, all you need is a Python (version ≥3.11) installation. If you use [`poetry`](https://python-poetry.org), and you start a new project,
|
|
20
22
|
|
|
21
23
|
```
|
|
@@ -32,6 +34,7 @@ pip install entangled-cli
|
|
|
32
34
|
```
|
|
33
35
|
|
|
34
36
|
## Use
|
|
37
|
+
|
|
35
38
|
Run the `entangled watch` daemon in the root of your project folder. By default all Markdown files are monitored for fenced code blocks like so:
|
|
36
39
|
|
|
37
40
|
~~~markdown
|
|
@@ -43,6 +46,7 @@ Run the `entangled watch` daemon in the root of your project folder. By default
|
|
|
43
46
|
The syntax of code block properties is the same as CSS properties: `#hello` gives the block the `hello` identifier, `.rust` adds the `rust` class and the `file` attribute is set to `src/world.rs` (quotes are optional). For Entangled to know how to tangle this block, you need to specify a language and a target file. However, now comes the cool stuff. We can split our code in meaningful components by cross-refrences.
|
|
44
47
|
|
|
45
48
|
### Hello World in C++
|
|
49
|
+
|
|
46
50
|
The combined code-blocks in this example compose a compilable source code for "Hello World". For didactic reasons we don't always give the listing of an entire source file in one go. In stead, we use a system of references known as *noweb* (after Ramsey 1994).
|
|
47
51
|
|
|
48
52
|
Inside source fragments you may encounter a line with `<<...>>` marks like,
|
|
@@ -86,6 +90,7 @@ return EXIT_SUCCESS;
|
|
|
86
90
|
These blocks of code can be *tangled* into source files.
|
|
87
91
|
|
|
88
92
|
### Configuring
|
|
93
|
+
|
|
89
94
|
Entangled is configured by putting a `entangled.toml` in the root of your project.
|
|
90
95
|
|
|
91
96
|
```toml
|
|
@@ -149,7 +154,38 @@ identifiers = ["java"]
|
|
|
149
154
|
comment = { open = "//" }
|
|
150
155
|
```
|
|
151
156
|
|
|
157
|
+
## Working with Git
|
|
158
|
+
|
|
159
|
+
When using Entangled in conjunction with Git, there are a few tricks that you may want to know about.
|
|
160
|
+
|
|
161
|
+
### Restoring files when both Markdown and code have changed
|
|
162
|
+
|
|
163
|
+
When you edited both Markdown and code without the daemon running, you may need to do some tricks to get back into a consistent state.
|
|
164
|
+
|
|
165
|
+
```shell
|
|
166
|
+
git add .
|
|
167
|
+
git commit -m 'fixed everything' # save everything you did
|
|
168
|
+
entangled tangle --force # overwrites some changes you made
|
|
169
|
+
git restore src/brilliant_code.c # retrieve from latest commit
|
|
170
|
+
entangled stitch # apply changes back to markdown
|
|
171
|
+
git add .
|
|
172
|
+
git commit --amend # amend your commit to perfection
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
There may be better/faster ways to do this.
|
|
176
|
+
|
|
177
|
+
### Entangled conflicts after merging branches
|
|
178
|
+
|
|
179
|
+
Entangled can get confused when you merge, and there is a conflict on `.entangled/filedb.json`. This file keeps track of which files are sources for Entangled and which ones are generated by Entangled. That way, Entangled will never overwrite files it isn't supposed to, and the other way around, when you rename a target, the old one gets removed. It is very hard to merge this file though. When you need to, you can regenerate this file using:
|
|
180
|
+
|
|
181
|
+
```shell
|
|
182
|
+
entangled tangle -r
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
This will perform the tangle as if it is the first time, but it won't actually write files.
|
|
186
|
+
|
|
152
187
|
## Hooks
|
|
188
|
+
|
|
153
189
|
Entangled has a system of *hooks*: these add actions to the tangling process:
|
|
154
190
|
|
|
155
191
|
- `build` trigger actions in a generated `Makefile`
|
|
@@ -158,6 +194,7 @@ Entangled has a system of *hooks*: these add actions to the tangling process:
|
|
|
158
194
|
- `shebang` takes the first line if it starts with `#!` and puts it at the top of the file.
|
|
159
195
|
|
|
160
196
|
### `build` hook
|
|
197
|
+
|
|
161
198
|
You can enable this hook in `entangled.toml`:
|
|
162
199
|
|
|
163
200
|
```toml
|
|
@@ -226,6 +263,7 @@ plot.savefig("fig/plot.svg")
|
|
|
226
263
|
The snippet for generating the data is given as a dependency for that data; to generate the figure, both `result.csv` and the code snippet are dependencies.
|
|
227
264
|
|
|
228
265
|
### `quarto_attributes` hook
|
|
266
|
+
|
|
229
267
|
Sometimes using the `build` hook (or the `brei` hook, see below), leads to long header lines. It is then better to specify attributes in a header section of your code. The Quarto project came up with a syntax, having the header be indicated by a comment with a vertical bar, e.g. `#|` or `//|` etc. The `quarto_attributes` hook reads those attributes and adds them to the properties of the code block.
|
|
230
268
|
|
|
231
269
|
Example with the `brei` hook:
|
|
@@ -274,6 +312,7 @@ if __name__ == "__main__":
|
|
|
274
312
|
The `id` attribute is reserved for the code's identifier (normally indicated with `#`) and the `classes` attribute can be used to indicate a list of classes in addition to the language class already given.
|
|
275
313
|
|
|
276
314
|
## Brei
|
|
315
|
+
|
|
277
316
|
Entangled has a small build engine (similar to GNU Make) embedded, called Brei. You may give it a list of tasks (specified in TOML) that may depend on one another. Brei will run these when dependencies are newer than the target. Execution is lazy and in parallel. Brei supports:
|
|
278
317
|
|
|
279
318
|
- Running tasks by passing a script to any configured interpreter, e.g. Bash, Python, Lua etc.
|
|
@@ -286,6 +325,7 @@ Entangled has a small build engine (similar to GNU Make) embedded, called Brei.
|
|
|
286
325
|
Brei is available as a separate package, see [the Brei documentation](https://entangled.github.io/brei).
|
|
287
326
|
|
|
288
327
|
### Examples
|
|
328
|
+
|
|
289
329
|
To write out "Hello, World!" to a file `msg.txt`, we may do the following,
|
|
290
330
|
|
|
291
331
|
```toml
|
|
@@ -324,7 +364,8 @@ requires = ["msg.txt"]
|
|
|
324
364
|
```
|
|
325
365
|
|
|
326
366
|
### The `brei` hook
|
|
327
|
-
|
|
367
|
+
|
|
368
|
+
The following example uses both `brei` and `quatro_attributes` hooks. To add a Brei task, tag a code block with the `.task` class.
|
|
328
369
|
|
|
329
370
|
~~~markdown
|
|
330
371
|
First we generate some data.
|
|
@@ -376,9 +417,11 @@ entangled brei figures
|
|
|
376
417
|
You can use `${variable}` syntax inside Brei tasks just as you would in a stand-alone Brei script.
|
|
377
418
|
|
|
378
419
|
## Support for Document Generators
|
|
420
|
+
|
|
379
421
|
Entangled has been used successfully with the following document generators. Note that some of these examples were built using older versions of Entangled, but they should work just the same.
|
|
380
422
|
|
|
381
423
|
### Pandoc
|
|
424
|
+
|
|
382
425
|
[Pandoc](https://pandoc.org/) is a very versatile tool for converting documents in any format. It specifically has very wide support for different forms of Markdown syntax out in the wild, including a filter system that lets you extend the workings of Pandoc. Those filters can be written in any language through an API, for instance Python filters can be written using `panflute`, but there is also native support for Lua.
|
|
383
426
|
|
|
384
427
|
To work with Entangled style literate documents, there is a set of [Pandoc filters](https://github.com/entangled/filters) available. The major downside of Pandoc, is that it offers no help in making your output HTML look beautiful. One option is to use the [Bootstrap template](https://github.com/entangled/bootstrap), but you may wan't to try out others as well, or design your own. These days a lot can be done with a single well designed CSS file.
|
|
@@ -393,6 +436,7 @@ To work with Entangled style literate documents, there is a set of [Pandoc filte
|
|
|
393
436
|
Example: [Hello World in C++](https://entangled.github.io/examples/hello-world.html)
|
|
394
437
|
|
|
395
438
|
### MkDocs
|
|
439
|
+
|
|
396
440
|
[MkDocs](https://www.mkdocs.org/) is specifically taylored towards converting Markdown into good looking, easy to navigate HTML, especially when used in combination with the [`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/) theme. To use Entangled style code blocks with MkDocs, you'll need to install the [`mkdocs-entangled-plugin`](https://github.com/entangled/mkdocs-plugin) as well.
|
|
397
441
|
|
|
398
442
|
- :heavy_plus_sign: specifically designed for Markdown to HTML, i.e. software documentation
|
|
@@ -404,6 +448,7 @@ Example: [Hello World in C++](https://entangled.github.io/examples/hello-world.h
|
|
|
404
448
|
Example: TBD
|
|
405
449
|
|
|
406
450
|
### Typst
|
|
451
|
+
|
|
407
452
|
[Typst](https://typst.app/) has a syntax that is similar to Markdown when it comes to code blocks. Set the code block markers in `entangled.toml` like so:
|
|
408
453
|
|
|
409
454
|
```toml
|
|
@@ -417,26 +462,31 @@ close="^(?P<indent>\\s*)```\\s*$"
|
|
|
417
462
|
```
|
|
418
463
|
|
|
419
464
|
### Documenter.jl
|
|
465
|
+
|
|
420
466
|
[Documenter.jl]() is the standard tool to write Julia documention in. It has internal support for evaluating code block contents.
|
|
421
467
|
|
|
422
468
|
Example: [Intro to code generation in Julia](https://jhidding.github.io/MacroExercises.jl/dev/)
|
|
423
469
|
|
|
424
470
|
### PDoc
|
|
471
|
+
|
|
425
472
|
[PDoc]() is a tool for documenting smaller Python projects. It grabs all documentation from the doc-strings in your Python library and generates a page from that. To have it include its own literate source, I had to use some very ugly hacks.
|
|
426
473
|
|
|
427
474
|
Example: [check-deps, a Universal dependency checker in Python](https://jhidding.github.io/check-deps/checkdeps.html)
|
|
428
475
|
|
|
429
476
|
### Docsify
|
|
477
|
+
|
|
430
478
|
[Docsify](https://docsify.js.org/#/) serves the markdown files and does the conversion to HTML in a Javascript library (in browser).
|
|
431
479
|
|
|
432
480
|
Example: [Guide to C++ on the web through WASM](https://nlesc-jcer.github.io/cpp2wasm/#/)
|
|
433
481
|
|
|
434
482
|
## History
|
|
483
|
+
|
|
435
484
|
This is a rewrite of Entangled in Python. Older versions were written in Haskell. The rewrite in Python was motivated by ease of installation, larger community and quite frankly, a fit of mental derangement.
|
|
436
485
|
|
|
437
486
|
## Contributing
|
|
487
|
+
|
|
438
488
|
If you have an idea for improving Entangled, please file an issue before creating a pull request. Code in this repository is formatted using `black` and type checked using `mypy`.
|
|
439
489
|
|
|
440
490
|
## License
|
|
441
|
-
Copyright 2023 Netherlands eScience Center, written by Johan Hidding, licensed under the Apache 2 license, see LICENSE.
|
|
442
491
|
|
|
492
|
+
Copyright 2023 Netherlands eScience Center, written by Johan Hidding, licensed under the Apache 2 license, see LICENSE.
|
|
@@ -12,6 +12,17 @@ from ..hooks import get_hooks
|
|
|
12
12
|
from ..errors.user import UserError
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
def get_input_files() -> list[Path]:
|
|
16
|
+
include_file_list = chain.from_iterable(map(Path(".").glob, config.watch_list))
|
|
17
|
+
exclude_file_list = list(
|
|
18
|
+
chain.from_iterable(map(Path(".").glob, config.ignore_list))
|
|
19
|
+
)
|
|
20
|
+
input_file_list = [
|
|
21
|
+
path for path in include_file_list if not path in exclude_file_list
|
|
22
|
+
]
|
|
23
|
+
return input_file_list
|
|
24
|
+
|
|
25
|
+
|
|
15
26
|
@argh.arg(
|
|
16
27
|
"-a",
|
|
17
28
|
"--annotate",
|
|
@@ -20,7 +31,8 @@ from ..errors.user import UserError
|
|
|
20
31
|
)
|
|
21
32
|
@argh.arg("--force", help="force overwrite on conflict")
|
|
22
33
|
@argh.arg("-s", "--show", help="only show, don't act")
|
|
23
|
-
|
|
34
|
+
@argh.arg("-r", "--reset-db", help="resets database")
|
|
35
|
+
def tangle(*, annotate: Optional[str] = None, force: bool = False, show: bool = False, reset_db = False):
|
|
24
36
|
"""Tangle codes from Markdown"""
|
|
25
37
|
config.read()
|
|
26
38
|
|
|
@@ -33,13 +45,7 @@ def tangle(*, annotate: Optional[str] = None, force: bool = False, show: bool =
|
|
|
33
45
|
else:
|
|
34
46
|
annotation_method = AnnotationMethod[annotate.upper()]
|
|
35
47
|
|
|
36
|
-
|
|
37
|
-
exclude_file_list = list(
|
|
38
|
-
chain.from_iterable(map(Path(".").glob, config.ignore_list))
|
|
39
|
-
)
|
|
40
|
-
input_file_list = [
|
|
41
|
-
path for path in include_file_list if not path in exclude_file_list
|
|
42
|
-
]
|
|
48
|
+
input_file_list = get_input_files()
|
|
43
49
|
|
|
44
50
|
refs = ReferenceMap()
|
|
45
51
|
hooks = get_hooks()
|
|
@@ -47,6 +53,8 @@ def tangle(*, annotate: Optional[str] = None, force: bool = False, show: bool =
|
|
|
47
53
|
|
|
48
54
|
if show:
|
|
49
55
|
mode = TransactionMode.SHOW
|
|
56
|
+
elif reset_db:
|
|
57
|
+
mode = TransactionMode.RESETDB
|
|
50
58
|
elif force:
|
|
51
59
|
mode = TransactionMode.FORCE
|
|
52
60
|
else:
|
|
@@ -103,8 +103,19 @@ class FileDB:
|
|
|
103
103
|
set(map(Path, raw["target"])),
|
|
104
104
|
)
|
|
105
105
|
|
|
106
|
+
def clear(self):
|
|
107
|
+
self._files = {}
|
|
108
|
+
self._source = set()
|
|
109
|
+
self._target = set()
|
|
110
|
+
|
|
106
111
|
@property
|
|
107
112
|
def managed(self) -> set[Path]:
|
|
113
|
+
"""List all managed files. These are files that can be reconstructed
|
|
114
|
+
from the sources, at least when things are in a consistent state.
|
|
115
|
+
|
|
116
|
+
For example: markdown sources cannot be reconstructed, so are not
|
|
117
|
+
listed here. However, generated code is first constructed from
|
|
118
|
+
the markdown, so is considered to be managed."""
|
|
108
119
|
return self._target
|
|
109
120
|
|
|
110
121
|
def write(self):
|
|
@@ -155,12 +166,17 @@ class FileDB:
|
|
|
155
166
|
db = FileDB.read()
|
|
156
167
|
undead = list(filter(lambda p: not p.exists(), db.files))
|
|
157
168
|
for path in undead:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
if path in db.managed:
|
|
170
|
+
logging.warning(
|
|
171
|
+
"File `%s` in DB seems not to exist, but this file is managed.\n"
|
|
172
|
+
"This may happen every now and then with certain editors that "
|
|
173
|
+
"delete a file before writing.", path
|
|
174
|
+
)
|
|
175
|
+
else:
|
|
176
|
+
logging.warning(
|
|
177
|
+
"File `%s` is in database but doesn't seem to exist.\n"
|
|
178
|
+
"Run `entangled tangle -r` to recreate the database.", path
|
|
179
|
+
)
|
|
164
180
|
return db
|
|
165
181
|
|
|
166
182
|
FileDB.path().parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -18,6 +18,10 @@ log = logger()
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def ensure_list(strs: str | list[str]) -> list[str]:
|
|
21
|
+
"""Some options may be given either as a list or as a single string,
|
|
22
|
+
where the string is supposed to have a whitespace separated list.
|
|
23
|
+
This function converts from either to a list of strings.
|
|
24
|
+
"""
|
|
21
25
|
if isinstance(strs, str):
|
|
22
26
|
return strs.split()
|
|
23
27
|
elif isinstance(strs, list):
|
|
@@ -30,6 +30,10 @@ class Action:
|
|
|
30
30
|
a file that is not managed by Entangled."""
|
|
31
31
|
raise NotImplementedError()
|
|
32
32
|
|
|
33
|
+
def add_to_db(self, _: FileDB):
|
|
34
|
+
"""Only perform the corresponding database action."""
|
|
35
|
+
raise NotImplementedError()
|
|
36
|
+
|
|
33
37
|
def run(self, _: FileDB):
|
|
34
38
|
"""Run the action, if `interact` is `True` then confirmation is
|
|
35
39
|
asked in case of a conflict."""
|
|
@@ -54,6 +58,11 @@ class Create(Action):
|
|
|
54
58
|
return f"{self.target} is not managed by Entangled"
|
|
55
59
|
return None
|
|
56
60
|
|
|
61
|
+
def add_to_db(self, db: FileDB):
|
|
62
|
+
db.update(self.target, self.sources)
|
|
63
|
+
if self.sources != []:
|
|
64
|
+
db.managed.add(self.target)
|
|
65
|
+
|
|
57
66
|
def run(self, db: FileDB):
|
|
58
67
|
self.target.parent.mkdir(parents=True, exist_ok=True)
|
|
59
68
|
# Write to tmp file then replace with file name
|
|
@@ -67,9 +76,7 @@ class Create(Action):
|
|
|
67
76
|
os.chmod(f.name, self.mode)
|
|
68
77
|
os.fsync(f.fileno())
|
|
69
78
|
os.replace(f.name, self.target)
|
|
70
|
-
|
|
71
|
-
if self.sources != []:
|
|
72
|
-
db.managed.add(self.target)
|
|
79
|
+
self.add_to_db(db)
|
|
73
80
|
|
|
74
81
|
def __str__(self):
|
|
75
82
|
return f"create `{self.target}`"
|
|
@@ -98,6 +105,9 @@ class Write(Action):
|
|
|
98
105
|
return f"`{self.target}` seems to be newer than `{newest_src.path}`"
|
|
99
106
|
return None
|
|
100
107
|
|
|
108
|
+
def add_to_db(self, db: FileDB):
|
|
109
|
+
db.update(self.target, self.sources)
|
|
110
|
+
|
|
101
111
|
def run(self, db: FileDB):
|
|
102
112
|
# Write to tmp file then replace with file name
|
|
103
113
|
tmp_dir = Path() / ".entangled" / "tmp"
|
|
@@ -110,7 +120,7 @@ class Write(Action):
|
|
|
110
120
|
os.chmod(f.name, self.mode)
|
|
111
121
|
os.fsync(f.fileno())
|
|
112
122
|
os.replace(f.name, self.target)
|
|
113
|
-
|
|
123
|
+
self.add_to_db(db)
|
|
114
124
|
|
|
115
125
|
def __str__(self):
|
|
116
126
|
return f"write `{self.target}`"
|
|
@@ -126,13 +136,16 @@ class Delete(Action):
|
|
|
126
136
|
)
|
|
127
137
|
return None
|
|
128
138
|
|
|
139
|
+
def add_to_db(self, db: FileDB):
|
|
140
|
+
del db[self.target]
|
|
141
|
+
|
|
129
142
|
def run(self, db: FileDB):
|
|
130
143
|
self.target.unlink()
|
|
131
144
|
parent = self.target.parent
|
|
132
145
|
while list(parent.iterdir()) == []:
|
|
133
146
|
parent.rmdir()
|
|
134
147
|
parent = parent.parent
|
|
135
|
-
|
|
148
|
+
self.add_to_db(db)
|
|
136
149
|
|
|
137
150
|
def __str__(self):
|
|
138
151
|
return f"delete `{self.target}`"
|
|
@@ -190,17 +203,27 @@ class Transaction:
|
|
|
190
203
|
for f in self.updates:
|
|
191
204
|
self.db.update(f)
|
|
192
205
|
|
|
206
|
+
def updatedb(self):
|
|
207
|
+
for a in self.actions:
|
|
208
|
+
a.add_to_db(self.db)
|
|
209
|
+
for f in self.updates:
|
|
210
|
+
self.db.update(f)
|
|
211
|
+
|
|
193
212
|
|
|
194
213
|
class TransactionMode(Enum):
|
|
195
214
|
SHOW = 1
|
|
196
215
|
FAIL = 2
|
|
197
216
|
CONFIRM = 3
|
|
198
217
|
FORCE = 4
|
|
218
|
+
RESETDB = 5
|
|
199
219
|
|
|
200
220
|
|
|
201
221
|
@contextmanager
|
|
202
222
|
def transaction(mode: TransactionMode = TransactionMode.FAIL):
|
|
203
223
|
with file_db() as db:
|
|
224
|
+
if mode == TransactionMode.RESETDB:
|
|
225
|
+
db.clear()
|
|
226
|
+
|
|
204
227
|
tr = Transaction(db)
|
|
205
228
|
|
|
206
229
|
logging.debug("Open transaction")
|
|
@@ -212,17 +235,25 @@ def transaction(mode: TransactionMode = TransactionMode.FAIL):
|
|
|
212
235
|
case TransactionMode.SHOW:
|
|
213
236
|
logging.info("nothing is done")
|
|
214
237
|
return
|
|
238
|
+
|
|
239
|
+
case TransactionMode.RESETDB:
|
|
240
|
+
logging.info("rebuilding database")
|
|
241
|
+
tr.updatedb()
|
|
242
|
+
return
|
|
243
|
+
|
|
215
244
|
case TransactionMode.FAIL:
|
|
216
245
|
if not tr.all_ok():
|
|
217
246
|
logging.error(
|
|
218
247
|
"conflicts found, breaking off (use `--force` to run anyway)"
|
|
219
248
|
)
|
|
220
249
|
return
|
|
250
|
+
|
|
221
251
|
case TransactionMode.CONFIRM:
|
|
222
252
|
if not tr.all_ok():
|
|
223
253
|
reply = input("Ok to continue? (y/n) ")
|
|
224
254
|
if not (reply == "y" or reply == "yes"):
|
|
225
255
|
return
|
|
256
|
+
|
|
226
257
|
case TransactionMode.FORCE:
|
|
227
258
|
logging.warning("conflicts found, but continuing anyway")
|
|
228
259
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|