tagsnip 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tagsnip-1.0.0/LICENSE.md +21 -0
- tagsnip-1.0.0/PKG-INFO +159 -0
- tagsnip-1.0.0/README-pypi.md +145 -0
- tagsnip-1.0.0/README.md +187 -0
- tagsnip-1.0.0/pyproject.toml +25 -0
- tagsnip-1.0.0/setup.cfg +8 -0
- tagsnip-1.0.0/tagsnip/__init__.py +0 -0
- tagsnip-1.0.0/tagsnip/__main__.py +8 -0
- tagsnip-1.0.0/tagsnip/cli.py +113 -0
- tagsnip-1.0.0/tagsnip/extractor.py +51 -0
- tagsnip-1.0.0/tagsnip/fetcher.py +22 -0
- tagsnip-1.0.0/tagsnip/validate.py +5 -0
- tagsnip-1.0.0/tagsnip.egg-info/PKG-INFO +159 -0
- tagsnip-1.0.0/tagsnip.egg-info/SOURCES.txt +19 -0
- tagsnip-1.0.0/tagsnip.egg-info/dependency_links.txt +1 -0
- tagsnip-1.0.0/tagsnip.egg-info/entry_points.txt +2 -0
- tagsnip-1.0.0/tagsnip.egg-info/requires.txt +1 -0
- tagsnip-1.0.0/tagsnip.egg-info/top_level.txt +1 -0
- tagsnip-1.0.0/tests/test_extractor.py +91 -0
- tagsnip-1.0.0/tests/test_fetcher.py +4 -0
tagsnip-1.0.0/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rostislav Brož
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tagsnip-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tagsnip
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python package for extracting tagged code snippets from local files or remote sources.
|
|
5
|
+
Author: Rostislav Brož
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/brozrost/tagsnip
|
|
8
|
+
Project-URL: Repository, https://github.com/brozrost/tagsnip
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE.md
|
|
12
|
+
Requires-Dist: requests
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
`tagsnip` is a Python CLI tool for extracting tagged code snippets from local files or remote URLs, such as GitHub raw files. It serves as the backend for the tagsnip LaTeX package and enables dynamic inclusion of code snippets directly in LaTeX documents.
|
|
16
|
+
|
|
17
|
+
<div align="center">
|
|
18
|
+
<a href="https://github.com/brozrost/tagsnip/actions">
|
|
19
|
+
<img src="https://github.com/brozrost/tagsnip/actions/workflows/python-package.yml/badge.svg">
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://github.com/brozrost/tagsnip/graphs/contributors">
|
|
22
|
+
<img src="https://img.shields.io/github/contributors/brozrost/tagsnip">
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://github.com/brozrost/tagsnip/issues">
|
|
25
|
+
<img src="https://img.shields.io/github/issues/brozrost/tagsnip">
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://github.com/brozrost/tagsnip/pulls">
|
|
28
|
+
<img src="https://img.shields.io/github/issues-pr/brozrost/tagsnip">
|
|
29
|
+
</a>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Install `tagsnip` from PyPI:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
pip install tagsnip
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
After installation, the command `tagsnip` should be available in your shell:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
tagsnip --help
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
tagsnip -s <source> -t <tag> [-o <output>]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
tagsnip -c <output>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Command-line options
|
|
57
|
+
```text
|
|
58
|
+
-s, --source Local file path or remote URL.
|
|
59
|
+
-t, --tag Snippet tag to extract.
|
|
60
|
+
-o, --output Optional output file path.
|
|
61
|
+
-c, --cleanup Removes temporary files at specified path.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`--source` and `--tag` are mandatory arguments. If `--output` is provided, `tagsnip` writes the extracted snippet to the selected file. Otherwise, the snippet is written to `stdout`.
|
|
65
|
+
|
|
66
|
+
When `--output` is used, `tagsnip` also writes a metadata file next to the output file. For example, if the output file is `snippet.tmp`, then the metadata file is `snippet.tmp.meta`. Both files can be removed using the `--cleanup` option.
|
|
67
|
+
|
|
68
|
+
The metadata file contains the original start and end line numbers of the extracted snippet. This is used by the LaTeX package to preserve source line numbering.
|
|
69
|
+
|
|
70
|
+
### Supported sources
|
|
71
|
+
|
|
72
|
+
- Local text files
|
|
73
|
+
- Remote text files available over HTTP or HTTPS
|
|
74
|
+
- GitHub raw file URLs
|
|
75
|
+
|
|
76
|
+
### Tag format
|
|
77
|
+
|
|
78
|
+
`tagsnip` extracts code between two matching markers:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
tagsnip-start <tag>
|
|
82
|
+
...
|
|
83
|
+
tagsnip-end <tag>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Tags are case-sensitive. Markers must appear on separate lines. The tag must follow the marker name after one space.
|
|
87
|
+
|
|
88
|
+
For example:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# tagsnip-start demo
|
|
92
|
+
print("This line will be extracted.")
|
|
93
|
+
# tagsnip-end demo
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The marker lines themselves are not included in the extracted snippet.
|
|
97
|
+
|
|
98
|
+
### Error handling
|
|
99
|
+
|
|
100
|
+
`tagsnip` reports errors when:
|
|
101
|
+
|
|
102
|
+
- The tag is not found
|
|
103
|
+
- Start/end markers are mismatched
|
|
104
|
+
- Multiple start markers exist
|
|
105
|
+
- The source cannot be fetched
|
|
106
|
+
|
|
107
|
+
## Quick example
|
|
108
|
+
|
|
109
|
+
### Example source file
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# tagsnip-start demo
|
|
113
|
+
def main():
|
|
114
|
+
x = 1
|
|
115
|
+
y = 2
|
|
116
|
+
print(x + y)
|
|
117
|
+
|
|
118
|
+
return 0
|
|
119
|
+
# tagsnip-end demo
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
import sys
|
|
123
|
+
sys.exit(main())
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### CLI usage
|
|
127
|
+
|
|
128
|
+
Write the snippet to `stdout`:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
tagsnip -s example.py -t demo
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Write the snippet to a file:
|
|
135
|
+
```bash
|
|
136
|
+
tagsnip -s example.py -t demo -o out/out.txt
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Get snippet from URL:
|
|
140
|
+
```bash
|
|
141
|
+
tagsnip -s https://raw.githubusercontent.com/brozrost/tagsnip/main/docs/example.py -t demo
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Remove temporary files:
|
|
145
|
+
```bash
|
|
146
|
+
tagsnip -c out/out.txt
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Python usage
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from tagsnip import extractor
|
|
153
|
+
|
|
154
|
+
snippet = extractor.extract_from_file("example.py", "demo")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Compatibility note
|
|
158
|
+
|
|
159
|
+
The Python package and the LaTeX package should be kept in sync. Backwards compatibility is not guaranteed.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
`tagsnip` is a Python CLI tool for extracting tagged code snippets from local files or remote URLs, such as GitHub raw files. It serves as the backend for the tagsnip LaTeX package and enables dynamic inclusion of code snippets directly in LaTeX documents.
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<a href="https://github.com/brozrost/tagsnip/actions">
|
|
5
|
+
<img src="https://github.com/brozrost/tagsnip/actions/workflows/python-package.yml/badge.svg">
|
|
6
|
+
</a>
|
|
7
|
+
<a href="https://github.com/brozrost/tagsnip/graphs/contributors">
|
|
8
|
+
<img src="https://img.shields.io/github/contributors/brozrost/tagsnip">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://github.com/brozrost/tagsnip/issues">
|
|
11
|
+
<img src="https://img.shields.io/github/issues/brozrost/tagsnip">
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://github.com/brozrost/tagsnip/pulls">
|
|
14
|
+
<img src="https://img.shields.io/github/issues-pr/brozrost/tagsnip">
|
|
15
|
+
</a>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Install `tagsnip` from PyPI:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pip install tagsnip
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After installation, the command `tagsnip` should be available in your shell:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
tagsnip --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
tagsnip -s <source> -t <tag> [-o <output>]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
tagsnip -c <output>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Command-line options
|
|
43
|
+
```text
|
|
44
|
+
-s, --source Local file path or remote URL.
|
|
45
|
+
-t, --tag Snippet tag to extract.
|
|
46
|
+
-o, --output Optional output file path.
|
|
47
|
+
-c, --cleanup Removes temporary files at specified path.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`--source` and `--tag` are mandatory arguments. If `--output` is provided, `tagsnip` writes the extracted snippet to the selected file. Otherwise, the snippet is written to `stdout`.
|
|
51
|
+
|
|
52
|
+
When `--output` is used, `tagsnip` also writes a metadata file next to the output file. For example, if the output file is `snippet.tmp`, then the metadata file is `snippet.tmp.meta`. Both files can be removed using the `--cleanup` option.
|
|
53
|
+
|
|
54
|
+
The metadata file contains the original start and end line numbers of the extracted snippet. This is used by the LaTeX package to preserve source line numbering.
|
|
55
|
+
|
|
56
|
+
### Supported sources
|
|
57
|
+
|
|
58
|
+
- Local text files
|
|
59
|
+
- Remote text files available over HTTP or HTTPS
|
|
60
|
+
- GitHub raw file URLs
|
|
61
|
+
|
|
62
|
+
### Tag format
|
|
63
|
+
|
|
64
|
+
`tagsnip` extracts code between two matching markers:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
tagsnip-start <tag>
|
|
68
|
+
...
|
|
69
|
+
tagsnip-end <tag>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Tags are case-sensitive. Markers must appear on separate lines. The tag must follow the marker name after one space.
|
|
73
|
+
|
|
74
|
+
For example:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# tagsnip-start demo
|
|
78
|
+
print("This line will be extracted.")
|
|
79
|
+
# tagsnip-end demo
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The marker lines themselves are not included in the extracted snippet.
|
|
83
|
+
|
|
84
|
+
### Error handling
|
|
85
|
+
|
|
86
|
+
`tagsnip` reports errors when:
|
|
87
|
+
|
|
88
|
+
- The tag is not found
|
|
89
|
+
- Start/end markers are mismatched
|
|
90
|
+
- Multiple start markers exist
|
|
91
|
+
- The source cannot be fetched
|
|
92
|
+
|
|
93
|
+
## Quick example
|
|
94
|
+
|
|
95
|
+
### Example source file
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
# tagsnip-start demo
|
|
99
|
+
def main():
|
|
100
|
+
x = 1
|
|
101
|
+
y = 2
|
|
102
|
+
print(x + y)
|
|
103
|
+
|
|
104
|
+
return 0
|
|
105
|
+
# tagsnip-end demo
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
import sys
|
|
109
|
+
sys.exit(main())
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### CLI usage
|
|
113
|
+
|
|
114
|
+
Write the snippet to `stdout`:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
tagsnip -s example.py -t demo
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Write the snippet to a file:
|
|
121
|
+
```bash
|
|
122
|
+
tagsnip -s example.py -t demo -o out/out.txt
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Get snippet from URL:
|
|
126
|
+
```bash
|
|
127
|
+
tagsnip -s https://raw.githubusercontent.com/brozrost/tagsnip/main/docs/example.py -t demo
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Remove temporary files:
|
|
131
|
+
```bash
|
|
132
|
+
tagsnip -c out/out.txt
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Python usage
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from tagsnip import extractor
|
|
139
|
+
|
|
140
|
+
snippet = extractor.extract_from_file("example.py", "demo")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Compatibility note
|
|
144
|
+
|
|
145
|
+
The Python package and the LaTeX package should be kept in sync. Backwards compatibility is not guaranteed.
|
tagsnip-1.0.0/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
### `tagsnip` is a LaTeX package for including tagged code snippets from local files or remote URLs. It extracts marked sections of code and typesets them in a consistent style, supporting reproducible and maintainable documentation.
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<a href="https://github.com/brozrost/tagsnip/actions">
|
|
5
|
+
<img src="https://github.com/brozrost/tagsnip/actions/workflows/python-package.yml/badge.svg">
|
|
6
|
+
</a>
|
|
7
|
+
<a href="https://github.com/brozrost/tagsnip/graphs/contributors">
|
|
8
|
+
<img src="https://img.shields.io/github/contributors/brozrost/tagsnip">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://github.com/brozrost/tagsnip/issues">
|
|
11
|
+
<img src="https://img.shields.io/github/issues/brozrost/tagsnip">
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://github.com/brozrost/tagsnip/pulls">
|
|
14
|
+
<img src="https://img.shields.io/github/issues-pr/brozrost/tagsnip">
|
|
15
|
+
</a>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
**Documentation:** <a href="https://github.com/brozrost/tagsnip/blob/main/docs/tagsnip-docs.pdf">tagsnip-docs.pdf</a>
|
|
19
|
+
**Czech documentation:** <a href="https://github.com/brozrost/tagsnip/blob/main/docs/tagsnip-docs-czech.pdf">tagsnip-docs-czech.pdf</a>
|
|
20
|
+
|
|
21
|
+
> The documentation files also double as example documents for `tagsnip`.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
`tagsnip` consists of two parts:
|
|
26
|
+
|
|
27
|
+
1. LaTeX package `tagsnip.sty`,
|
|
28
|
+
2. Python backend package `tagsnip`.
|
|
29
|
+
|
|
30
|
+
Both parts are required.
|
|
31
|
+
|
|
32
|
+
### Installing the LaTeX package from CTAN
|
|
33
|
+
|
|
34
|
+
Download the <a href="https://ctan.org/pkg/tagsnip">`tagsnip` package archive from CTAN</a> and extract it. For a local project installation, copy `tagsnip.sty` next to your main `.tex` file:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
project/
|
|
38
|
+
├── main.tex
|
|
39
|
+
└── tagsnip.sty
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This is the simplest installation method and is sufficient for compiling a single project. For a user-wide installation, place `tagsnip.sty` into your local TeX tree.
|
|
43
|
+
|
|
44
|
+
Also see: https://ctan.org/pkg/tagsnip
|
|
45
|
+
|
|
46
|
+
### Installing the Python backend
|
|
47
|
+
|
|
48
|
+
`tagsnip` uses a Python backend with the same name to parse source files. The backend is published on PyPI. Before using the LaTeX package, install the backend:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
pip install tagsnip
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Also see: https://pypi.org/project/tagsnip/
|
|
55
|
+
|
|
56
|
+
After installation, the backend must be available in the system `PATH` under the command name `tagsnip`. You can check this with:
|
|
57
|
+
|
|
58
|
+
~~~sh
|
|
59
|
+
tagsnip --help
|
|
60
|
+
~~~
|
|
61
|
+
|
|
62
|
+
## Compilation
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
lualatex --shell-escape docs/tagsnip-docs.tex
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
**Example document:** <a href="https://github.com/brozrost/tagsnip/blob/main/docs/docs.pdf">docs.pdf</a>
|
|
71
|
+
|
|
72
|
+
`tagsnip` defines the command `\IncludeCode`, which is used as follows:
|
|
73
|
+
|
|
74
|
+
```tex
|
|
75
|
+
\IncludeCode[options]{source}{tag}{language}{caption}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The optional argument `options` allows the user to override code formatting settings. These options are passed directly to `minted`, so they must be valid `minted` options and must be separated by commas. For example:
|
|
79
|
+
|
|
80
|
+
```tex
|
|
81
|
+
\IncludeCode[
|
|
82
|
+
firstnumber=1,
|
|
83
|
+
fontsize=\scriptsize,
|
|
84
|
+
style=monokai
|
|
85
|
+
]{docs/example.py}{tag1}{Python}{Example snippet.}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The option `firstnumber=1` starts line numbering at line 1, `fontsize=\scriptsize` changes the font size, and `style=monokai` changes the syntax highlighting style.
|
|
89
|
+
|
|
90
|
+
Without changing `firstnumber`, `tagsnip` preserves the original line numbers from the source file.
|
|
91
|
+
|
|
92
|
+
The mandatory argument `source` defines where the code should be loaded from. It can be either a local file path or a URL pointing to a remote text file. `tagsnip` distinguishes these cases automatically.
|
|
93
|
+
|
|
94
|
+
The mandatory argument `tag` specifies the unique keyword identifying the requested snippet. A snippet is delimited in the source file by `tagsnip-start` and `tagsnip-end` markers:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# tagsnip-start tag1
|
|
98
|
+
def main():
|
|
99
|
+
x = 1
|
|
100
|
+
y = 2
|
|
101
|
+
print(x + y)
|
|
102
|
+
|
|
103
|
+
return 0
|
|
104
|
+
# tagsnip-end tag1
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The tag must follow the marker after exactly one space.
|
|
108
|
+
|
|
109
|
+
The mandatory argument `language` selects the programming language used for syntax highlighting.
|
|
110
|
+
|
|
111
|
+
The final argument `caption` defines the snippet caption. It may be empty, but the braces must still be written.
|
|
112
|
+
|
|
113
|
+
## Local snippet
|
|
114
|
+
|
|
115
|
+
Suppose the local file `docs/example.py` contains a function `main()` marked with the tag `tag1`, as seen above. The function can be included in the document with:
|
|
116
|
+
|
|
117
|
+
```tex
|
|
118
|
+
\IncludeCode{example.py}{tag1}{Python}{Úryvek 2: ...}
|
|
119
|
+
```
|
|
120
|
+
<div align="center">
|
|
121
|
+
<img width="835" height="265" alt="# tagsnip-start tag" src="https://github.com/user-attachments/assets/4ca1bd1a-abe8-4478-963d-15c42fbe9479" />
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
## Remote snippet
|
|
126
|
+
|
|
127
|
+
`tagsnip` can also include snippets from source files available through web URLs.
|
|
128
|
+
|
|
129
|
+
For example, a snippet marked with the tag `tag2` in a remote Python file can be included as follows:
|
|
130
|
+
|
|
131
|
+
```tex
|
|
132
|
+
\IncludeCode[firstnumber=1]{https://raw.githubusercontent.com/brozrost/tagsnip/main/docs/example2.py}{tag2}{Python}{Code snippet from a remote file.}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
<div align="center">
|
|
136
|
+
<img width="833" height="324" alt="Pasted Graphic 1 2" src="https://github.com/user-attachments/assets/02dfe4b5-0340-42b1-bb25-63cc0b331827" />
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
The remote file must be accessible over HTTP or HTTPS and must be readable as a plain text source file.
|
|
140
|
+
|
|
141
|
+
## Architecture
|
|
142
|
+
|
|
143
|
+
`tagsnip` consists of two main parts: a LaTeX frontend package and an external backend utility written in Python.
|
|
144
|
+
|
|
145
|
+
The frontend defines the command `\IncludeCode` and handles the final typesetting of extracted snippets through the `minted` package.
|
|
146
|
+
|
|
147
|
+
The backend is responsible for accessing source files, finding marked code regions, and extracting them into temporary files.
|
|
148
|
+
|
|
149
|
+
During document compilation, the frontend uses shell escape to call the backend utility. It passes the source path or URL and the requested tag to the backend. The backend writes the extracted code to a temporary file, which is then inserted into the document and typeset by `minted`.
|
|
150
|
+
|
|
151
|
+
## Backend
|
|
152
|
+
|
|
153
|
+
The Python package `tagsnip` provides a command-line interface that accepts a source file, a tag name, and an output file path.
|
|
154
|
+
|
|
155
|
+
The backend loads the source file, searches for the corresponding pair of `tagsnip-start` and `tagsnip-end` markers, and extracts the text between them.
|
|
156
|
+
|
|
157
|
+
The package also checks for error states, such as:
|
|
158
|
+
|
|
159
|
+
- a non-existent source file,
|
|
160
|
+
- an inaccessible remote file,
|
|
161
|
+
- a missing tag,
|
|
162
|
+
- a missing start or end marker,
|
|
163
|
+
- multiple occurrences of the same tag in one source file.
|
|
164
|
+
|
|
165
|
+
If an error is detected, the backend raises an exception. This interrupts the LaTeX compilation and prints an appropriate error message.
|
|
166
|
+
|
|
167
|
+
For local files, the backend uses Python's `pathlib` module. For tag matching, it uses `re`. For loading remote files over HTTP, it uses the `requests` library.
|
|
168
|
+
|
|
169
|
+
## Limitations
|
|
170
|
+
|
|
171
|
+
- `tagsnip` requires LuaLaTeX.
|
|
172
|
+
- The document must be compiled with shell escape enabled, for example with `--shell-escape`.
|
|
173
|
+
- The Python backend must be installed and available in the system `PATH` under the command `tagsnip`.
|
|
174
|
+
- `tagsnip` depends on `minted`.
|
|
175
|
+
- Remote source files must be accessible over HTTP or HTTPS.
|
|
176
|
+
- Remote source files must be plain text files.
|
|
177
|
+
|
|
178
|
+
## Security note
|
|
179
|
+
|
|
180
|
+
`tagsnip` requires shell escape because it calls an external Python backend during compilation and removes temporary files (lines 80, 93, and 94 in `tagsnip.sty`).
|
|
181
|
+
|
|
182
|
+
Only compile trusted documents with shell escape enabled.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
Copyright (c) 2026 Rostislav Brož.
|
|
186
|
+
|
|
187
|
+
> `tagsnip` is distributed under the MIT License. The full license text is included in the repository in the [`LICENSE`](https://github.com/brozrost/tagsnip/blob/main/LICENSE.md) file.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tagsnip"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python package for extracting tagged code snippets from local files or remote sources."
|
|
9
|
+
readme = "README-pypi.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = ["requests"]
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Rostislav Brož" }
|
|
14
|
+
]
|
|
15
|
+
license = "MIT"
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://github.com/brozrost/tagsnip"
|
|
19
|
+
Repository = "https://github.com/brozrost/tagsnip"
|
|
20
|
+
|
|
21
|
+
[tool.setuptools]
|
|
22
|
+
packages = ["tagsnip"]
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
tagsnip = "tagsnip.cli:main"
|
tagsnip-1.0.0/setup.cfg
ADDED
|
File without changes
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import argparse
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from tagsnip import extractor
|
|
6
|
+
from tagsnip import fetcher
|
|
7
|
+
from tagsnip import validate
|
|
8
|
+
|
|
9
|
+
def cleanup_generated_files(output_path: str) -> None:
|
|
10
|
+
snippet_path = Path(output_path)
|
|
11
|
+
meta_path = Path(str(snippet_path) + ".meta")
|
|
12
|
+
|
|
13
|
+
for path in (snippet_path, meta_path):
|
|
14
|
+
try:
|
|
15
|
+
path.unlink()
|
|
16
|
+
except FileNotFoundError:
|
|
17
|
+
pass
|
|
18
|
+
except OSError as exc:
|
|
19
|
+
raise RuntimeError(f"Error: Could not remove temporary file: {path}") from exc
|
|
20
|
+
|
|
21
|
+
def main() -> int:
|
|
22
|
+
parser = argparse.ArgumentParser(
|
|
23
|
+
description="tagsnip is a Python package for extracting tagged code snippets " \
|
|
24
|
+
"from local files or remote sources.",
|
|
25
|
+
usage="""
|
|
26
|
+
tagsnip --help
|
|
27
|
+
|
|
28
|
+
tagsnip --source <file path> --tag <tag>
|
|
29
|
+
tagsnip -s example.py -t demo
|
|
30
|
+
|
|
31
|
+
tagsnip --source <file path> --tag <tag> --out <out file path>
|
|
32
|
+
tagsnip -s example.py -t demo -o out/out.txt
|
|
33
|
+
|
|
34
|
+
tagsnip --source <url> --tag <tag>
|
|
35
|
+
tagsnip -s https://raw.githubusercontent.com/brozrost/tagsnip/main/docs/example.py -t demo
|
|
36
|
+
|
|
37
|
+
tagsnip --cleanup <generated file path>
|
|
38
|
+
tagsnip --cleanup out/out.txt
|
|
39
|
+
"""
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"-s", "--source",
|
|
44
|
+
help="Path to a local file or URL"
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"-t", "--tag",
|
|
48
|
+
help="Snippet tag name."
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"-o", "--out",
|
|
52
|
+
help="Path to the output file. If omitted, prints to stdout."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-c", "--cleanup",
|
|
57
|
+
metavar="FILE",
|
|
58
|
+
help="Remove a generated snippet file and its .meta file."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
args = parser.parse_args()
|
|
62
|
+
|
|
63
|
+
if args.cleanup:
|
|
64
|
+
try:
|
|
65
|
+
cleanup_generated_files(args.cleanup)
|
|
66
|
+
except RuntimeError as exc:
|
|
67
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
68
|
+
return 1
|
|
69
|
+
|
|
70
|
+
return 0
|
|
71
|
+
|
|
72
|
+
if not args.source or not args.tag:
|
|
73
|
+
parser.error("Error: Arguments -s/--source and -t/--tag are required unless --cleanup is used.")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
if validate.is_url(args.source):
|
|
78
|
+
fetch = fetcher.FetcherClient()
|
|
79
|
+
text = fetch.fetch_text(args.source)
|
|
80
|
+
else:
|
|
81
|
+
text = Path(args.source).read_text(encoding="utf-8")
|
|
82
|
+
|
|
83
|
+
snippet, first_line_num, last_line_num = extractor.extract_tagged_block(text, args.tag)
|
|
84
|
+
|
|
85
|
+
except fetcher.FetcherError as exc:
|
|
86
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
87
|
+
return 1
|
|
88
|
+
except OSError:
|
|
89
|
+
print(f"Error: Could not read file: {args.source}", file=sys.stderr)
|
|
90
|
+
return 1
|
|
91
|
+
except extractor.TagsnipError as exc:
|
|
92
|
+
print(f"Error: {exc}", file=sys.stderr)
|
|
93
|
+
return 1
|
|
94
|
+
|
|
95
|
+
if args.out:
|
|
96
|
+
output_path = Path(args.out)
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
output_path.write_text(snippet, encoding="utf-8")
|
|
101
|
+
|
|
102
|
+
meta_path = Path(str(output_path) + ".meta")
|
|
103
|
+
meta_path.write_text(f"{first_line_num}\n{last_line_num}", encoding="utf-8")
|
|
104
|
+
except OSError as exc:
|
|
105
|
+
print(f"Error: Could not write file: {output_path}")
|
|
106
|
+
return 1
|
|
107
|
+
else:
|
|
108
|
+
print(snippet)
|
|
109
|
+
|
|
110
|
+
return 0
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
sys.exit(main())
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
class TagsnipError(RuntimeError):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
def marker_matches(line: str, marker: str) -> bool:
|
|
8
|
+
pattern = rf"{re.escape(marker)}(?!\S)"
|
|
9
|
+
return re.search(pattern, line) is not None
|
|
10
|
+
|
|
11
|
+
def extract_tagged_block(text: str, tag: str):
|
|
12
|
+
start_marker = f"tagsnip-start {tag}"
|
|
13
|
+
end_marker = f"tagsnip-end {tag}"
|
|
14
|
+
|
|
15
|
+
lines = text.splitlines()
|
|
16
|
+
|
|
17
|
+
start_index = None
|
|
18
|
+
end_index = None
|
|
19
|
+
|
|
20
|
+
for i, line in enumerate(lines):
|
|
21
|
+
if marker_matches(line, start_marker):
|
|
22
|
+
if start_index is not None:
|
|
23
|
+
raise TagsnipError(f"Multiple start tags found for '{tag}'")
|
|
24
|
+
|
|
25
|
+
start_index = i
|
|
26
|
+
|
|
27
|
+
if start_index is None:
|
|
28
|
+
raise TagsnipError(f"Start tag not found for '{tag}'")
|
|
29
|
+
|
|
30
|
+
for i in range(start_index + 1, len(lines)):
|
|
31
|
+
if marker_matches(lines[i], end_marker):
|
|
32
|
+
end_index = i
|
|
33
|
+
break
|
|
34
|
+
|
|
35
|
+
if end_index is None:
|
|
36
|
+
raise TagsnipError(f"End tag not found for '{tag}'")
|
|
37
|
+
|
|
38
|
+
return "\n".join(lines[start_index + 1:end_index]), start_index + 2, end_index
|
|
39
|
+
|
|
40
|
+
def extract_from_file(path: str | Path, tag: str) -> str:
|
|
41
|
+
file_path = Path(path)
|
|
42
|
+
|
|
43
|
+
if not file_path.is_file():
|
|
44
|
+
raise TagsnipError(f"File not found: {file_path}")
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
text = file_path.read_text(encoding="utf-8")
|
|
48
|
+
except OSError as exc:
|
|
49
|
+
raise TagsnipError(f"Could not read file: {file_path}") from exc
|
|
50
|
+
|
|
51
|
+
return extract_tagged_block(text, tag)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
class FetcherError(RuntimeError):
|
|
4
|
+
pass
|
|
5
|
+
|
|
6
|
+
class FetcherClient:
|
|
7
|
+
def __init__(self, timeout: float = 30.0):
|
|
8
|
+
self.timeout = timeout
|
|
9
|
+
|
|
10
|
+
def fetch_text(self, url: str) -> str:
|
|
11
|
+
try:
|
|
12
|
+
response = requests.get(url, timeout=self.timeout)
|
|
13
|
+
response.raise_for_status()
|
|
14
|
+
except requests.HTTPError as exc:
|
|
15
|
+
raise FetcherError(f"HTTP error while fetching '{url}': {exc}") from exc
|
|
16
|
+
except requests.RequestException as exc:
|
|
17
|
+
raise FetcherError(f"Network error while fetching '{url}': {exc}") from exc
|
|
18
|
+
|
|
19
|
+
if not response.text.strip():
|
|
20
|
+
raise FetcherError(f"Empty response from {url}")
|
|
21
|
+
|
|
22
|
+
return response.text
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tagsnip
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python package for extracting tagged code snippets from local files or remote sources.
|
|
5
|
+
Author: Rostislav Brož
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/brozrost/tagsnip
|
|
8
|
+
Project-URL: Repository, https://github.com/brozrost/tagsnip
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE.md
|
|
12
|
+
Requires-Dist: requests
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
`tagsnip` is a Python CLI tool for extracting tagged code snippets from local files or remote URLs, such as GitHub raw files. It serves as the backend for the tagsnip LaTeX package and enables dynamic inclusion of code snippets directly in LaTeX documents.
|
|
16
|
+
|
|
17
|
+
<div align="center">
|
|
18
|
+
<a href="https://github.com/brozrost/tagsnip/actions">
|
|
19
|
+
<img src="https://github.com/brozrost/tagsnip/actions/workflows/python-package.yml/badge.svg">
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://github.com/brozrost/tagsnip/graphs/contributors">
|
|
22
|
+
<img src="https://img.shields.io/github/contributors/brozrost/tagsnip">
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://github.com/brozrost/tagsnip/issues">
|
|
25
|
+
<img src="https://img.shields.io/github/issues/brozrost/tagsnip">
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://github.com/brozrost/tagsnip/pulls">
|
|
28
|
+
<img src="https://img.shields.io/github/issues-pr/brozrost/tagsnip">
|
|
29
|
+
</a>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Install `tagsnip` from PyPI:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
pip install tagsnip
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
After installation, the command `tagsnip` should be available in your shell:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
tagsnip --help
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
tagsnip -s <source> -t <tag> [-o <output>]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
tagsnip -c <output>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Command-line options
|
|
57
|
+
```text
|
|
58
|
+
-s, --source Local file path or remote URL.
|
|
59
|
+
-t, --tag Snippet tag to extract.
|
|
60
|
+
-o, --output Optional output file path.
|
|
61
|
+
-c, --cleanup Removes temporary files at specified path.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`--source` and `--tag` are mandatory arguments. If `--output` is provided, `tagsnip` writes the extracted snippet to the selected file. Otherwise, the snippet is written to `stdout`.
|
|
65
|
+
|
|
66
|
+
When `--output` is used, `tagsnip` also writes a metadata file next to the output file. For example, if the output file is `snippet.tmp`, then the metadata file is `snippet.tmp.meta`. Both files can be removed using the `--cleanup` option.
|
|
67
|
+
|
|
68
|
+
The metadata file contains the original start and end line numbers of the extracted snippet. This is used by the LaTeX package to preserve source line numbering.
|
|
69
|
+
|
|
70
|
+
### Supported sources
|
|
71
|
+
|
|
72
|
+
- Local text files
|
|
73
|
+
- Remote text files available over HTTP or HTTPS
|
|
74
|
+
- GitHub raw file URLs
|
|
75
|
+
|
|
76
|
+
### Tag format
|
|
77
|
+
|
|
78
|
+
`tagsnip` extracts code between two matching markers:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
tagsnip-start <tag>
|
|
82
|
+
...
|
|
83
|
+
tagsnip-end <tag>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Tags are case-sensitive. Markers must appear on separate lines. The tag must follow the marker name after one space.
|
|
87
|
+
|
|
88
|
+
For example:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# tagsnip-start demo
|
|
92
|
+
print("This line will be extracted.")
|
|
93
|
+
# tagsnip-end demo
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The marker lines themselves are not included in the extracted snippet.
|
|
97
|
+
|
|
98
|
+
### Error handling
|
|
99
|
+
|
|
100
|
+
`tagsnip` reports errors when:
|
|
101
|
+
|
|
102
|
+
- The tag is not found
|
|
103
|
+
- Start/end markers are mismatched
|
|
104
|
+
- Multiple start markers exist
|
|
105
|
+
- The source cannot be fetched
|
|
106
|
+
|
|
107
|
+
## Quick example
|
|
108
|
+
|
|
109
|
+
### Example source file
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# tagsnip-start demo
|
|
113
|
+
def main():
|
|
114
|
+
x = 1
|
|
115
|
+
y = 2
|
|
116
|
+
print(x + y)
|
|
117
|
+
|
|
118
|
+
return 0
|
|
119
|
+
# tagsnip-end demo
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
import sys
|
|
123
|
+
sys.exit(main())
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### CLI usage
|
|
127
|
+
|
|
128
|
+
Write the snippet to `stdout`:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
tagsnip -s example.py -t demo
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Write the snippet to a file:
|
|
135
|
+
```bash
|
|
136
|
+
tagsnip -s example.py -t demo -o out/out.txt
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Get snippet from URL:
|
|
140
|
+
```bash
|
|
141
|
+
tagsnip -s https://raw.githubusercontent.com/brozrost/tagsnip/main/docs/example.py -t demo
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Remove temporary files:
|
|
145
|
+
```bash
|
|
146
|
+
tagsnip -c out/out.txt
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Python usage
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from tagsnip import extractor
|
|
153
|
+
|
|
154
|
+
snippet = extractor.extract_from_file("example.py", "demo")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Compatibility note
|
|
158
|
+
|
|
159
|
+
The Python package and the LaTeX package should be kept in sync. Backwards compatibility is not guaranteed.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README-pypi.md
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
setup.cfg
|
|
6
|
+
tagsnip/__init__.py
|
|
7
|
+
tagsnip/__main__.py
|
|
8
|
+
tagsnip/cli.py
|
|
9
|
+
tagsnip/extractor.py
|
|
10
|
+
tagsnip/fetcher.py
|
|
11
|
+
tagsnip/validate.py
|
|
12
|
+
tagsnip.egg-info/PKG-INFO
|
|
13
|
+
tagsnip.egg-info/SOURCES.txt
|
|
14
|
+
tagsnip.egg-info/dependency_links.txt
|
|
15
|
+
tagsnip.egg-info/entry_points.txt
|
|
16
|
+
tagsnip.egg-info/requires.txt
|
|
17
|
+
tagsnip.egg-info/top_level.txt
|
|
18
|
+
tests/test_extractor.py
|
|
19
|
+
tests/test_fetcher.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tagsnip
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from tagsnip import extractor
|
|
2
|
+
|
|
3
|
+
PATH = "./docs/example.py"
|
|
4
|
+
|
|
5
|
+
TEXT = """
|
|
6
|
+
# tagsnip-start demo
|
|
7
|
+
def main():
|
|
8
|
+
x = 1
|
|
9
|
+
y = 2
|
|
10
|
+
print(x + y)
|
|
11
|
+
|
|
12
|
+
return 0
|
|
13
|
+
# tagsnip-end demo
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
import sys
|
|
17
|
+
sys.exit(main())"""
|
|
18
|
+
|
|
19
|
+
OUT = """def main():
|
|
20
|
+
x = 1
|
|
21
|
+
y = 2
|
|
22
|
+
print(x + y)
|
|
23
|
+
|
|
24
|
+
return 0"""
|
|
25
|
+
|
|
26
|
+
def test_extract_tagged_block():
|
|
27
|
+
snippet, first_line_num, last_line_num = extractor.extract_tagged_block(TEXT, "demo")
|
|
28
|
+
assert snippet == OUT
|
|
29
|
+
|
|
30
|
+
def test_extract_from_file():
|
|
31
|
+
snippet, first_line_num, last_line_num = extractor.extract_from_file(PATH, "tag1")
|
|
32
|
+
assert snippet == OUT
|
|
33
|
+
|
|
34
|
+
def test_missing_start_tag():
|
|
35
|
+
text = """
|
|
36
|
+
hello
|
|
37
|
+
# tagsnip-end 1
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
extractor.extract_tagged_block(text, "1")
|
|
42
|
+
assert False, "Expected TagsnipError"
|
|
43
|
+
except extractor.TagsnipError as err:
|
|
44
|
+
assert str(err) == "Start tag not found for '1'"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_missing_end_tag():
|
|
48
|
+
text = """
|
|
49
|
+
# tagsnip-start 1
|
|
50
|
+
hello
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
extractor.extract_tagged_block(text, "1")
|
|
55
|
+
assert False, "Expected TagsnipError"
|
|
56
|
+
except extractor.TagsnipError as err:
|
|
57
|
+
assert str(err) == "End tag not found for '1'"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_multiple_start_tags():
|
|
61
|
+
text = """
|
|
62
|
+
# tagsnip-start 1
|
|
63
|
+
hello
|
|
64
|
+
# tagsnip-start 1
|
|
65
|
+
world
|
|
66
|
+
# tagsnip-end 1
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
extractor.extract_tagged_block(text, "1")
|
|
71
|
+
assert False, "Expected TagsnipError"
|
|
72
|
+
except extractor.TagsnipError as err:
|
|
73
|
+
assert str(err) == "Multiple start tags found for '1'"
|
|
74
|
+
|
|
75
|
+
def test_missing_file():
|
|
76
|
+
try:
|
|
77
|
+
extractor.extract_from_file("./does_not_exist.py", "1")
|
|
78
|
+
assert False, "Expected TagsnipError"
|
|
79
|
+
except extractor.TagsnipError:
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
def main():
|
|
83
|
+
test_extract_tagged_block()
|
|
84
|
+
test_extract_from_file()
|
|
85
|
+
test_missing_start_tag()
|
|
86
|
+
test_missing_end_tag()
|
|
87
|
+
test_multiple_start_tags()
|
|
88
|
+
test_missing_file()
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|