entangled-cli 2.0.0b1__tar.gz → 2.0.0b2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. entangled_cli-2.0.0b2/PKG-INFO +258 -0
  2. entangled_cli-2.0.0b2/README.md +236 -0
  3. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/code_reader.py +0 -9
  4. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/commands/stitch.py +1 -1
  5. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/commands/tangle.py +6 -4
  6. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/commands/watch.py +1 -2
  7. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/config/__init__.py +38 -10
  8. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/config/language.py +2 -1
  9. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/construct.py +8 -7
  10. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/document.py +3 -41
  11. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/errors/internal.py +4 -2
  12. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/errors/user.py +8 -0
  13. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/hooks/__init__.py +1 -3
  14. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/hooks/base.py +4 -4
  15. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/hooks/build.py +28 -7
  16. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/main.py +9 -2
  17. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/markdown_reader.py +13 -6
  18. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/parsing.py +1 -0
  19. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/properties.py +10 -1
  20. entangled_cli-2.0.0b2/entangled/tangle.py +128 -0
  21. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/utility.py +0 -2
  22. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/pyproject.toml +5 -6
  23. entangled_cli-2.0.0b1/PKG-INFO +0 -106
  24. entangled_cli-2.0.0b1/README.md +0 -86
  25. entangled_cli-2.0.0b1/entangled/data/defaults.toml +0 -1
  26. entangled_cli-2.0.0b1/entangled/tangle.py +0 -59
  27. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/LICENSE +0 -0
  28. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/__init__.py +0 -0
  29. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/commands/__init__.py +0 -0
  30. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/commands/init.py +0 -0
  31. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/commands/sync.py +0 -0
  32. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/config/version.py +0 -0
  33. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/errors/__init__.py +0 -0
  34. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/filedb.py +0 -0
  35. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/py.typed +0 -0
  36. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/transaction.py +0 -0
  37. {entangled_cli-2.0.0b1 → entangled_cli-2.0.0b2}/entangled/version.py +0 -0
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.1
2
+ Name: entangled-cli
3
+ Version: 2.0.0b2
4
+ Summary: Literate Programming toolbox
5
+ Home-page: https://entangled.github.io/
6
+ License: Apache 2
7
+ Author: Johan Hidding
8
+ Author-email: j.hidding@esciencecenter.nl
9
+ Requires-Python: >=3.11,<4.0
10
+ Classifier: License :: Other/Proprietary License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Provides-Extra: rich
14
+ Requires-Dist: argh (>=0.28.1,<0.29.0)
15
+ Requires-Dist: filelock (>=3.12.0,<4.0.0)
16
+ Requires-Dist: mawk (>=0.1.4,<0.2.0)
17
+ Requires-Dist: rich (>=13.3.5,<14.0.0) ; extra == "rich"
18
+ Requires-Dist: watchdog (>=3.0.0,<4.0.0)
19
+ Project-URL: Repository, https://github.com/entangled/entangled.py
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Entangled
23
+ [![Upload Python Package](https://github.com/entangled/entangled.py/actions/workflows/deploy.yml/badge.svg)](https://github.com/entangled/entangled.py/actions/workflows/deploy.yml)
24
+ [![Python package](https://github.com/entangled/entangled.py/actions/workflows/python-package.yml/badge.svg)](https://github.com/entangled/entangled.py/actions/workflows/python-package.yml)
25
+
26
+ Entangled is a solution for Literate Programming, a technique in which the programmer writes a human narrative first, then implementing the program in code blocks.
27
+ Literate programming was introduced by Donald Knuth in 1984 and has since then found several surges in popularity. One thing holding back the popularity of literate programming is the lack of maintainability under increasing program complexity. Entangled solves this issue by offering a two-way synchronisation mechanism. You can edit and debug your code as normal in your favourite IDE or text editor. Entangled will make sure that your Markdown files stay up-to-date with your code and vice-versa. Because Entangled works with Markdown, you can use it with most static document generators. To summarise, you keep using:
28
+
29
+ - your favourite **editor**: Entangled runs as a daemon in the background, keeping your text files synchronised.
30
+ - your favourite **programming language**: Entangled is agnostic to programming languages.
31
+ - your favourite **document generator**: Entangled is configurable to any dialect of Markdown.
32
+
33
+ We’re trying to increase the visibility of Entangled. If you like Entangled, please consider adding this badge [![Entangled badge](https://img.shields.io/badge/entangled-Use%20the%20source!-%2300aeff)](https://entangled.github.io/) to the appropriate location in your project:
34
+
35
+ ```markdown
36
+ [![Entangled badge](https://img.shields.io/badge/entangled-Use%20the%20source!-%2300aeff)](https://entangled.github.io/)
37
+ ```
38
+
39
+ ## Get started
40
+ To install Entangled, all you need is a Python installation. If you use [`poetry`](https://python-poetry.org),
41
+
42
+ ```
43
+ poetry add entangled-cli[rich]
44
+ ```
45
+
46
+ Or, if you prefer `pip`,
47
+
48
+ ```
49
+ pip install entangled-cli[rich]
50
+ ```
51
+
52
+ ## Use
53
+
54
+ > :warning: **This version of Entangled is still in beta.** In general things are working as they should, but there may still be some rough edges in the user experience.
55
+
56
+ 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:
57
+
58
+ ~~~markdown
59
+ ``` {.rust #hello file="src/world.rs"}
60
+ ...
61
+ ```
62
+ ~~~
63
+
64
+ 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.
65
+
66
+ ### Hello World in C++
67
+ 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).
68
+
69
+ Inside source fragments you may encounter a line with `<<...>>` marks like,
70
+
71
+ ~~~markdown
72
+ ``` {.cpp file=hello_world.cc}
73
+ #include <cstdlib>
74
+ #include <iostream>
75
+
76
+ <<example-main-function>>
77
+ ```
78
+ ~~~
79
+
80
+ which is then elsewhere specified. Order doesn't matter,
81
+
82
+ ~~~markdown
83
+ ``` {.cpp #hello-world}
84
+ std::cout << "Hello, World!" << std::endl;
85
+ ```
86
+ ~~~
87
+
88
+ So we can reference the `<<hello-world>>` code block later on.
89
+
90
+ ~~~markdown
91
+ ``` {.cpp #example-main-function}
92
+ int main(int argc, char **argv)
93
+ {
94
+ <<hello-world>>
95
+ }
96
+ ```
97
+ ~~~
98
+
99
+ A definition can be appended with more code as follows (in this case, order does matter!):
100
+
101
+ ~~~markdown
102
+ ``` {.cpp #hello-world}
103
+ return EXIT_SUCCESS;
104
+ ```
105
+ ~~~
106
+
107
+ These blocks of code can be *tangled* into source files.
108
+
109
+ ### Configuring
110
+ Entangled is configured by putting a `entangled.toml` in the root of your project.
111
+
112
+ ```toml
113
+ # required: the minimum version of Entangled
114
+ version = "2.0"
115
+
116
+ # default watch_list is ["**/*.md"]
117
+ watch_list = ["docs/**/*.md"]
118
+ ```
119
+
120
+ You may add languages as follows:
121
+
122
+ ```toml
123
+ [[languages]]
124
+ name = "Java"
125
+ identifiers = ["java"]
126
+ comment = { open = "//" }
127
+
128
+ # Some languages have comments that are not terminated by
129
+ # newlines, like XML or CSS.
130
+ [[languages]]
131
+ name = "XML"
132
+ identifiers = ["xml", "html", "svg"]
133
+ comment = { open = "<!--", close = "-->" }
134
+ ```
135
+
136
+ The `identifiers` are the tags that you may use in your code block header to identify the language. Using the above config, you should be able to write:
137
+
138
+ ~~~markdown
139
+ ``` {.html file=index.html}
140
+ <!DOCTYPE html>
141
+ <html lang="en">
142
+ <<header>>
143
+ <<body>>
144
+ </html>
145
+ ```
146
+
147
+ And so on...
148
+ ~~~
149
+
150
+ ### Using the `build` hook
151
+ Entangled has a system of *hooks*, of which there exists currently only one: `build`. You can enable this hook in `entangled.toml`:
152
+
153
+ ```toml
154
+ version = "2.0"
155
+ watch_list = ["docs/**/*.md"]
156
+ hooks = ["build"]
157
+ ```
158
+
159
+ Then in your Markdown, you may enter Make code tagged with the `#build` id.
160
+
161
+ ~~~markdown
162
+ ``` {.make #build target=docs/fig/plot.svg}
163
+ docs/fig/plot.svg: src/plot.gnuplot
164
+ > gnuplot $< > $@
165
+ ```
166
+ ~~~
167
+
168
+ Here, `$<` is the Make variable for the first dependency, and `$@` is the Make variable for the target. So, running this through `make` will create `plot.svg` if it doesn't exist yet or if it is older than `plot.gnuplot`.
169
+
170
+ Entangled adds a preamble to `#build`,
171
+
172
+ ```make
173
+ .RECIPEPREFIX = >
174
+ .PHONY = all
175
+
176
+ all: {targets}
177
+
178
+ {rules}
179
+ ```
180
+
181
+ changing the prefix from `TAB` to `>` (you really want this), and collecting all `target=...` attributes into the `all` target.
182
+
183
+ Once you have the code in place to generate figures and markdown tables, you can use the syntax at your disposal to include those into your Markdown. In this example that would be
184
+
185
+ ~~~markdown
186
+ ![My awesome plot](fig/plot.svg)
187
+ ~~~
188
+
189
+ In the case of tables or other rich content, Standard Markdown (or CommonMark) has no syntax for including other Markdown files, so you'll have to check with your own document generator how to do that. In MkDocs, you could use `mkdocs-macro-plugin`, Pandoc has `pandoc-include`, etc.
190
+
191
+ #### Ideas for other hooks
192
+
193
+ > A: Did you hack it together?
194
+
195
+ > B: No, its hooked into the system.
196
+
197
+ > A: That's clever!
198
+
199
+ In principle, you could do a lot of things with the `build` hook, supposing that you're well versed in the secrets of GNU Make. However, I also want to make things easier to use. As a principle I would like Entangled to be as much independent of choice of document generator as possible. That means that some of the features you may want are actually best implemented as plugins for these document generators. One obvious example of an add-on that doesn't belong in the core of Entangled would be support for Jupyter. If your work is anything serious, meaning your computations take somewhat longer, you'll be much happier with a build system anyway :smile:!
200
+
201
+ That being said, candidates for hooks could be:
202
+
203
+ - *Code metadata*. Some code could use more meta data than just a name and language. One way to include metadata is by having a header that is separated with a three hyphen line `---` from the actual code content. A hook could change the way the code is tangled, possibly injecting the metadata as a docstring, or leaving it out of the tangled code and have the document generator use it for other purposes.
204
+ - *She-bang lines*. When you're coding scripts, it may be desirable to have `#!/bin/bash` equivalent line at the top. This is currently not supported in the Python port of Entangled.
205
+ - *Integration with package managers* like `cargo`, `cabal`, `poetry` etc. These are usually configured in a separate file. A hook could be used to specify dependencies. That way you could also document why a certain dependency is needed, or why it needs to have the version you specify.
206
+
207
+ ## Support for Document Generators
208
+ 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.
209
+
210
+ ### Pandoc
211
+ [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.
212
+
213
+ 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.
214
+
215
+ - :heavy_plus_sign: dynamic
216
+ - :heavy_plus_sign: supports most Markdown syntax out of the box
217
+ - :heavy_plus_sign: excellent for science: citation, numbered figures, tables and equations
218
+ - :heavy_plus_sign: support for LaTeX
219
+ - :heavy_minus_sign: harder to setup
220
+ - :heavy_minus_sign: takes work to make look good
221
+
222
+ Example: [Hello World in C++](https://entangled.github.io/examples/hello-world.html)
223
+
224
+ ### MkDocs
225
+ [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.
226
+
227
+ - :heavy_plus_sign: specifically designed for Markdown to HTML, i.e. software documentation
228
+ - :heavy_plus_sign: pretty output, out of the box
229
+ - :heavy_plus_sign: easy to install
230
+ - :heavy_minus_sign: not intended for scientific use: numbering and referencing equations, figures and tables is hard to setup
231
+ - :heavy_minus_sign: documentation is on par with most Python projects: Ok for most things, but really hard if you want specifics
232
+
233
+ Example: TBD
234
+
235
+ ### Documenter.jl
236
+ [Documenter.jl]() is the standard tool to write Julia documention in. It has internal support for evaluating code block contents.
237
+
238
+ Example: [Intro to code generation in Julia](https://jhidding.github.io/MacroExercises.jl/dev/)
239
+
240
+ ### PDoc
241
+ [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.
242
+
243
+ Example: [check-deps, a Universal dependency checker in Python](https://jhidding.github.io/check-deps/checkdeps.html)
244
+
245
+ ### Docsify
246
+ [Docsify](https://docsify.js.org/#/) serves the markdown files and does the conversion to HTML in a Javascript library (in browser).
247
+
248
+ Example: [Guide to C++ on the web through WASM](https://nlesc-jcer.github.io/cpp2wasm/#/)
249
+
250
+ ## History
251
+ 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.
252
+
253
+ ## Contributing
254
+ 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`.
255
+
256
+ ## License
257
+ Copyright 2023 Netherlands eScience Center, written by Johan Hidding, licensed under the Apache 2 license, see LICENSE.
258
+
@@ -0,0 +1,236 @@
1
+ # Entangled
2
+ [![Upload Python Package](https://github.com/entangled/entangled.py/actions/workflows/deploy.yml/badge.svg)](https://github.com/entangled/entangled.py/actions/workflows/deploy.yml)
3
+ [![Python package](https://github.com/entangled/entangled.py/actions/workflows/python-package.yml/badge.svg)](https://github.com/entangled/entangled.py/actions/workflows/python-package.yml)
4
+
5
+ Entangled is a solution for Literate Programming, a technique in which the programmer writes a human narrative first, then implementing the program in code blocks.
6
+ Literate programming was introduced by Donald Knuth in 1984 and has since then found several surges in popularity. One thing holding back the popularity of literate programming is the lack of maintainability under increasing program complexity. Entangled solves this issue by offering a two-way synchronisation mechanism. You can edit and debug your code as normal in your favourite IDE or text editor. Entangled will make sure that your Markdown files stay up-to-date with your code and vice-versa. Because Entangled works with Markdown, you can use it with most static document generators. To summarise, you keep using:
7
+
8
+ - your favourite **editor**: Entangled runs as a daemon in the background, keeping your text files synchronised.
9
+ - your favourite **programming language**: Entangled is agnostic to programming languages.
10
+ - your favourite **document generator**: Entangled is configurable to any dialect of Markdown.
11
+
12
+ We’re trying to increase the visibility of Entangled. If you like Entangled, please consider adding this badge [![Entangled badge](https://img.shields.io/badge/entangled-Use%20the%20source!-%2300aeff)](https://entangled.github.io/) to the appropriate location in your project:
13
+
14
+ ```markdown
15
+ [![Entangled badge](https://img.shields.io/badge/entangled-Use%20the%20source!-%2300aeff)](https://entangled.github.io/)
16
+ ```
17
+
18
+ ## Get started
19
+ To install Entangled, all you need is a Python installation. If you use [`poetry`](https://python-poetry.org),
20
+
21
+ ```
22
+ poetry add entangled-cli[rich]
23
+ ```
24
+
25
+ Or, if you prefer `pip`,
26
+
27
+ ```
28
+ pip install entangled-cli[rich]
29
+ ```
30
+
31
+ ## Use
32
+
33
+ > :warning: **This version of Entangled is still in beta.** In general things are working as they should, but there may still be some rough edges in the user experience.
34
+
35
+ 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
+
37
+ ~~~markdown
38
+ ``` {.rust #hello file="src/world.rs"}
39
+ ...
40
+ ```
41
+ ~~~
42
+
43
+ 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
+
45
+ ### Hello World in C++
46
+ 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
+
48
+ Inside source fragments you may encounter a line with `<<...>>` marks like,
49
+
50
+ ~~~markdown
51
+ ``` {.cpp file=hello_world.cc}
52
+ #include <cstdlib>
53
+ #include <iostream>
54
+
55
+ <<example-main-function>>
56
+ ```
57
+ ~~~
58
+
59
+ which is then elsewhere specified. Order doesn't matter,
60
+
61
+ ~~~markdown
62
+ ``` {.cpp #hello-world}
63
+ std::cout << "Hello, World!" << std::endl;
64
+ ```
65
+ ~~~
66
+
67
+ So we can reference the `<<hello-world>>` code block later on.
68
+
69
+ ~~~markdown
70
+ ``` {.cpp #example-main-function}
71
+ int main(int argc, char **argv)
72
+ {
73
+ <<hello-world>>
74
+ }
75
+ ```
76
+ ~~~
77
+
78
+ A definition can be appended with more code as follows (in this case, order does matter!):
79
+
80
+ ~~~markdown
81
+ ``` {.cpp #hello-world}
82
+ return EXIT_SUCCESS;
83
+ ```
84
+ ~~~
85
+
86
+ These blocks of code can be *tangled* into source files.
87
+
88
+ ### Configuring
89
+ Entangled is configured by putting a `entangled.toml` in the root of your project.
90
+
91
+ ```toml
92
+ # required: the minimum version of Entangled
93
+ version = "2.0"
94
+
95
+ # default watch_list is ["**/*.md"]
96
+ watch_list = ["docs/**/*.md"]
97
+ ```
98
+
99
+ You may add languages as follows:
100
+
101
+ ```toml
102
+ [[languages]]
103
+ name = "Java"
104
+ identifiers = ["java"]
105
+ comment = { open = "//" }
106
+
107
+ # Some languages have comments that are not terminated by
108
+ # newlines, like XML or CSS.
109
+ [[languages]]
110
+ name = "XML"
111
+ identifiers = ["xml", "html", "svg"]
112
+ comment = { open = "<!--", close = "-->" }
113
+ ```
114
+
115
+ The `identifiers` are the tags that you may use in your code block header to identify the language. Using the above config, you should be able to write:
116
+
117
+ ~~~markdown
118
+ ``` {.html file=index.html}
119
+ <!DOCTYPE html>
120
+ <html lang="en">
121
+ <<header>>
122
+ <<body>>
123
+ </html>
124
+ ```
125
+
126
+ And so on...
127
+ ~~~
128
+
129
+ ### Using the `build` hook
130
+ Entangled has a system of *hooks*, of which there exists currently only one: `build`. You can enable this hook in `entangled.toml`:
131
+
132
+ ```toml
133
+ version = "2.0"
134
+ watch_list = ["docs/**/*.md"]
135
+ hooks = ["build"]
136
+ ```
137
+
138
+ Then in your Markdown, you may enter Make code tagged with the `#build` id.
139
+
140
+ ~~~markdown
141
+ ``` {.make #build target=docs/fig/plot.svg}
142
+ docs/fig/plot.svg: src/plot.gnuplot
143
+ > gnuplot $< > $@
144
+ ```
145
+ ~~~
146
+
147
+ Here, `$<` is the Make variable for the first dependency, and `$@` is the Make variable for the target. So, running this through `make` will create `plot.svg` if it doesn't exist yet or if it is older than `plot.gnuplot`.
148
+
149
+ Entangled adds a preamble to `#build`,
150
+
151
+ ```make
152
+ .RECIPEPREFIX = >
153
+ .PHONY = all
154
+
155
+ all: {targets}
156
+
157
+ {rules}
158
+ ```
159
+
160
+ changing the prefix from `TAB` to `>` (you really want this), and collecting all `target=...` attributes into the `all` target.
161
+
162
+ Once you have the code in place to generate figures and markdown tables, you can use the syntax at your disposal to include those into your Markdown. In this example that would be
163
+
164
+ ~~~markdown
165
+ ![My awesome plot](fig/plot.svg)
166
+ ~~~
167
+
168
+ In the case of tables or other rich content, Standard Markdown (or CommonMark) has no syntax for including other Markdown files, so you'll have to check with your own document generator how to do that. In MkDocs, you could use `mkdocs-macro-plugin`, Pandoc has `pandoc-include`, etc.
169
+
170
+ #### Ideas for other hooks
171
+
172
+ > A: Did you hack it together?
173
+
174
+ > B: No, its hooked into the system.
175
+
176
+ > A: That's clever!
177
+
178
+ In principle, you could do a lot of things with the `build` hook, supposing that you're well versed in the secrets of GNU Make. However, I also want to make things easier to use. As a principle I would like Entangled to be as much independent of choice of document generator as possible. That means that some of the features you may want are actually best implemented as plugins for these document generators. One obvious example of an add-on that doesn't belong in the core of Entangled would be support for Jupyter. If your work is anything serious, meaning your computations take somewhat longer, you'll be much happier with a build system anyway :smile:!
179
+
180
+ That being said, candidates for hooks could be:
181
+
182
+ - *Code metadata*. Some code could use more meta data than just a name and language. One way to include metadata is by having a header that is separated with a three hyphen line `---` from the actual code content. A hook could change the way the code is tangled, possibly injecting the metadata as a docstring, or leaving it out of the tangled code and have the document generator use it for other purposes.
183
+ - *She-bang lines*. When you're coding scripts, it may be desirable to have `#!/bin/bash` equivalent line at the top. This is currently not supported in the Python port of Entangled.
184
+ - *Integration with package managers* like `cargo`, `cabal`, `poetry` etc. These are usually configured in a separate file. A hook could be used to specify dependencies. That way you could also document why a certain dependency is needed, or why it needs to have the version you specify.
185
+
186
+ ## Support for Document Generators
187
+ 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.
188
+
189
+ ### Pandoc
190
+ [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.
191
+
192
+ 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.
193
+
194
+ - :heavy_plus_sign: dynamic
195
+ - :heavy_plus_sign: supports most Markdown syntax out of the box
196
+ - :heavy_plus_sign: excellent for science: citation, numbered figures, tables and equations
197
+ - :heavy_plus_sign: support for LaTeX
198
+ - :heavy_minus_sign: harder to setup
199
+ - :heavy_minus_sign: takes work to make look good
200
+
201
+ Example: [Hello World in C++](https://entangled.github.io/examples/hello-world.html)
202
+
203
+ ### MkDocs
204
+ [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.
205
+
206
+ - :heavy_plus_sign: specifically designed for Markdown to HTML, i.e. software documentation
207
+ - :heavy_plus_sign: pretty output, out of the box
208
+ - :heavy_plus_sign: easy to install
209
+ - :heavy_minus_sign: not intended for scientific use: numbering and referencing equations, figures and tables is hard to setup
210
+ - :heavy_minus_sign: documentation is on par with most Python projects: Ok for most things, but really hard if you want specifics
211
+
212
+ Example: TBD
213
+
214
+ ### Documenter.jl
215
+ [Documenter.jl]() is the standard tool to write Julia documention in. It has internal support for evaluating code block contents.
216
+
217
+ Example: [Intro to code generation in Julia](https://jhidding.github.io/MacroExercises.jl/dev/)
218
+
219
+ ### PDoc
220
+ [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.
221
+
222
+ Example: [check-deps, a Universal dependency checker in Python](https://jhidding.github.io/check-deps/checkdeps.html)
223
+
224
+ ### Docsify
225
+ [Docsify](https://docsify.js.org/#/) serves the markdown files and does the conversion to HTML in a Javascript library (in browser).
226
+
227
+ Example: [Guide to C++ on the web through WASM](https://nlesc-jcer.github.io/cpp2wasm/#/)
228
+
229
+ ## History
230
+ 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.
231
+
232
+ ## Contributing
233
+ 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`.
234
+
235
+ ## License
236
+ Copyright 2023 Netherlands eScience Center, written by Johan Hidding, licensed under the Apache 2 license, see LICENSE.
@@ -8,15 +8,6 @@ from .document import ReferenceId, TextLocation, ReferenceMap
8
8
  from .errors.user import IndentationError
9
9
 
10
10
 
11
- @dataclass
12
- class StitchError(Exception):
13
- path: Path
14
- msg: str
15
-
16
- def __str__(self):
17
- return f"indentation error in `{self.path}`"
18
-
19
-
20
11
  @dataclass
21
12
  class Frame:
22
13
  ref: ReferenceId
@@ -56,6 +56,6 @@ def stitch(*, force: bool = False, show: bool = False):
56
56
 
57
57
  for path in input_file_list:
58
58
  t.write(path, stitch_markdown(refs, content[path]), [])
59
-
59
+
60
60
  except UserError as e:
61
61
  logging.error(str(e))
@@ -24,8 +24,10 @@ from ..errors.user import UserError
24
24
  @argh.arg("-s", "--show", help="only show, don't act")
25
25
  def tangle(*, annotate: Optional[str] = None, force: bool = False, show: bool = False):
26
26
  """Tangle codes from Markdown"""
27
- if annotate is not None:
28
- config.annotation = AnnotationMethod[annotate.upper()]
27
+ if annotate is None:
28
+ annotation_method = config.annotation
29
+ else:
30
+ annotation_method = AnnotationMethod[annotate.upper()]
29
31
 
30
32
  input_file_list = chain.from_iterable(map(Path(".").glob, config.watch_list))
31
33
  refs = ReferenceMap()
@@ -47,7 +49,7 @@ def tangle(*, annotate: Optional[str] = None, force: bool = False, show: bool =
47
49
  MarkdownReader(str(path), refs, hooks).run(f.read())
48
50
 
49
51
  for tgt in refs.targets:
50
- result, deps = tangle_ref(refs, tgt)
52
+ result, deps = tangle_ref(refs, tgt, annotation_method)
51
53
  t.write(Path(tgt), result, list(map(Path, deps)))
52
54
  t.clear_orphans()
53
55
 
@@ -55,4 +57,4 @@ def tangle(*, annotate: Optional[str] = None, force: bool = False, show: bool =
55
57
  h.post_tangle(refs)
56
58
 
57
59
  except UserError as e:
58
- logging.error(str(e))
60
+ logging.error(str(e))
@@ -1,7 +1,6 @@
1
1
  from typing import Optional
2
2
  from pathlib import Path
3
3
  from itertools import chain
4
- import logging
5
4
  from threading import Event
6
5
 
7
6
  from watchdog.observers import Observer
@@ -37,7 +36,7 @@ def _watch(_stop_event: Optional[Event] = None):
37
36
 
38
37
  def stop() -> bool:
39
38
  return _stop_event is not None and _stop_event.is_set()
40
-
39
+
41
40
  sync()
42
41
 
43
42
  event_handler = EventHandler()
@@ -9,12 +9,15 @@ from enum import Enum
9
9
  from dataclasses import dataclass, field
10
10
  from copy import copy
11
11
  from pathlib import Path
12
+ from contextlib import contextmanager
12
13
 
13
14
  from ..construct import construct
14
15
  from .version import Version
15
16
  from .language import Language, languages
16
17
 
18
+ import threading
17
19
  import tomllib
20
+ import logging
18
21
 
19
22
 
20
23
  class AnnotationMethod(Enum):
@@ -47,13 +50,14 @@ markers = Markers(
47
50
  r"^(?P<indent>\s*)```\s*{(?P<properties>[^{}]*)}\s*$",
48
51
  r"^(?P<indent>\s*)```\s*$",
49
52
  r"^\s*\~\~\~markdown\s*$",
50
- r"^\s*\~\~\~\s*$"
53
+ r"^\s*\~\~\~\s*$",
51
54
  )
52
55
 
53
56
 
54
57
  @dataclass
55
- class Config:
56
- """Main config class."""
58
+ class Config(threading.local):
59
+ """Main config class. This class is made thread-local to make
60
+ it possible to test in parallel."""
57
61
 
58
62
  version: Version
59
63
  languages: list[Language] = field(default_factory=list)
@@ -74,9 +78,6 @@ class Config:
74
78
  for i in l.identifiers:
75
79
  self.language_index[i] = l
76
80
 
77
- def get_language(self, lang_name: str) -> Optional[Language]:
78
- return self.language_index.get(lang_name, None)
79
-
80
81
 
81
82
  default = Config(Version.from_string("2.0"))
82
83
 
@@ -84,11 +85,38 @@ default = Config(Version.from_string("2.0"))
84
85
  def read_config():
85
86
  if not Path("./entangled.toml").exists():
86
87
  return default
87
- with open(Path("./entangled.toml"), "rb") as f:
88
- json = tomllib.load(f)
89
- return construct(Config, json)
88
+ try:
89
+ with open(Path("./entangled.toml"), "rb") as f:
90
+ json = tomllib.load(f)
91
+ return construct(Config, json)
92
+ except ValueError as e:
93
+ logging.error("Could not read config: %s", e)
94
+ return default
95
+
96
+
97
+ class ConfigWrapper:
98
+ def __init__(self, config):
99
+ self.config = config
100
+
101
+ def read(self):
102
+ self.config = read_config()
103
+
104
+ def __getattr__(self, attr):
105
+ return getattr(self.config, attr)
106
+
107
+ @contextmanager
108
+ def __call__(self, **kwargs):
109
+ backup = {k: getattr(self.config, k) for k in kwargs}
110
+ for k, v in kwargs.items():
111
+ setattr(self.config, k, v)
112
+ yield
113
+ for k in kwargs.keys():
114
+ setattr(self.config, k, backup[k])
115
+
116
+ def get_language(self, lang_name: str) -> Optional[Language]:
117
+ return self.config.language_index.get(lang_name, None)
90
118
 
91
119
 
92
- config = read_config()
120
+ config = ConfigWrapper(read_config())
93
121
  """The `config.config` variable is changed when the `config` module is loaded.
94
122
  Config is read from `entangled.toml` file."""
@@ -26,6 +26,7 @@ class Language:
26
26
 
27
27
 
28
28
  languages = [
29
+ Language("Shell", ["sh", "bash", "fish", "zsh"], Comment("#")),
29
30
  Language("C", ["c", "cpp", "c++"], Comment("/*", "*/")),
30
31
  Language("Python", ["python"], Comment("#")),
31
32
  Language("Rust", ["rust"], Comment("//")),
@@ -39,5 +40,5 @@ languages = [
39
40
  Language("CSS", ["css"], Comment("/*", "*/")),
40
41
  Language("Lua", ["lua"], Comment("--")),
41
42
  Language("Make", ["make", "makefile"], Comment("#")),
42
- Language("Gnuplot", ["gnuplot"], Comment("#"))
43
+ Language("Gnuplot", ["gnuplot"], Comment("#")),
43
44
  ]