arguebuf 2.7.2__tar.gz → 3.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.
- {arguebuf-2.7.2/src/arguebuf.egg-info → arguebuf-3.0.0}/PKG-INFO +25 -19
- {arguebuf-2.7.2 → arguebuf-3.0.0}/README.md +2 -1
- arguebuf-3.0.0/pyproject.toml +96 -0
- arguebuf-3.0.0/pyproject.toml.orig +77 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/__init__.py +18 -21
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/cli/app.py +4 -1
- arguebuf-3.0.0/src/arguebuf/cli/graph.py +240 -0
- arguebuf-3.0.0/src/arguebuf/cli/model.py +148 -0
- arguebuf-3.0.0/src/arguebuf/cli/server.py +74 -0
- arguebuf-3.0.0/src/arguebuf/cli/text.py +35 -0
- arguebuf-3.0.0/src/arguebuf/cli/translator.py +72 -0
- arguebuf-3.0.0/src/arguebuf/dt.py +29 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/dump/__init__.py +9 -11
- arguebuf-3.0.0/src/arguebuf/dump/_config.py +26 -0
- arguebuf-3.0.0/src/arguebuf/dump/_dump_aif.py +49 -0
- arguebuf-3.0.0/src/arguebuf/dump/_dump_d2.py +65 -0
- arguebuf-3.0.0/src/arguebuf/dump/_dump_dict.py +21 -0
- arguebuf-3.0.0/src/arguebuf/dump/_dump_graphviz.py +92 -0
- arguebuf-3.0.0/src/arguebuf/dump/_dump_io.py +13 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/dump/_dump_json.py +4 -4
- arguebuf-3.0.0/src/arguebuf/dump/_dump_networkx.py +76 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/dump/_dump_path.py +8 -4
- arguebuf-3.0.0/src/arguebuf/dump/_dump_protobuf.py +173 -0
- arguebuf-3.0.0/src/arguebuf/dump/_dump_xaif.py +25 -0
- arguebuf-3.0.0/src/arguebuf/load/_config.py +111 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_aif.py +173 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_aml.py +111 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_argdown.py +52 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_brat.py +52 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_casebase.py +177 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_dict.py +34 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_io.py +32 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_json.py +18 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/load/_load_kialo.py +30 -49
- arguebuf-3.0.0/src/arguebuf/load/_load_microtexts.py +120 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_ova.py +138 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_path.py +76 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_protobuf.py +182 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_sadface.py +82 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_text.py +26 -0
- arguebuf-3.0.0/src/arguebuf/load/_load_xaif.py +14 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/model/__init__.py +18 -22
- arguebuf-3.0.0/src/arguebuf/model/analyst.py +17 -0
- arguebuf-3.0.0/src/arguebuf/model/edge.py +20 -0
- arguebuf-3.0.0/src/arguebuf/model/graph.py +515 -0
- arguebuf-3.0.0/src/arguebuf/model/metadata.py +14 -0
- arguebuf-3.0.0/src/arguebuf/model/node.py +121 -0
- arguebuf-3.0.0/src/arguebuf/model/participant.py +23 -0
- arguebuf-3.0.0/src/arguebuf/model/reference.py +20 -0
- arguebuf-3.0.0/src/arguebuf/model/resource.py +26 -0
- arguebuf-3.0.0/src/arguebuf/model/scheme.py +196 -0
- arguebuf-3.0.0/src/arguebuf/model/userdata.py +5 -0
- arguebuf-3.0.0/src/arguebuf/model/utils.py +29 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/render/__init__.py +1 -1
- arguebuf-3.0.0/src/arguebuf/render/_render_d2.py +20 -0
- arguebuf-3.0.0/src/arguebuf/render/_render_graphviz.py +19 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/schemas/__init__.py +3 -1
- arguebuf-3.0.0/src/arguebuf/schemas/aif.py +48 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/schemas/argdown.py +5 -6
- arguebuf-3.0.0/src/arguebuf/schemas/d2.py +64 -0
- arguebuf-3.0.0/src/arguebuf/schemas/graphviz.py +9 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/schemas/microtexts.py +2 -2
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/schemas/ova.py +2 -2
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/schemas/sadface.py +1 -2
- arguebuf-3.0.0/src/arguebuf/schemas/xaif.py +55 -0
- arguebuf-3.0.0/src/arguebuf/traverse.py +121 -0
- arguebuf-3.0.0/src/arguebuf/utils.py +11 -0
- arguebuf-2.7.2/LICENSE +0 -21
- arguebuf-2.7.2/PKG-INFO +0 -178
- arguebuf-2.7.2/pyproject.toml +0 -55
- arguebuf-2.7.2/setup.cfg +0 -4
- arguebuf-2.7.2/src/arguebuf/cli/graph.py +0 -219
- arguebuf-2.7.2/src/arguebuf/cli/model.py +0 -70
- arguebuf-2.7.2/src/arguebuf/cli/server.py +0 -60
- arguebuf-2.7.2/src/arguebuf/cli/text.py +0 -54
- arguebuf-2.7.2/src/arguebuf/cli/translator.py +0 -73
- arguebuf-2.7.2/src/arguebuf/dt.py +0 -19
- arguebuf-2.7.2/src/arguebuf/dump/_config.py +0 -19
- arguebuf-2.7.2/src/arguebuf/dump/_dump_aif.py +0 -56
- arguebuf-2.7.2/src/arguebuf/dump/_dump_d2.py +0 -106
- arguebuf-2.7.2/src/arguebuf/dump/_dump_dict.py +0 -23
- arguebuf-2.7.2/src/arguebuf/dump/_dump_graphviz.py +0 -176
- arguebuf-2.7.2/src/arguebuf/dump/_dump_io.py +0 -13
- arguebuf-2.7.2/src/arguebuf/dump/_dump_networkx.py +0 -96
- arguebuf-2.7.2/src/arguebuf/dump/_dump_protobuf.py +0 -187
- arguebuf-2.7.2/src/arguebuf/dump/_dump_xaif.py +0 -64
- arguebuf-2.7.2/src/arguebuf/load/_config.py +0 -31
- arguebuf-2.7.2/src/arguebuf/load/_load_aif.py +0 -368
- arguebuf-2.7.2/src/arguebuf/load/_load_aml.py +0 -238
- arguebuf-2.7.2/src/arguebuf/load/_load_argdown.py +0 -93
- arguebuf-2.7.2/src/arguebuf/load/_load_brat.py +0 -57
- arguebuf-2.7.2/src/arguebuf/load/_load_casebase.py +0 -165
- arguebuf-2.7.2/src/arguebuf/load/_load_dict.py +0 -29
- arguebuf-2.7.2/src/arguebuf/load/_load_io.py +0 -32
- arguebuf-2.7.2/src/arguebuf/load/_load_json.py +0 -18
- arguebuf-2.7.2/src/arguebuf/load/_load_microtexts.py +0 -126
- arguebuf-2.7.2/src/arguebuf/load/_load_ova.py +0 -165
- arguebuf-2.7.2/src/arguebuf/load/_load_path.py +0 -78
- arguebuf-2.7.2/src/arguebuf/load/_load_protobuf.py +0 -192
- arguebuf-2.7.2/src/arguebuf/load/_load_sadface.py +0 -123
- arguebuf-2.7.2/src/arguebuf/load/_load_text.py +0 -28
- arguebuf-2.7.2/src/arguebuf/load/_load_xaif.py +0 -96
- arguebuf-2.7.2/src/arguebuf/model/analyst.py +0 -27
- arguebuf-2.7.2/src/arguebuf/model/edge.py +0 -95
- arguebuf-2.7.2/src/arguebuf/model/graph.py +0 -697
- arguebuf-2.7.2/src/arguebuf/model/metadata.py +0 -21
- arguebuf-2.7.2/src/arguebuf/model/node.py +0 -218
- arguebuf-2.7.2/src/arguebuf/model/participant.py +0 -43
- arguebuf-2.7.2/src/arguebuf/model/reference.py +0 -31
- arguebuf-2.7.2/src/arguebuf/model/resource.py +0 -30
- arguebuf-2.7.2/src/arguebuf/model/scheme.py +0 -270
- arguebuf-2.7.2/src/arguebuf/model/typing.py +0 -15
- arguebuf-2.7.2/src/arguebuf/model/userdata.py +0 -5
- arguebuf-2.7.2/src/arguebuf/model/utils.py +0 -145
- arguebuf-2.7.2/src/arguebuf/render/_render_d2.py +0 -35
- arguebuf-2.7.2/src/arguebuf/render/_render_graphviz.py +0 -43
- arguebuf-2.7.2/src/arguebuf/schemas/aif.py +0 -34
- arguebuf-2.7.2/src/arguebuf/schemas/d2.py +0 -60
- arguebuf-2.7.2/src/arguebuf/schemas/graphviz.py +0 -12
- arguebuf-2.7.2/src/arguebuf/schemas/xaif.py +0 -68
- arguebuf-2.7.2/src/arguebuf/traverse.py +0 -126
- arguebuf-2.7.2/src/arguebuf/utils.py +0 -10
- arguebuf-2.7.2/src/arguebuf.egg-info/SOURCES.txt +0 -83
- arguebuf-2.7.2/src/arguebuf.egg-info/dependency_links.txt +0 -1
- arguebuf-2.7.2/src/arguebuf.egg-info/entry_points.txt +0 -2
- arguebuf-2.7.2/src/arguebuf.egg-info/requires.txt +0 -15
- arguebuf-2.7.2/src/arguebuf.egg-info/top_level.txt +0 -1
- arguebuf-2.7.2/tests/test_create_graph.py +0 -208
- arguebuf-2.7.2/tests/test_load_casebase.py +0 -116
- arguebuf-2.7.2/tests/test_load_edge.py +0 -259
- arguebuf-2.7.2/tests/test_load_node.py +0 -385
- arguebuf-2.7.2/tests/test_preprocess_dialog.py +0 -134
- arguebuf-2.7.2/tests/test_render_graph.py +0 -75
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/cli/__init__.py +0 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/cli/__main__.py +0 -0
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/load/__init__.py +6 -6
- {arguebuf-2.7.2 → arguebuf-3.0.0}/src/arguebuf/py.typed +0 -0
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arguebuf
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Create and analyze argument graphs and serialize them via Protobuf
|
|
5
|
+
Keywords: argument-graph,argument-mining,argumentation,nlp,protobuf
|
|
6
|
+
Author: Mirko Lenz
|
|
5
7
|
Author-email: Mirko Lenz <mirko@mirkolenz.com>
|
|
6
|
-
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Dist: arg-services>=2,<3
|
|
18
|
+
Requires-Dist: lxml>=6,<7
|
|
19
|
+
Requires-Dist: networkx>=3,<4
|
|
20
|
+
Requires-Dist: pygraphviz>=2,<3
|
|
21
|
+
Requires-Dist: arg-services[grpcio]>=2,<3 ; extra == 'cli'
|
|
22
|
+
Requires-Dist: deepl>=1,<2 ; extra == 'cli'
|
|
23
|
+
Requires-Dist: typer>=0.27,<0.28 ; extra == 'cli'
|
|
24
|
+
Requires-Python: >=3.13, <4
|
|
25
|
+
Project-URL: Changelog, https://github.com/recap-utr/arguebuf-python/releases
|
|
7
26
|
Project-URL: Documentation, https://arguebuf.readthedocs.io
|
|
8
27
|
Project-URL: Issues, https://github.com/recap-utr/arguebuf-python/issues
|
|
9
|
-
Project-URL:
|
|
10
|
-
Requires-Python: >=3.11
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
|
-
Requires-Dist: arg-services<2,>=1
|
|
14
|
-
Requires-Dist: networkx<4,>=3
|
|
15
|
-
Requires-Dist: pendulum<4,>=3
|
|
16
|
-
Requires-Dist: graphviz<1,>=0.20
|
|
17
|
-
Requires-Dist: lxml<6,>=5
|
|
18
|
-
Provides-Extra: all
|
|
19
|
-
Requires-Dist: arguebuf[cli,graphviz]; extra == "all"
|
|
28
|
+
Project-URL: Repository, https://github.com/recap-utr/arguebuf-python
|
|
20
29
|
Provides-Extra: cli
|
|
21
|
-
|
|
22
|
-
Requires-Dist: deepl<2,>=1; extra == "cli"
|
|
23
|
-
Provides-Extra: graphviz
|
|
24
|
-
Requires-Dist: pygraphviz<2,>=1; extra == "graphviz"
|
|
25
|
-
Dynamic: license-file
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
26
31
|
|
|
27
32
|
# Arguebuf Python
|
|
28
33
|
|
|
@@ -117,8 +122,9 @@ This also makes it really easy to integrate the popular [`spacy` library](http:/
|
|
|
117
122
|
|
|
118
123
|
```python
|
|
119
124
|
import spacy
|
|
125
|
+
|
|
120
126
|
nlp = spacy.load("en_core_web_lg")
|
|
121
|
-
graph = ag.load.file("graph.json", nlp=nlp)
|
|
127
|
+
graph = ag.load.file("graph.json", config=ag.load.Config(nlp=nlp))
|
|
122
128
|
```
|
|
123
129
|
|
|
124
130
|
### Programmatically Create a New Graph
|
|
@@ -91,8 +91,9 @@ This also makes it really easy to integrate the popular [`spacy` library](http:/
|
|
|
91
91
|
|
|
92
92
|
```python
|
|
93
93
|
import spacy
|
|
94
|
+
|
|
94
95
|
nlp = spacy.load("en_core_web_lg")
|
|
95
|
-
graph = ag.load.file("graph.json", nlp=nlp)
|
|
96
|
+
graph = ag.load.file("graph.json", config=ag.load.Config(nlp=nlp))
|
|
96
97
|
```
|
|
97
98
|
|
|
98
99
|
### Programmatically Create a New Graph
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "arguebuf"
|
|
3
|
+
version = "3.0.0"
|
|
4
|
+
description = "Create and analyze argument graphs and serialize them via Protobuf"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13,<4"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
keywords = [
|
|
9
|
+
"argument-graph",
|
|
10
|
+
"argument-mining",
|
|
11
|
+
"argumentation",
|
|
12
|
+
"nlp",
|
|
13
|
+
"protobuf",
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Intended Audience :: Science/Research",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Programming Language :: Python :: 3.15",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"arg-services>=2,<3",
|
|
27
|
+
"lxml>=6,<7",
|
|
28
|
+
"networkx>=3,<4",
|
|
29
|
+
"pygraphviz>=2,<3",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[[project.authors]]
|
|
33
|
+
name = "Mirko Lenz"
|
|
34
|
+
email = "mirko@mirkolenz.com"
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Changelog = "https://github.com/recap-utr/arguebuf-python/releases"
|
|
38
|
+
Documentation = "https://arguebuf.readthedocs.io"
|
|
39
|
+
Issues = "https://github.com/recap-utr/arguebuf-python/issues"
|
|
40
|
+
Repository = "https://github.com/recap-utr/arguebuf-python"
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
arguebuf = "arguebuf.cli.app:cli"
|
|
44
|
+
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
cli = [
|
|
47
|
+
"arg-services[grpcio]>=2,<3",
|
|
48
|
+
"deepl>=1,<2",
|
|
49
|
+
"typer>=0.27,<0.28",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[dependency-groups]
|
|
53
|
+
dev = [
|
|
54
|
+
"lxml-stubs>=0.5,<0.6",
|
|
55
|
+
"types-networkx>=3,<4",
|
|
56
|
+
{ include-group = "docs" },
|
|
57
|
+
{ include-group = "test" },
|
|
58
|
+
]
|
|
59
|
+
docs = ["pdoc>=16,<17"]
|
|
60
|
+
test = [
|
|
61
|
+
"deepdiff>=9,<10",
|
|
62
|
+
"pytest>=9,<10",
|
|
63
|
+
"pytest-cov>=7,<8",
|
|
64
|
+
"pytest-xdist>=3,<4",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[build-system]
|
|
68
|
+
requires = ["uv-build>=0.13,<0.14"]
|
|
69
|
+
build-backend = "uv_build"
|
|
70
|
+
|
|
71
|
+
[tool.pytest]
|
|
72
|
+
addopts = [
|
|
73
|
+
"--numprocesses=auto",
|
|
74
|
+
"--cov=arguebuf",
|
|
75
|
+
"--cov-report=term-missing",
|
|
76
|
+
"--doctest-modules",
|
|
77
|
+
"--ignore=src/arguebuf/cli",
|
|
78
|
+
]
|
|
79
|
+
doctest_optionflags = [
|
|
80
|
+
"ELLIPSIS",
|
|
81
|
+
"IGNORE_EXCEPTION_DETAIL",
|
|
82
|
+
"NORMALIZE_WHITESPACE",
|
|
83
|
+
]
|
|
84
|
+
testpaths = [
|
|
85
|
+
"src",
|
|
86
|
+
"tests",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.ruff.lint.flake8-bugbear]
|
|
90
|
+
extend-immutable-calls = ["arguebuf.load._config.Config"]
|
|
91
|
+
|
|
92
|
+
[tool.ruff.lint.pydocstyle]
|
|
93
|
+
convention = "google"
|
|
94
|
+
|
|
95
|
+
[tool.uv.exclude-newer-package]
|
|
96
|
+
arg-services = false
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "arguebuf"
|
|
3
|
+
version = "3.0.0"
|
|
4
|
+
description = "Create and analyze argument graphs and serialize them via Protobuf"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13,<4"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [{ name = "Mirko Lenz", email = "mirko@mirkolenz.com" }]
|
|
9
|
+
keywords = ["argument-graph", "argument-mining", "argumentation", "nlp", "protobuf"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Intended Audience :: Science/Research",
|
|
12
|
+
"Operating System :: OS Independent",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.13",
|
|
15
|
+
"Programming Language :: Python :: 3.14",
|
|
16
|
+
"Programming Language :: Python :: 3.15",
|
|
17
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"arg-services>=2,<3",
|
|
22
|
+
"lxml>=6,<7",
|
|
23
|
+
"networkx>=3,<4",
|
|
24
|
+
"pygraphviz>=2,<3",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Changelog = "https://github.com/recap-utr/arguebuf-python/releases"
|
|
29
|
+
Documentation = "https://arguebuf.readthedocs.io"
|
|
30
|
+
Issues = "https://github.com/recap-utr/arguebuf-python/issues"
|
|
31
|
+
Repository = "https://github.com/recap-utr/arguebuf-python"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
arguebuf = "arguebuf.cli.app:cli"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
cli = ["arg-services[grpcio]>=2,<3", "deepl>=1,<2", "typer>=0.27,<0.28"]
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"lxml-stubs>=0.5,<0.6",
|
|
42
|
+
"types-networkx>=3,<4",
|
|
43
|
+
{ include-group = "docs" },
|
|
44
|
+
{ include-group = "test" },
|
|
45
|
+
]
|
|
46
|
+
docs = ["pdoc>=16,<17"]
|
|
47
|
+
test = [
|
|
48
|
+
"deepdiff>=9,<10",
|
|
49
|
+
"pytest>=9,<10",
|
|
50
|
+
"pytest-cov>=7,<8",
|
|
51
|
+
"pytest-xdist>=3,<4",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[build-system]
|
|
55
|
+
requires = ["uv-build>=0.13,<0.14"]
|
|
56
|
+
build-backend = "uv_build"
|
|
57
|
+
|
|
58
|
+
[tool.pytest]
|
|
59
|
+
addopts = [
|
|
60
|
+
"--numprocesses=auto",
|
|
61
|
+
"--cov=arguebuf",
|
|
62
|
+
"--cov-report=term-missing",
|
|
63
|
+
"--doctest-modules",
|
|
64
|
+
"--ignore=src/arguebuf/cli",
|
|
65
|
+
]
|
|
66
|
+
doctest_optionflags = ["ELLIPSIS", "IGNORE_EXCEPTION_DETAIL", "NORMALIZE_WHITESPACE"]
|
|
67
|
+
testpaths = ["src", "tests"]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint.flake8-bugbear]
|
|
70
|
+
# frozen and thus safe to use as default argument
|
|
71
|
+
extend-immutable-calls = ["arguebuf.load._config.Config"]
|
|
72
|
+
|
|
73
|
+
[tool.ruff.lint.pydocstyle]
|
|
74
|
+
convention = "google"
|
|
75
|
+
|
|
76
|
+
[tool.uv.exclude-newer-package]
|
|
77
|
+
arg-services = false
|
|
@@ -30,32 +30,29 @@ from .utils import copy
|
|
|
30
30
|
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
31
31
|
|
|
32
32
|
__all__ = (
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"dump",
|
|
36
|
-
"render",
|
|
37
|
-
"traverse",
|
|
38
|
-
"schemas",
|
|
39
|
-
"model",
|
|
40
|
-
# functions
|
|
41
|
-
"uuid",
|
|
42
|
-
"copy",
|
|
43
|
-
# classes
|
|
44
|
-
"Graph",
|
|
33
|
+
"AbstractNode",
|
|
34
|
+
"Analyst",
|
|
45
35
|
"AtomNode",
|
|
46
|
-
"
|
|
36
|
+
"AtomOrSchemeNode",
|
|
37
|
+
"Attack",
|
|
47
38
|
"Edge",
|
|
39
|
+
"Graph",
|
|
48
40
|
"Metadata",
|
|
49
|
-
"Userdata",
|
|
50
|
-
"Analyst",
|
|
51
41
|
"Participant",
|
|
52
|
-
"
|
|
42
|
+
"Preference",
|
|
53
43
|
"Reference",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
44
|
+
"Rephrase",
|
|
45
|
+
"Resource",
|
|
56
46
|
"Scheme",
|
|
47
|
+
"SchemeNode",
|
|
57
48
|
"Support",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
49
|
+
"Userdata",
|
|
50
|
+
"copy",
|
|
51
|
+
"dump",
|
|
52
|
+
"load",
|
|
53
|
+
"model",
|
|
54
|
+
"render",
|
|
55
|
+
"schemas",
|
|
56
|
+
"traverse",
|
|
57
|
+
"uuid",
|
|
61
58
|
)
|
|
@@ -2,7 +2,10 @@ from typer import Typer
|
|
|
2
2
|
|
|
3
3
|
from . import graph, server, text
|
|
4
4
|
|
|
5
|
-
cli = Typer(
|
|
5
|
+
cli = Typer(
|
|
6
|
+
help="Convert, render, translate, and serve argument graphs.",
|
|
7
|
+
no_args_is_help=True,
|
|
8
|
+
)
|
|
6
9
|
cli.add_typer(
|
|
7
10
|
graph.cli, name="graph", help="Commands for dealing with argument graphs."
|
|
8
11
|
)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from deepl import DeepLClient
|
|
7
|
+
|
|
8
|
+
import arguebuf as ag
|
|
9
|
+
|
|
10
|
+
from . import model
|
|
11
|
+
from .translator import Translator
|
|
12
|
+
|
|
13
|
+
cli = typer.Typer(no_args_is_help=True)
|
|
14
|
+
|
|
15
|
+
OutputFormat = Annotated[
|
|
16
|
+
ag.dump.Format, typer.Option(help="Format of the exported graphs.")
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@cli.command()
|
|
21
|
+
def translate(
|
|
22
|
+
input_folder: model.InputFolder,
|
|
23
|
+
source_lang: model.SourceLang,
|
|
24
|
+
target_lang: model.TargetLang,
|
|
25
|
+
auth_key: model.AuthKey,
|
|
26
|
+
input_glob: model.InputGlob,
|
|
27
|
+
output_folder: model.OutputFolder = None,
|
|
28
|
+
output_format: OutputFormat = ag.dump.Format.ARGUEBUF,
|
|
29
|
+
clean: model.Clean = False,
|
|
30
|
+
overwrite: model.Overwrite = False,
|
|
31
|
+
start: model.Start = 1,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Translate the texts of atom nodes, references, and resources with DeepL."""
|
|
34
|
+
translator = Translator(DeepLClient(auth_key), source_lang, target_lang)
|
|
35
|
+
|
|
36
|
+
for path_pair in model.pending_pairs(
|
|
37
|
+
input_folder, output_folder, input_glob, ".json", clean, overwrite, start
|
|
38
|
+
):
|
|
39
|
+
graph = ag.load.file(path_pair.source)
|
|
40
|
+
translator.translate_graph(graph)
|
|
41
|
+
ag.dump.file(graph, path_pair.target, ag.dump.Config(format=output_format))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def node_label_formatter(
|
|
45
|
+
strip_labels: bool, strip_labels_char: str | None
|
|
46
|
+
) -> Callable[[ag.AbstractNode], str] | None:
|
|
47
|
+
_replace_char = "–" if strip_labels_char is None else strip_labels_char
|
|
48
|
+
|
|
49
|
+
def _node_label(node: ag.AbstractNode) -> str:
|
|
50
|
+
return "".join(char if char.isspace() else _replace_char for char in node.label)
|
|
51
|
+
|
|
52
|
+
if strip_labels:
|
|
53
|
+
return _node_label
|
|
54
|
+
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@cli.command()
|
|
59
|
+
def render(
|
|
60
|
+
input_folder: model.InputFolder,
|
|
61
|
+
input_glob: model.InputGlob,
|
|
62
|
+
output_folder: model.OutputFolder = None,
|
|
63
|
+
output_format: Annotated[
|
|
64
|
+
str,
|
|
65
|
+
typer.Option(
|
|
66
|
+
help="Suffix of the rendered files that determines their format,"
|
|
67
|
+
" e.g., '.pdf', '.png', or '.svg'."
|
|
68
|
+
),
|
|
69
|
+
] = ".pdf",
|
|
70
|
+
strip_scheme_nodes: Annotated[
|
|
71
|
+
bool,
|
|
72
|
+
typer.Option(help="Remove scheme nodes and connect their atoms directly."),
|
|
73
|
+
] = False,
|
|
74
|
+
strip_node_labels: Annotated[
|
|
75
|
+
bool,
|
|
76
|
+
typer.Option(help="Mask node labels, e.g., to show only the structure."),
|
|
77
|
+
] = False,
|
|
78
|
+
strip_node_labels_char: Annotated[
|
|
79
|
+
str | None,
|
|
80
|
+
typer.Option(
|
|
81
|
+
help="Character replacing all non-whitespace characters of masked labels,"
|
|
82
|
+
" defaults to an en dash.",
|
|
83
|
+
show_default=False,
|
|
84
|
+
),
|
|
85
|
+
] = None,
|
|
86
|
+
edge_style: Annotated[
|
|
87
|
+
ag.schemas.graphviz.EdgeStyle, typer.Option(help="Shape of the edges.")
|
|
88
|
+
] = ag.schemas.graphviz.EdgeStyle.STEP,
|
|
89
|
+
nodesep: Annotated[
|
|
90
|
+
float,
|
|
91
|
+
typer.Option(help="Minimum space between nodes of the same rank in inches."),
|
|
92
|
+
] = 0.25,
|
|
93
|
+
ranksep: Annotated[
|
|
94
|
+
float, typer.Option(help="Minimum space between ranks in inches.")
|
|
95
|
+
] = 0.5,
|
|
96
|
+
node_wrap_col: Annotated[
|
|
97
|
+
int, typer.Option(help="Maximum number of characters per line in labels.")
|
|
98
|
+
] = 36,
|
|
99
|
+
node_margin: Annotated[
|
|
100
|
+
tuple[float, float],
|
|
101
|
+
typer.Option(help="Horizontal and vertical padding of labels in inches."),
|
|
102
|
+
] = (0.15, 0.1),
|
|
103
|
+
font_name: Annotated[str, typer.Option(help="Font of the labels.")] = "Arial",
|
|
104
|
+
font_size: Annotated[
|
|
105
|
+
float, typer.Option(help="Font size of the labels in points.")
|
|
106
|
+
] = 11,
|
|
107
|
+
clean: model.Clean = False,
|
|
108
|
+
overwrite: model.Overwrite = False,
|
|
109
|
+
start: model.Start = 1,
|
|
110
|
+
max_nodes: Annotated[
|
|
111
|
+
int, typer.Option(help="Skip graphs with more nodes than this limit.")
|
|
112
|
+
] = 1000,
|
|
113
|
+
prog: Annotated[
|
|
114
|
+
str, typer.Option(help="Graphviz layout engine, e.g., 'dot' or 'neato'.")
|
|
115
|
+
] = "dot",
|
|
116
|
+
dpi: Annotated[
|
|
117
|
+
int, typer.Option(help="Resolution of raster formats such as PNG.")
|
|
118
|
+
] = 300,
|
|
119
|
+
monochrome: Annotated[
|
|
120
|
+
bool, typer.Option(help="Render in black and white instead of color.")
|
|
121
|
+
] = False,
|
|
122
|
+
) -> None:
|
|
123
|
+
"""Render graphs as images with Graphviz."""
|
|
124
|
+
label_formatter = node_label_formatter(strip_node_labels, strip_node_labels_char)
|
|
125
|
+
|
|
126
|
+
for path_pair in model.pending_pairs(
|
|
127
|
+
input_folder, output_folder, input_glob, output_format, clean, overwrite, start
|
|
128
|
+
):
|
|
129
|
+
g = ag.load.file(path_pair.source)
|
|
130
|
+
|
|
131
|
+
if strip_scheme_nodes:
|
|
132
|
+
g.strip_scheme_nodes()
|
|
133
|
+
|
|
134
|
+
gv = ag.dump.graphviz(
|
|
135
|
+
g,
|
|
136
|
+
nodesep=nodesep,
|
|
137
|
+
ranksep=ranksep,
|
|
138
|
+
wrap_col=node_wrap_col,
|
|
139
|
+
margin=node_margin,
|
|
140
|
+
font_name=font_name,
|
|
141
|
+
font_size=font_size,
|
|
142
|
+
atom_label=label_formatter,
|
|
143
|
+
scheme_label=label_formatter,
|
|
144
|
+
edge_style=edge_style,
|
|
145
|
+
max_nodes=max_nodes,
|
|
146
|
+
monochrome=monochrome,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# graphs exceeding `max_nodes` are skipped
|
|
150
|
+
if gv is not None:
|
|
151
|
+
ag.render.graphviz(gv, path_pair.target, prog, dpi)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@cli.command()
|
|
155
|
+
def convert(
|
|
156
|
+
input_folder: model.InputFolder,
|
|
157
|
+
input_glob: model.InputGlob,
|
|
158
|
+
output_folder: model.OutputFolder = None,
|
|
159
|
+
output_format: OutputFormat = ag.dump.Format.ARGUEBUF,
|
|
160
|
+
clean: model.Clean = False,
|
|
161
|
+
overwrite: model.Overwrite = False,
|
|
162
|
+
start: model.Start = 1,
|
|
163
|
+
text_folder: Annotated[
|
|
164
|
+
Path | None,
|
|
165
|
+
typer.Option(
|
|
166
|
+
help="Folder mirroring INPUT_FOLDER with plain texts to add as resources,"
|
|
167
|
+
" defaults to the text files next to the graphs.",
|
|
168
|
+
show_default=False,
|
|
169
|
+
),
|
|
170
|
+
] = None,
|
|
171
|
+
text_suffix: Annotated[
|
|
172
|
+
str, typer.Option(help="Suffix of the files in TEXT_FOLDER.")
|
|
173
|
+
] = ".txt",
|
|
174
|
+
reconstruct_dialog: Annotated[
|
|
175
|
+
bool,
|
|
176
|
+
typer.Option(
|
|
177
|
+
help="Connect AIF and xAIF atoms only linked through dialog nodes"
|
|
178
|
+
" to the atoms of adjacent turns, otherwise they become isolated."
|
|
179
|
+
),
|
|
180
|
+
] = False,
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Convert graphs between formats, the input format is derived from the suffix."""
|
|
183
|
+
load_config = ag.load.Config(reconstruct_dialog=reconstruct_dialog)
|
|
184
|
+
dump_config = ag.dump.Config(format=output_format)
|
|
185
|
+
|
|
186
|
+
for path_pair in model.pending_pairs(
|
|
187
|
+
input_folder, output_folder, input_glob, ".json", clean, overwrite, start
|
|
188
|
+
):
|
|
189
|
+
text_file = (
|
|
190
|
+
text_folder
|
|
191
|
+
/ path_pair.source.relative_to(input_folder).with_suffix(text_suffix)
|
|
192
|
+
if text_folder
|
|
193
|
+
else None
|
|
194
|
+
)
|
|
195
|
+
graph = ag.load.file(path_pair.source, text_file=text_file, config=load_config)
|
|
196
|
+
ag.dump.file(graph, path_pair.target, dump_config)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@cli.command()
|
|
200
|
+
def statistics(
|
|
201
|
+
input_folder: Annotated[
|
|
202
|
+
Path,
|
|
203
|
+
typer.Argument(
|
|
204
|
+
help="Folder to search with INPUT_GLOB.", exists=True, file_okay=False
|
|
205
|
+
),
|
|
206
|
+
],
|
|
207
|
+
input_glob: model.InputGlob,
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Print node and edge counts of the selected graphs."""
|
|
210
|
+
files = sorted(input_folder.glob(input_glob))
|
|
211
|
+
|
|
212
|
+
graphs = [ag.load.file(file) for file in files]
|
|
213
|
+
atom_nodes = [len(graph.atom_nodes) for graph in graphs]
|
|
214
|
+
scheme_nodes = [len(graph.scheme_nodes) for graph in graphs]
|
|
215
|
+
edges = [len(graph.edges) for graph in graphs]
|
|
216
|
+
|
|
217
|
+
total_graphs = len(graphs)
|
|
218
|
+
total_atom_nodes = sum(atom_nodes)
|
|
219
|
+
total_scheme_nodes = sum(scheme_nodes)
|
|
220
|
+
total_edges = sum(edges)
|
|
221
|
+
|
|
222
|
+
typer.echo(
|
|
223
|
+
f"""Total Graphs: {total_graphs}
|
|
224
|
+
|
|
225
|
+
Total Atom Nodes: {total_atom_nodes}
|
|
226
|
+
Total Scheme Nodes: {total_scheme_nodes}
|
|
227
|
+
Total Edges: {total_edges}
|
|
228
|
+
|
|
229
|
+
Atom Nodes per Graph: {total_atom_nodes / total_graphs}
|
|
230
|
+
Scheme Nodes per Graph: {total_scheme_nodes / total_graphs}
|
|
231
|
+
Edges per Graph: {total_edges / total_graphs}
|
|
232
|
+
|
|
233
|
+
Max. Atom Nodes: {max(atom_nodes)}
|
|
234
|
+
Max. Scheme Nodes: {max(scheme_nodes)}
|
|
235
|
+
Max. Edges: {max(edges)}
|
|
236
|
+
|
|
237
|
+
Min. Atom Nodes: {min(atom_nodes)}
|
|
238
|
+
Min. Scheme Nodes: {min(scheme_nodes)}
|
|
239
|
+
Min. Edges: {min(edges)}"""
|
|
240
|
+
)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from collections.abc import Iterator
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Annotated, Self
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
InputFolder = Annotated[
|
|
10
|
+
Path,
|
|
11
|
+
typer.Argument(
|
|
12
|
+
help="File or folder to process, folders are searched with INPUT_GLOB.",
|
|
13
|
+
exists=True,
|
|
14
|
+
),
|
|
15
|
+
]
|
|
16
|
+
InputGlob = Annotated[
|
|
17
|
+
str,
|
|
18
|
+
typer.Argument(
|
|
19
|
+
help="Glob pattern selecting the files within INPUT_FOLDER, e.g., '**/*.json'."
|
|
20
|
+
),
|
|
21
|
+
]
|
|
22
|
+
OutputFolder = Annotated[
|
|
23
|
+
Path | None,
|
|
24
|
+
typer.Option(
|
|
25
|
+
help="File or folder to write the results to, defaults to INPUT_FOLDER.",
|
|
26
|
+
show_default=False,
|
|
27
|
+
),
|
|
28
|
+
]
|
|
29
|
+
Clean = Annotated[
|
|
30
|
+
bool, typer.Option(help="Delete the output folder before processing.")
|
|
31
|
+
]
|
|
32
|
+
Overwrite = Annotated[
|
|
33
|
+
bool, typer.Option(help="Overwrite existing output files instead of skipping.")
|
|
34
|
+
]
|
|
35
|
+
Start = Annotated[
|
|
36
|
+
int,
|
|
37
|
+
typer.Option(
|
|
38
|
+
help="Position of the first input file to process, used to resume a run.",
|
|
39
|
+
min=1,
|
|
40
|
+
),
|
|
41
|
+
]
|
|
42
|
+
SourceLang = Annotated[
|
|
43
|
+
str, typer.Argument(help="DeepL code of the source language, e.g., 'DE'.")
|
|
44
|
+
]
|
|
45
|
+
TargetLang = Annotated[
|
|
46
|
+
str, typer.Argument(help="DeepL code of the target language, e.g., 'EN-US'.")
|
|
47
|
+
]
|
|
48
|
+
AuthKey = Annotated[
|
|
49
|
+
str,
|
|
50
|
+
typer.Option(
|
|
51
|
+
help="DeepL API authentication key, prefer the environment variable.",
|
|
52
|
+
envvar="DEEPL_AUTH_KEY",
|
|
53
|
+
show_default=False,
|
|
54
|
+
),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(slots=True, frozen=True)
|
|
59
|
+
class PathPair:
|
|
60
|
+
"""A pair of paths for modification of files."""
|
|
61
|
+
|
|
62
|
+
source: Path
|
|
63
|
+
target: Path
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def create(
|
|
67
|
+
cls,
|
|
68
|
+
path_in: Path,
|
|
69
|
+
path_out: Path,
|
|
70
|
+
input_glob: str | None,
|
|
71
|
+
output_suffix: str | None,
|
|
72
|
+
) -> list[Self]:
|
|
73
|
+
pairs: list[Self] = []
|
|
74
|
+
|
|
75
|
+
if path_in.is_file():
|
|
76
|
+
if path_out.suffix:
|
|
77
|
+
pairs.append(cls(path_in, path_out))
|
|
78
|
+
elif output_suffix:
|
|
79
|
+
pairs.append(cls(path_in, path_out.with_suffix(output_suffix)))
|
|
80
|
+
else:
|
|
81
|
+
raise ValueError(
|
|
82
|
+
"File given as input. Please also provide a file as output."
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
elif path_in.is_dir() and input_glob and output_suffix:
|
|
86
|
+
path_out.mkdir(parents=True, exist_ok=True)
|
|
87
|
+
|
|
88
|
+
files_in = sorted(path_in.glob(input_glob))
|
|
89
|
+
files_out: list[Path] = []
|
|
90
|
+
|
|
91
|
+
for file_in in files_in:
|
|
92
|
+
file_out = path_out / file_in.relative_to(path_in)
|
|
93
|
+
file_out = file_out.with_suffix(output_suffix)
|
|
94
|
+
file_out.parent.mkdir(parents=True, exist_ok=True)
|
|
95
|
+
|
|
96
|
+
files_out.append(file_out)
|
|
97
|
+
|
|
98
|
+
pairs.extend(
|
|
99
|
+
cls(file_in, file_out)
|
|
100
|
+
for file_in, file_out in zip(files_in, files_out, strict=True)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
else:
|
|
104
|
+
raise ValueError(
|
|
105
|
+
"Folder given as input. Please also provide a folder as output together"
|
|
106
|
+
" with a shell globbing pattern and an output suffix."
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return pairs
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _label(path_pair: PathPair | None) -> str:
|
|
113
|
+
return path_pair.source.name if path_pair else ""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def pending_pairs(
|
|
117
|
+
input_folder: Path,
|
|
118
|
+
output_folder: Path | None,
|
|
119
|
+
input_glob: str | None,
|
|
120
|
+
output_suffix: str | None,
|
|
121
|
+
clean: bool,
|
|
122
|
+
overwrite: bool,
|
|
123
|
+
start: int,
|
|
124
|
+
) -> Iterator[PathPair]:
|
|
125
|
+
"""Yield the path pairs still to be processed while showing a progress bar.
|
|
126
|
+
|
|
127
|
+
The output folder defaults to the input folder.
|
|
128
|
+
"""
|
|
129
|
+
output_folder = output_folder or input_folder
|
|
130
|
+
|
|
131
|
+
if clean:
|
|
132
|
+
# cleaning an output folder that contains the inputs would delete them
|
|
133
|
+
if input_folder.resolve().is_relative_to(output_folder.resolve()):
|
|
134
|
+
raise typer.BadParameter(
|
|
135
|
+
"The output folder must not contain the input when using --clean."
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
shutil.rmtree(output_folder, ignore_errors=True)
|
|
139
|
+
output_folder.mkdir(parents=True)
|
|
140
|
+
|
|
141
|
+
pairs = PathPair.create(input_folder, output_folder, input_glob, output_suffix)
|
|
142
|
+
|
|
143
|
+
with typer.progressbar(
|
|
144
|
+
pairs[start - 1 :], item_show_func=_label, show_pos=True
|
|
145
|
+
) as bar:
|
|
146
|
+
for path_pair in bar:
|
|
147
|
+
if overwrite or not path_pair.target.exists():
|
|
148
|
+
yield path_pair
|