isabelle-block 0.1.2__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.
- isabelle_block-0.1.2/.gitignore +29 -0
- isabelle_block-0.1.2/LICENSE +21 -0
- isabelle_block-0.1.2/PKG-INFO +140 -0
- isabelle_block-0.1.2/README.md +115 -0
- isabelle_block-0.1.2/pyproject.toml +61 -0
- isabelle_block-0.1.2/src/isabelle_block/__init__.py +11 -0
- isabelle_block-0.1.2/src/isabelle_block/__main__.py +5 -0
- isabelle_block-0.1.2/src/isabelle_block/cli.py +1198 -0
- isabelle_block-0.1.2/tests/conftest.py +168 -0
- isabelle_block-0.1.2/tests/test_contract.py +558 -0
- isabelle_block-0.1.2/tests/test_corpus.py +82 -0
- isabelle_block-0.1.2/tests/test_verbs.py +223 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Virtual environments. `.venv/` is the throwaway install target the test
|
|
10
|
+
# instructions use -- the suite is run against a real `pip install .`, never
|
|
11
|
+
# against `PYTHONPATH=src`, so a venv is a working file rather than a artefact.
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
|
|
15
|
+
# Editor / OS / agent
|
|
16
|
+
.claude/*
|
|
17
|
+
!.claude/memory/
|
|
18
|
+
.DS_Store
|
|
19
|
+
|
|
20
|
+
# Pytest / lint caches
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.ruff_cache/
|
|
23
|
+
|
|
24
|
+
# End-of-session handoff scratchpad -- rewritten each session.
|
|
25
|
+
prompt.md
|
|
26
|
+
|
|
27
|
+
# Working files: scratch space and the commit-message file (CLAUDE.md).
|
|
28
|
+
scratch/
|
|
29
|
+
.gitcommit.txt
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 András Salamon
|
|
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.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: isabelle-block
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Mechanical block surgery on Isabelle theory sources: move, copy, delete, rewrite and substitute inside regions named by line span or by entry name.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ott2/isabelle-block
|
|
6
|
+
Project-URL: Source, https://github.com/ott2/isabelle-block
|
|
7
|
+
Author-email: András Salamon <Andras.Salamon@st-andrews.ac.uk>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: afp,formal-verification,isabelle,isar,refactoring,theorem-proving
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
18
|
+
Classifier: Topic :: Software Development
|
|
19
|
+
Requires-Python: >=3.9
|
|
20
|
+
Requires-Dist: isabelle-layout>=0.2.2
|
|
21
|
+
Requires-Dist: isabelle-query>=1.1.1
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# isabelle-block
|
|
27
|
+
|
|
28
|
+
**Mechanical edits to regions of Isabelle theory files, where a region is
|
|
29
|
+
named by a line span or by the name of a lemma.**
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
block move Foo.thy:helper_lemma before first_user # hoist a helper
|
|
33
|
+
block delete Foo.thy:dead_lemma
|
|
34
|
+
block move Foo.thy:1669..2657 before Bar.thy:46 # between theories
|
|
35
|
+
block reargs 'cost $1 $2' 'Suc (len $1)' Foo.thy --expect 14
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Name addressing is the reason to use it. `Foo.thy:helper_lemma` resolves,
|
|
39
|
+
through [`isabelle-query`](https://github.com/ott2/isabelle-query)'s parser,
|
|
40
|
+
to the lemma's span, from its doc block through the last line of its proof.
|
|
41
|
+
So an edit needs no line arithmetic, and it cannot go stale between looking
|
|
42
|
+
a lemma up and editing it.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
python3 -m pip install git+https://github.com/ott2/isabelle-block@v0.1.2
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This puts `block` on your `PATH`. Needs Python 3.9 or later; depends on
|
|
51
|
+
`isabelle-query` and `isabelle-layout`, which pip fetches from PyPI. It
|
|
52
|
+
needs no Isabelle process.
|
|
53
|
+
|
|
54
|
+
`isabelle-block` is not on PyPI yet. It will be published there once the
|
|
55
|
+
standalone tool has seen more use; until then, install a tagged release from
|
|
56
|
+
GitHub as above.
|
|
57
|
+
|
|
58
|
+
## Verbs
|
|
59
|
+
|
|
60
|
+
| verb | does |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `move SRC:REGION before DEST` | relocate a block, within a file or between files |
|
|
63
|
+
| `copy SRC:REGION before DEST` | place a copy and keep the source |
|
|
64
|
+
| `delete FILE:REGION` | excise a block |
|
|
65
|
+
| `emit FILE:REGION` | print a block verbatim |
|
|
66
|
+
| `replace FILE:REGION NEWFILE` | swap a block for a file's contents |
|
|
67
|
+
| `sub OLD NEW FILE[:REGION]...` | literal substring substitution |
|
|
68
|
+
| `resub PAT NEW FILE[:REGION]...` | regex substitution; a match may span lines |
|
|
69
|
+
| `reargs 'F $1 $2' TMPL FILE[:REGION]...` | rewrite `F`'s applications by their (bracket-aware) arguments; skips prose |
|
|
70
|
+
| `split SRC 'NAME\|IMPORTS\|RANGES'... [--cut]` | cut a theory into several |
|
|
71
|
+
| `merge BASE.thy SRC.thy...` | splice theory bodies into one |
|
|
72
|
+
|
|
73
|
+
A **REGION** is `A..B` (or `A-B`), a single line `N`, an entry `NAME`, or
|
|
74
|
+
`all`, which is also what a bare `FILE` means. A **DEST** is `LINE`, `NAME`,
|
|
75
|
+
`FILE:LINE` or `FILE:NAME`; the block lands immediately before it, preamble
|
|
76
|
+
included.
|
|
77
|
+
|
|
78
|
+
**Modifiers:**
|
|
79
|
+
|
|
80
|
+
- `--dry-run` reports the edit and writes nothing.
|
|
81
|
+
- `--expect N` makes `sub`, `resub` and `reargs` refuse unless exactly N
|
|
82
|
+
substitutions are made.
|
|
83
|
+
- `--except NAME` leaves an entry out of every region.
|
|
84
|
+
- `--no-preamble` starts a resolved span at the entry, not its doc block.
|
|
85
|
+
- `--with-preamble` keeps the doc block even when it sits directly under a
|
|
86
|
+
section heading. Such a `text` may introduce the section rather than
|
|
87
|
+
document the entry, and the source cannot say which. So there, `delete`,
|
|
88
|
+
`move` and `replace` refuse until you choose one of these two flags.
|
|
89
|
+
- `--keep-heading` / `--with-heading`: deleting the only entry under a
|
|
90
|
+
heading refuses until you say whether the heading stays (bare) or the
|
|
91
|
+
whole section goes with the entry.
|
|
92
|
+
|
|
93
|
+
A block moved `before NAME` that starts with its own heading, of the same or
|
|
94
|
+
higher rank as the heading above NAME, lands above that heading. So it
|
|
95
|
+
becomes a section beside NAME's, and does not swallow NAME.
|
|
96
|
+
- `--root DIR` names the session root whose keyword table name resolution
|
|
97
|
+
uses. By default it is discovered as `query` discovers it: an
|
|
98
|
+
environment variable, a `.isabelle-query` marker, or the nearest `ROOT`.
|
|
99
|
+
The table matters because a theory may use a command that another theory
|
|
100
|
+
in its session declares.
|
|
101
|
+
- `--no-trailing-blank` cuts verbatim, without blank-line healing.
|
|
102
|
+
|
|
103
|
+
`block --help` gives the full grammar, and `block VERB --help` gives one
|
|
104
|
+
verb's.
|
|
105
|
+
|
|
106
|
+
Every run ends with an explicit `PASS:` or `FAIL:` line, and the exit status
|
|
107
|
+
agrees. `emit` prints its `PASS:` on stderr, so `block emit … > FILE` is a
|
|
108
|
+
clean copy. A refusal writes nothing.
|
|
109
|
+
|
|
110
|
+
## Related tools
|
|
111
|
+
|
|
112
|
+
Editing by line span and plain substitution are what `ed`, `sed`, `sd` and
|
|
113
|
+
`fastmod` already do. [comby](https://comby.dev) does bracket-aware
|
|
114
|
+
rewriting much as `reargs` does. `block` is worth installing if your edits
|
|
115
|
+
need what those tools cannot see:
|
|
116
|
+
|
|
117
|
+
- lemma names as addresses;
|
|
118
|
+
- the difference between Isar code and prose (`text`, headings,
|
|
119
|
+
`\<comment>`);
|
|
120
|
+
- edits at theory level.
|
|
121
|
+
|
|
122
|
+
`docs/related-tools.md` compares them in detail.
|
|
123
|
+
|
|
124
|
+
## Status
|
|
125
|
+
|
|
126
|
+
This is alpha, extracted from the NDTHT formalisation's `bin/block.py`, where it
|
|
127
|
+
did the project's mechanical theory surgery. The planned verbs (`rename`, a
|
|
128
|
+
caller-checked `delete` and an import-checked `move`) are in `tasks.md`.
|
|
129
|
+
|
|
130
|
+
## Development
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
python3 -m venv .venv
|
|
134
|
+
.venv/bin/pip install -e '.[test]'
|
|
135
|
+
.venv/bin/pytest
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Authors & license
|
|
139
|
+
|
|
140
|
+
By András Salamon, with Claude Opus 4.7, 4.8, 5, and 5.5. [MIT](LICENSE).
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# isabelle-block
|
|
2
|
+
|
|
3
|
+
**Mechanical edits to regions of Isabelle theory files, where a region is
|
|
4
|
+
named by a line span or by the name of a lemma.**
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
block move Foo.thy:helper_lemma before first_user # hoist a helper
|
|
8
|
+
block delete Foo.thy:dead_lemma
|
|
9
|
+
block move Foo.thy:1669..2657 before Bar.thy:46 # between theories
|
|
10
|
+
block reargs 'cost $1 $2' 'Suc (len $1)' Foo.thy --expect 14
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Name addressing is the reason to use it. `Foo.thy:helper_lemma` resolves,
|
|
14
|
+
through [`isabelle-query`](https://github.com/ott2/isabelle-query)'s parser,
|
|
15
|
+
to the lemma's span, from its doc block through the last line of its proof.
|
|
16
|
+
So an edit needs no line arithmetic, and it cannot go stale between looking
|
|
17
|
+
a lemma up and editing it.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
python3 -m pip install git+https://github.com/ott2/isabelle-block@v0.1.2
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This puts `block` on your `PATH`. Needs Python 3.9 or later; depends on
|
|
26
|
+
`isabelle-query` and `isabelle-layout`, which pip fetches from PyPI. It
|
|
27
|
+
needs no Isabelle process.
|
|
28
|
+
|
|
29
|
+
`isabelle-block` is not on PyPI yet. It will be published there once the
|
|
30
|
+
standalone tool has seen more use; until then, install a tagged release from
|
|
31
|
+
GitHub as above.
|
|
32
|
+
|
|
33
|
+
## Verbs
|
|
34
|
+
|
|
35
|
+
| verb | does |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `move SRC:REGION before DEST` | relocate a block, within a file or between files |
|
|
38
|
+
| `copy SRC:REGION before DEST` | place a copy and keep the source |
|
|
39
|
+
| `delete FILE:REGION` | excise a block |
|
|
40
|
+
| `emit FILE:REGION` | print a block verbatim |
|
|
41
|
+
| `replace FILE:REGION NEWFILE` | swap a block for a file's contents |
|
|
42
|
+
| `sub OLD NEW FILE[:REGION]...` | literal substring substitution |
|
|
43
|
+
| `resub PAT NEW FILE[:REGION]...` | regex substitution; a match may span lines |
|
|
44
|
+
| `reargs 'F $1 $2' TMPL FILE[:REGION]...` | rewrite `F`'s applications by their (bracket-aware) arguments; skips prose |
|
|
45
|
+
| `split SRC 'NAME\|IMPORTS\|RANGES'... [--cut]` | cut a theory into several |
|
|
46
|
+
| `merge BASE.thy SRC.thy...` | splice theory bodies into one |
|
|
47
|
+
|
|
48
|
+
A **REGION** is `A..B` (or `A-B`), a single line `N`, an entry `NAME`, or
|
|
49
|
+
`all`, which is also what a bare `FILE` means. A **DEST** is `LINE`, `NAME`,
|
|
50
|
+
`FILE:LINE` or `FILE:NAME`; the block lands immediately before it, preamble
|
|
51
|
+
included.
|
|
52
|
+
|
|
53
|
+
**Modifiers:**
|
|
54
|
+
|
|
55
|
+
- `--dry-run` reports the edit and writes nothing.
|
|
56
|
+
- `--expect N` makes `sub`, `resub` and `reargs` refuse unless exactly N
|
|
57
|
+
substitutions are made.
|
|
58
|
+
- `--except NAME` leaves an entry out of every region.
|
|
59
|
+
- `--no-preamble` starts a resolved span at the entry, not its doc block.
|
|
60
|
+
- `--with-preamble` keeps the doc block even when it sits directly under a
|
|
61
|
+
section heading. Such a `text` may introduce the section rather than
|
|
62
|
+
document the entry, and the source cannot say which. So there, `delete`,
|
|
63
|
+
`move` and `replace` refuse until you choose one of these two flags.
|
|
64
|
+
- `--keep-heading` / `--with-heading`: deleting the only entry under a
|
|
65
|
+
heading refuses until you say whether the heading stays (bare) or the
|
|
66
|
+
whole section goes with the entry.
|
|
67
|
+
|
|
68
|
+
A block moved `before NAME` that starts with its own heading, of the same or
|
|
69
|
+
higher rank as the heading above NAME, lands above that heading. So it
|
|
70
|
+
becomes a section beside NAME's, and does not swallow NAME.
|
|
71
|
+
- `--root DIR` names the session root whose keyword table name resolution
|
|
72
|
+
uses. By default it is discovered as `query` discovers it: an
|
|
73
|
+
environment variable, a `.isabelle-query` marker, or the nearest `ROOT`.
|
|
74
|
+
The table matters because a theory may use a command that another theory
|
|
75
|
+
in its session declares.
|
|
76
|
+
- `--no-trailing-blank` cuts verbatim, without blank-line healing.
|
|
77
|
+
|
|
78
|
+
`block --help` gives the full grammar, and `block VERB --help` gives one
|
|
79
|
+
verb's.
|
|
80
|
+
|
|
81
|
+
Every run ends with an explicit `PASS:` or `FAIL:` line, and the exit status
|
|
82
|
+
agrees. `emit` prints its `PASS:` on stderr, so `block emit … > FILE` is a
|
|
83
|
+
clean copy. A refusal writes nothing.
|
|
84
|
+
|
|
85
|
+
## Related tools
|
|
86
|
+
|
|
87
|
+
Editing by line span and plain substitution are what `ed`, `sed`, `sd` and
|
|
88
|
+
`fastmod` already do. [comby](https://comby.dev) does bracket-aware
|
|
89
|
+
rewriting much as `reargs` does. `block` is worth installing if your edits
|
|
90
|
+
need what those tools cannot see:
|
|
91
|
+
|
|
92
|
+
- lemma names as addresses;
|
|
93
|
+
- the difference between Isar code and prose (`text`, headings,
|
|
94
|
+
`\<comment>`);
|
|
95
|
+
- edits at theory level.
|
|
96
|
+
|
|
97
|
+
`docs/related-tools.md` compares them in detail.
|
|
98
|
+
|
|
99
|
+
## Status
|
|
100
|
+
|
|
101
|
+
This is alpha, extracted from the NDTHT formalisation's `bin/block.py`, where it
|
|
102
|
+
did the project's mechanical theory surgery. The planned verbs (`rename`, a
|
|
103
|
+
caller-checked `delete` and an import-checked `move`) are in `tasks.md`.
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
python3 -m venv .venv
|
|
109
|
+
.venv/bin/pip install -e '.[test]'
|
|
110
|
+
.venv/bin/pytest
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Authors & license
|
|
114
|
+
|
|
115
|
+
By András Salamon, with Claude Opus 4.7, 4.8, 5, and 5.5. [MIT](LICENSE).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "isabelle-block"
|
|
7
|
+
version = "0.1.2"
|
|
8
|
+
description = "Mechanical block surgery on Isabelle theory sources: move, copy, delete, rewrite and substitute inside regions named by line span or by entry name."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "András Salamon", email = "Andras.Salamon@st-andrews.ac.uk" },
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"isabelle", "isar", "theorem-proving", "formal-verification",
|
|
17
|
+
"afp", "refactoring",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Science/Research",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
27
|
+
"Topic :: Software Development",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
# 1.1.0: an entry no longer ends at a heading inside its own proof
|
|
31
|
+
# [early-end]. 1.1.1: a declaration in mid-line is an entry (#29), so no
|
|
32
|
+
# cut_span holds a second lemma. Both keep a span written through to
|
|
33
|
+
# one entry.
|
|
34
|
+
"isabelle-query>=1.1.1",
|
|
35
|
+
# default_t_dir, the session-root discovery `query` itself uses.
|
|
36
|
+
"isabelle-layout>=0.2.2",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
test = ["pytest>=7"]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/ott2/isabelle-block"
|
|
44
|
+
Source = "https://github.com/ott2/isabelle-block"
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
block = "isabelle_block.cli:main"
|
|
48
|
+
|
|
49
|
+
[tool.pytest.ini_options]
|
|
50
|
+
testpaths = ["tests"]
|
|
51
|
+
# The corpus tests read real trees and take about a minute: `pytest -m corpus`.
|
|
52
|
+
addopts = "-ra --strict-markers -m 'not corpus'"
|
|
53
|
+
markers = ["corpus: read-only checks against NDTHT's t/ and the AFP"]
|
|
54
|
+
|
|
55
|
+
[tool.hatch.build.targets.wheel]
|
|
56
|
+
packages = ["src/isabelle_block"]
|
|
57
|
+
|
|
58
|
+
[tool.hatch.build.targets.sdist]
|
|
59
|
+
# Anchored with a leading `/`: an unanchored "README.md" matches at any depth,
|
|
60
|
+
# and shipped docs/prior-art/README.md and upstream/README.md.
|
|
61
|
+
include = ["/src", "/tests", "/README.md", "/LICENSE"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Mechanical block surgery on Isabelle theory sources.
|
|
2
|
+
|
|
3
|
+
The command-line tool is the product; `isabelle_block.cli` is its
|
|
4
|
+
implementation and has no public library surface yet.
|
|
5
|
+
"""
|
|
6
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
__version__ = version("isabelle-block")
|
|
10
|
+
except PackageNotFoundError: # running from a source tree, not installed
|
|
11
|
+
__version__ = "0+unknown"
|