pyomnigraph 0.2.2__tar.gz → 0.3.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.
Files changed (66) hide show
  1. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/.github/workflows/build.yml +14 -4
  2. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/.github/workflows/upload-to-pypi.yml +2 -2
  3. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/.project +1 -0
  4. pyomnigraph-0.3.0/CITATION.cff +31 -0
  5. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/PKG-INFO +40 -25
  6. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/README.md +39 -24
  7. pyomnigraph-0.3.0/docs/images/ypgen-generator-grid.webp +0 -0
  8. pyomnigraph-0.3.0/omnigraph/__init__.py +1 -0
  9. pyomnigraph-0.3.0/omnigraph/compose.py +176 -0
  10. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/omnigraph_cmd.py +28 -1
  11. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/omniserver.py +35 -14
  12. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/rdf_dataset.py +14 -1
  13. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/rdfdump.py +20 -2
  14. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/resources/examples/datasets.yaml +2 -0
  15. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/resources/examples/servers.yaml +31 -5
  16. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/server_config.py +96 -1
  17. pyomnigraph-0.3.0/omnigraph/servers/allegrograph.py +183 -0
  18. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/blazegraph.py +11 -3
  19. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/graphdb.py +39 -6
  20. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/jena.py +11 -4
  21. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/millenniumdb.py +44 -19
  22. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/oxigraph.py +5 -3
  23. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/qlever.py +52 -9
  24. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/stardog.py +1 -1
  25. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/virtuoso.py +95 -10
  26. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/software.py +11 -0
  27. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/sparql_server.py +245 -27
  28. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/version.py +5 -1
  29. pyomnigraph-0.3.0/tests/basesparqltest.py +129 -0
  30. pyomnigraph-0.3.0/tests/test_compose.py +94 -0
  31. pyomnigraph-0.3.0/tests/test_datasets.py +80 -0
  32. pyomnigraph-0.3.0/tests/test_examples.py +128 -0
  33. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/test_rdfdump.py +3 -0
  34. pyomnigraph-0.3.0/tests/test_royals.py +46 -0
  35. pyomnigraph-0.2.2/omnigraph/__init__.py +0 -1
  36. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/.gitignore +0 -0
  37. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/.pydevproject +0 -0
  38. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/AGENTS.md +0 -0
  39. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/LICENSE +0 -0
  40. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/docs/index.md +0 -0
  41. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/mkdocs.yml +0 -0
  42. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/basecmd.py +0 -0
  43. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/ominigraph_paths.py +0 -0
  44. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/rdfdump_cmd.py +0 -0
  45. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/resources/__init__.py +0 -0
  46. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/resources/examples/endpoints.yaml +0 -0
  47. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/resources/examples/prefixes.yaml +0 -0
  48. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/resources/examples/queries.yaml +0 -0
  49. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/resources/examples/royals.ttl +0 -0
  50. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/omnigraph/servers/__init__.py +0 -0
  51. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/pyproject.toml +0 -0
  52. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/scripts/blackisort +0 -0
  53. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/scripts/doc +0 -0
  54. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/scripts/gov-quickstart +0 -0
  55. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/scripts/install +0 -0
  56. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/scripts/quickstart +0 -0
  57. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/scripts/release +0 -0
  58. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/scripts/test +0 -0
  59. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/__init__.py +0 -0
  60. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/basetest.py +0 -0
  61. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/test_omniserver.py +0 -0
  62. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/test_qlever.py +0 -0
  63. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/test_rdf_dataset_params.py +0 -0
  64. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/test_software_list.py +0 -0
  65. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/test_sparql_server.py +0 -0
  66. {pyomnigraph-0.2.2 → pyomnigraph-0.3.0}/tests/test_upload.py +0 -0
@@ -18,21 +18,31 @@ jobs:
18
18
  runs-on: ubuntu-latest
19
19
  strategy:
20
20
  matrix:
21
- # os: [ubuntu-latest, macos-latest, windows-latest]
21
+ # os: [ubuntu-latest, macos-latest, windows-latest]
22
22
  os: [ubuntu-latest]
23
- # python-version: [ '3.10', '3.11', '3.12' ]
23
+ # python-version: [ '3.10', '3.11', '3.12', '3.13' ]
24
24
  python-version: [ '3.12' ]
25
25
 
26
26
  steps:
27
- - uses: actions/checkout@v4
27
+ - uses: actions/checkout@v6
28
28
  - name: Set up Python ${{ matrix.python-version }}
29
- uses: actions/setup-python@v5
29
+ uses: actions/setup-python@v6
30
30
  with:
31
31
  python-version: ${{ matrix.python-version }}
32
+ - name: Set up an emulator so images built for another processor family run
33
+ run: |
34
+ docker run --privileged --rm tonistiigi/binfmt --install arm64
35
+ - name: Check out the paper repository for its use case examples
36
+ uses: actions/checkout@v6
37
+ with:
38
+ repository: WolfgangFahl/pyomnigraphPaper
39
+ path: pyomnigraphPaper
32
40
  - name: Install python dependencies
33
41
  run: |
34
42
  python -m pip install --upgrade pip
35
43
  scripts/install
36
44
  - name: Run tests
45
+ env:
46
+ PYOMNIGRAPH_EXAMPLES: ${{ github.workspace }}/pyomnigraphPaper/examples/railway/dumps
37
47
  run: |
38
48
  scripts/test
@@ -11,9 +11,9 @@ jobs:
11
11
  # IMPORTANT: this permission is mandatory for trusted publishing
12
12
  id-token: write
13
13
  steps:
14
- - uses: actions/checkout@v4
14
+ - uses: actions/checkout@v6
15
15
  - name: Set up Python
16
- uses: actions/setup-python@v5
16
+ uses: actions/setup-python@v6
17
17
  with:
18
18
  python-version: '3.x'
19
19
  - name: Install dependencies
@@ -3,6 +3,7 @@
3
3
  <name>pyomnigraph</name>
4
4
  <comment></comment>
5
5
  <projects>
6
+ <project>pyLoDStorage</project>
6
7
  </projects>
7
8
  <buildSpec>
8
9
  <buildCommand>
@@ -0,0 +1,31 @@
1
+ # CITATION.cff for https://github.com/WolfgangFahl/pyomnigraph
2
+ # This CITATION.cff file was initially generated with cffinit.
3
+ # Visit https://bit.ly/cffinit to generate yours today!
4
+ cff-version: 1.2.0
5
+ title: pyomnigraph
6
+ message: >-
7
+ If you use this software, please cite it using the
8
+ metadata from this file.
9
+ type: software
10
+ authors:
11
+ - given-names: Wolfgang
12
+ family-names: Fahl
13
+ affiliation: BITPlan GmbH
14
+ orcid: 'https://orcid.org/0000-0002-0821-6995'
15
+ identifiers:
16
+ - type: doi
17
+ value: 10.5281/zenodo.21721772
18
+ description: Concept DOI (all versions) on Zenodo
19
+ repository-code: 'https://github.com/WolfgangFahl/pyomnigraph'
20
+ url: 'https://wiki.bitplan.com/index.php/pyomnigraph'
21
+ abstract: >-
22
+ Unified Python interface for multiple graph databases
23
+ keywords:
24
+ - RDF
25
+ - SPARQL
26
+ - Triple Store
27
+ - Graph Database
28
+ - Docker
29
+ - Wikidata
30
+ - Federated Queries
31
+ license: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyomnigraph
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: pyomnigraph: Unified Python interface for multiple graph databases (SPARQL, Neo4j, Weaviate)
5
5
  Project-URL: Home, https://github.com/WolfgangFahl/omnigraph
6
6
  Project-URL: Documentation, https://wiki.bitplan.com/index.php/omnigraph
@@ -45,16 +45,29 @@ Requires-Dist: pytest>=7.0.0; extra == 'test'
45
45
  Description-Content-Type: text/markdown
46
46
 
47
47
  # omnigraph
48
- [![pypi](https://img.shields.io/pypi/pyversions/pyomnigraph)](https://pypi.org/project/pyomnigraph/)
49
- [![Github Actions Build](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml)
50
- [![PyPI Status](https://img.shields.io/pypi/v/pyomnigraph.svg)](https://pypi.python.org/pypi/pyomnigraph/)
51
- [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues)
52
- [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues/?q=is%3Aissue+is%3Aclosed)
53
- [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyomnigraph/)
54
- [![License](https://img.shields.io/github/license/WolfgangFahl/pyomnigraph.svg)](https://www.apache.org/licenses/LICENSE-2.0)
48
+ Unified Python interface for multiple graph databases
55
49
 
50
+ | | |
51
+ | :--- | :--- |
52
+ | **PyPi** | [![PyPI Status](https://img.shields.io/pypi/v/pyomnigraph.svg)](https://pypi.python.org/pypi/pyomnigraph/) [![License](https://img.shields.io/github/license/WolfgangFahl/pyomnigraph.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![pypi](https://img.shields.io/pypi/pyversions/pyomnigraph)](https://pypi.org/project/pyomnigraph/) [![format](https://img.shields.io/pypi/format/pyomnigraph)](https://pypi.org/project/pyomnigraph/) [![downloads](https://img.shields.io/pypi/dd/pyomnigraph)](https://pypi.org/project/pyomnigraph/) |
53
+ | **GitHub** | [![Github Actions Build](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml) [![Release](https://img.shields.io/github/v/release/WolfgangFahl/pyomnigraph)](https://github.com/WolfgangFahl/pyomnigraph/releases) [![Contributors](https://img.shields.io/github/contributors/WolfgangFahl/pyomnigraph)](https://github.com/WolfgangFahl/pyomnigraph/graphs/contributors) [![Last Commit](https://img.shields.io/github/last-commit/WolfgangFahl/pyomnigraph)](https://github.com/WolfgangFahl/pyomnigraph/commits/) [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues) [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues/?q=is%3Aissue+is%3Aclosed) |
54
+ | **Code** | [![style-black](https://img.shields.io/badge/%20style-black-000000.svg)](https://github.com/psf/black) [![imports-isort](https://img.shields.io/badge/%20imports-isort-%231674b1)](https://pycqa.github.io/isort/) |
55
+ | **Docs** | [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyomnigraph/) [![formatter-docformatter](https://img.shields.io/badge/%20formatter-docformatter-fedcba.svg)](https://github.com/PyCQA/docformatter) [![style-google](https://img.shields.io/badge/%20style-google-3666d6.svg)](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) |
56
+ | **Cite** | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21721772.svg)](https://doi.org/10.5281/zenodo.21721772) |
56
57
 
57
- Unified Python interface for multiple graph databases
58
+ ## Cite as
59
+
60
+ If you use pyomnigraph in your research, please cite it via its Zenodo concept DOI
61
+ (which always resolves to the latest version):
62
+
63
+ > Fahl, W.
64
+ > *pyomnigraph — Unified Python interface for multiple graph databases.*
65
+ > Zenodo. https://doi.org/10.5281/zenodo.21721772
66
+
67
+ Machine-readable metadata is available in [`CITATION.cff`](./CITATION.cff);
68
+ GitHub's "Cite this repository" button and Zenodo pick it up automatically.
69
+ For a specific release, use the version DOI shown on the corresponding
70
+ [Zenodo record](https://doi.org/10.5281/zenodo.21721772).
58
71
 
59
72
  ## Docs and Tutorials
60
73
  [Wiki](https://wiki.bitplan.com/index.php/pyomnigraph)
@@ -85,31 +98,33 @@ This leads to:
85
98
 
86
99
  | Database | Status | Notes |
87
100
  |----------|--------|-------|
88
- | **Apache Jena** | 🟢 Working | Robust, standards compliant |
101
+ | **AllegroGraph** | 🟢 Working | Multi-model, entity-event knowledge graphs, no license file needed |
89
102
  | **Blazegraph** | 🟢 Working | High performance, easy setup |
103
+ | **GraphDB** | 🟢 Working | Enterprise features and reasoning; runs on the free image, empty ruleset |
104
+ | **Apache Jena** | 🟢 Working | Robust, standards compliant |
105
+ | **MillenniumDB** | 🟢 Working | Property Graph + RDF, build-only: the store is produced by mdb import before it can serve |
90
106
  | **Oxigraph** | 🟢 Working | Rust-based, embedded, fast |
91
107
  | **QLever** | 🟢 Working | Extremely fast queries |
92
- | **GraphDB** | 🛑 Disabled | License required (free version available) |
93
- | **MillenniumDB** | 🛑 Disabled | Import-first workflow, Property Graph + RDF |
94
- | **Virtuoso** | 🛑 Disabled | Permissions issue (#30) |
95
- | **Stardog** | 🛑 Planned | License required |
108
+ | **Virtuoso** | 🟢 Working | Mature, SPARQL and SQL over one engine, reasoning and inference, fine grained attribute based access control, SPARQL via SQL |
109
+ | **Stardog** | 🛑 Disabled | License required, knowledge graphs and reasoning |
96
110
 
97
111
  Whether you're building a semantic web application, conducting research, or evaluating different triple stores, pyomnigraph eliminates the complexity of working with multiple graph database systems.
98
112
 
99
113
  ## Examples
100
114
  ```bash
101
- omnigraph omnigraph --list --include-inactive --doc-format github
115
+ omnigraph --list --include-inactive --doc-format github
102
116
  ```
103
- | Active | Name | Container Name | Wikidata | Image | Port | Test Port | Dataset | User |
104
- |----------|-------------------------------------|----------------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------|--------|-------------|-----------|--------|
105
- | 🟢️ | [blazegraph](http://localhost:9898) | blazegraph-omnigraph | [Q20127748](https://www.wikidata.org/wiki/Q20127748) | [lyrasis/blazegraph:2.1.5](https://hub.docker.com/r/lyrasis/blazegraph) | 9898 | 7898 | kb | |
106
- | 🟢️ | [jena](http://localhost:3030) | jena-omnigraph | [Q109376461](https://www.wikidata.org/wiki/Q109376461) | [stain/jena-fuseki:latest](https://hub.docker.com/r/stain/jena-fuseki) | 3030 | 7030 | ds | admin |
107
- | 🟢️ | [oxigraph](http://localhost:7878) | oxigraph-omnigraph | [Q118980507](https://www.wikidata.org/wiki/Q118980507) | [oxigraph/oxigraph:latest](https://hub.docker.com/r/oxigraph/oxigraph) | 7878 | 7378 | default | |
108
- | 🟢️ | [qlever](http://localhost:7019) | qlever-omnigraph | [Q111016295](https://www.wikidata.org/wiki/Q111016295) | [adfreiburg/qlever:latest](https://hub.docker.com/r/adfreiburg/qlever) | 7019 | 7819 | olympics | |
109
- | 🛑 | [graphdb](http://localhost:7200) | graphdb-omnigraph | [Q58425577](https://www.wikidata.org/wiki/Q58425577) | [ontotext/graphdb:11.2.0](https://hub.docker.com/r/ontotext/graphdb) | 7200 | 7700 | repo1 | |
110
- | 🛑 | [millenniumdb](http://localhost:1234)| millenniumdb-omnigraph| [Q118954995](https://www.wikidata.org/wiki/Q118954995) | [imfd/millenniumdb](https://hub.docker.com/r/imfd/millenniumdb) | 1234 | 1734 | default | |
111
- | 🛑 | [stardog](http://localhost:5820) | stardog-omnigraph | [Q91147741](https://www.wikidata.org/wiki/Q91147741) | [stardog/stardog:latest](https://hub.docker.com/r/stardog/stardog) | 5820 | 5320 | mydb | admin |
112
- | 🛑 | [virtuoso](http://localhost:8890) | virtuoso-omnigraph | [Q7935239](https://www.wikidata.org/wiki/Q7935239) | [openlink/virtuoso-opensource-7:latest](https://hub.docker.com/r/openlink/virtuoso-opensource-7) | 8890 | 8390 | KB | dba |
117
+ | Active | Name | Container Name | Wikidata | Image | Port | Test Port | Dataset | User |
118
+ |----------|----------------------------------------|------------------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------|--------|-------------|-----------|--------|
119
+ | 🟢️ | [allegrograph](http://localhost:10035) | allegrograph-omnigraph | [Q4731408](https://www.wikidata.org/wiki/Q4731408) | [franzinc/agraph:latest](https://hub.docker.com/r/franzinc/agraph) | 10035 | 10535 | omnigraph | admin |
120
+ | 🟢️ | [blazegraph](http://localhost:9898) | blazegraph-omnigraph | [Q20127748](https://www.wikidata.org/wiki/Q20127748) | [lyrasis/blazegraph:2.1.5](https://hub.docker.com/r/lyrasis/blazegraph) | 9898 | 7898 | kb | |
121
+ | 🟢️ | [graphdb](http://localhost:7200) | graphdb-omnigraph | [Q58425577](https://www.wikidata.org/wiki/Q58425577) | [ontotext/graphdb:11.2.0](https://hub.docker.com/r/ontotext/graphdb) | 7200 | 7700 | repo1 | |
122
+ | 🟢️ | [jena](http://localhost:3030) | jena-omnigraph | [Q109376461](https://www.wikidata.org/wiki/Q109376461) | [stain/jena-fuseki:latest](https://hub.docker.com/r/stain/jena-fuseki) | 3030 | 7030 | ds | admin |
123
+ | 🟢️ | [millenniumdb](http://localhost:1234) | millenniumdb-omnigraph | [Q118954995](https://www.wikidata.org/wiki/Q118954995) | [imfd/millenniumdb](https://hub.docker.com/r/imfd/millenniumdb) | 1234 | 1734 | default | |
124
+ | 🟢️ | [oxigraph](http://localhost:7878) | oxigraph-omnigraph | [Q118980507](https://www.wikidata.org/wiki/Q118980507) | [oxigraph/oxigraph:latest](https://hub.docker.com/r/oxigraph/oxigraph) | 7878 | 7378 | default | |
125
+ | 🟢️ | [qlever](http://localhost:7019) | qlever-omnigraph | [Q111016295](https://www.wikidata.org/wiki/Q111016295) | [adfreiburg/qlever:latest](https://hub.docker.com/r/adfreiburg/qlever) | 7019 | 7819 | olympics | |
126
+ | 🟢️ | [virtuoso](http://localhost:8890) | virtuoso-omnigraph | [Q7935239](https://www.wikidata.org/wiki/Q7935239) | [openlink/virtuoso-opensource-7:latest](https://hub.docker.com/r/openlink/virtuoso-opensource-7) | 8890 | 8390 | KB | dba |
127
+ | 🛑 | [stardog](http://localhost:5820) | stardog-omnigraph | [Q91147741](https://www.wikidata.org/wiki/Q91147741) | [stardog/stardog:latest](https://hub.docker.com/r/stardog/stardog) | 5820 | 5320 | mydb | admin |
113
128
 
114
129
  ### Server Management
115
130
 
@@ -1,14 +1,27 @@
1
1
  # omnigraph
2
- [![pypi](https://img.shields.io/pypi/pyversions/pyomnigraph)](https://pypi.org/project/pyomnigraph/)
3
- [![Github Actions Build](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml)
4
- [![PyPI Status](https://img.shields.io/pypi/v/pyomnigraph.svg)](https://pypi.python.org/pypi/pyomnigraph/)
5
- [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues)
6
- [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues/?q=is%3Aissue+is%3Aclosed)
7
- [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyomnigraph/)
8
- [![License](https://img.shields.io/github/license/WolfgangFahl/pyomnigraph.svg)](https://www.apache.org/licenses/LICENSE-2.0)
2
+ Unified Python interface for multiple graph databases
9
3
 
4
+ | | |
5
+ | :--- | :--- |
6
+ | **PyPi** | [![PyPI Status](https://img.shields.io/pypi/v/pyomnigraph.svg)](https://pypi.python.org/pypi/pyomnigraph/) [![License](https://img.shields.io/github/license/WolfgangFahl/pyomnigraph.svg)](https://www.apache.org/licenses/LICENSE-2.0) [![pypi](https://img.shields.io/pypi/pyversions/pyomnigraph)](https://pypi.org/project/pyomnigraph/) [![format](https://img.shields.io/pypi/format/pyomnigraph)](https://pypi.org/project/pyomnigraph/) [![downloads](https://img.shields.io/pypi/dd/pyomnigraph)](https://pypi.org/project/pyomnigraph/) |
7
+ | **GitHub** | [![Github Actions Build](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyomnigraph/actions/workflows/build.yml) [![Release](https://img.shields.io/github/v/release/WolfgangFahl/pyomnigraph)](https://github.com/WolfgangFahl/pyomnigraph/releases) [![Contributors](https://img.shields.io/github/contributors/WolfgangFahl/pyomnigraph)](https://github.com/WolfgangFahl/pyomnigraph/graphs/contributors) [![Last Commit](https://img.shields.io/github/last-commit/WolfgangFahl/pyomnigraph)](https://github.com/WolfgangFahl/pyomnigraph/commits/) [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues) [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyomnigraph.svg)](https://github.com/WolfgangFahl/pyomnigraph/issues/?q=is%3Aissue+is%3Aclosed) |
8
+ | **Code** | [![style-black](https://img.shields.io/badge/%20style-black-000000.svg)](https://github.com/psf/black) [![imports-isort](https://img.shields.io/badge/%20imports-isort-%231674b1)](https://pycqa.github.io/isort/) |
9
+ | **Docs** | [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyomnigraph/) [![formatter-docformatter](https://img.shields.io/badge/%20formatter-docformatter-fedcba.svg)](https://github.com/PyCQA/docformatter) [![style-google](https://img.shields.io/badge/%20style-google-3666d6.svg)](https://google.github.io/styleguide/pyguide.html#s3.8-comments-and-docstrings) |
10
+ | **Cite** | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21721772.svg)](https://doi.org/10.5281/zenodo.21721772) |
10
11
 
11
- Unified Python interface for multiple graph databases
12
+ ## Cite as
13
+
14
+ If you use pyomnigraph in your research, please cite it via its Zenodo concept DOI
15
+ (which always resolves to the latest version):
16
+
17
+ > Fahl, W.
18
+ > *pyomnigraph — Unified Python interface for multiple graph databases.*
19
+ > Zenodo. https://doi.org/10.5281/zenodo.21721772
20
+
21
+ Machine-readable metadata is available in [`CITATION.cff`](./CITATION.cff);
22
+ GitHub's "Cite this repository" button and Zenodo pick it up automatically.
23
+ For a specific release, use the version DOI shown on the corresponding
24
+ [Zenodo record](https://doi.org/10.5281/zenodo.21721772).
12
25
 
13
26
  ## Docs and Tutorials
14
27
  [Wiki](https://wiki.bitplan.com/index.php/pyomnigraph)
@@ -39,31 +52,33 @@ This leads to:
39
52
 
40
53
  | Database | Status | Notes |
41
54
  |----------|--------|-------|
42
- | **Apache Jena** | 🟢 Working | Robust, standards compliant |
55
+ | **AllegroGraph** | 🟢 Working | Multi-model, entity-event knowledge graphs, no license file needed |
43
56
  | **Blazegraph** | 🟢 Working | High performance, easy setup |
57
+ | **GraphDB** | 🟢 Working | Enterprise features and reasoning; runs on the free image, empty ruleset |
58
+ | **Apache Jena** | 🟢 Working | Robust, standards compliant |
59
+ | **MillenniumDB** | 🟢 Working | Property Graph + RDF, build-only: the store is produced by mdb import before it can serve |
44
60
  | **Oxigraph** | 🟢 Working | Rust-based, embedded, fast |
45
61
  | **QLever** | 🟢 Working | Extremely fast queries |
46
- | **GraphDB** | 🛑 Disabled | License required (free version available) |
47
- | **MillenniumDB** | 🛑 Disabled | Import-first workflow, Property Graph + RDF |
48
- | **Virtuoso** | 🛑 Disabled | Permissions issue (#30) |
49
- | **Stardog** | 🛑 Planned | License required |
62
+ | **Virtuoso** | 🟢 Working | Mature, SPARQL and SQL over one engine, reasoning and inference, fine grained attribute based access control, SPARQL via SQL |
63
+ | **Stardog** | 🛑 Disabled | License required, knowledge graphs and reasoning |
50
64
 
51
65
  Whether you're building a semantic web application, conducting research, or evaluating different triple stores, pyomnigraph eliminates the complexity of working with multiple graph database systems.
52
66
 
53
67
  ## Examples
54
68
  ```bash
55
- omnigraph omnigraph --list --include-inactive --doc-format github
69
+ omnigraph --list --include-inactive --doc-format github
56
70
  ```
57
- | Active | Name | Container Name | Wikidata | Image | Port | Test Port | Dataset | User |
58
- |----------|-------------------------------------|----------------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------|--------|-------------|-----------|--------|
59
- | 🟢️ | [blazegraph](http://localhost:9898) | blazegraph-omnigraph | [Q20127748](https://www.wikidata.org/wiki/Q20127748) | [lyrasis/blazegraph:2.1.5](https://hub.docker.com/r/lyrasis/blazegraph) | 9898 | 7898 | kb | |
60
- | 🟢️ | [jena](http://localhost:3030) | jena-omnigraph | [Q109376461](https://www.wikidata.org/wiki/Q109376461) | [stain/jena-fuseki:latest](https://hub.docker.com/r/stain/jena-fuseki) | 3030 | 7030 | ds | admin |
61
- | 🟢️ | [oxigraph](http://localhost:7878) | oxigraph-omnigraph | [Q118980507](https://www.wikidata.org/wiki/Q118980507) | [oxigraph/oxigraph:latest](https://hub.docker.com/r/oxigraph/oxigraph) | 7878 | 7378 | default | |
62
- | 🟢️ | [qlever](http://localhost:7019) | qlever-omnigraph | [Q111016295](https://www.wikidata.org/wiki/Q111016295) | [adfreiburg/qlever:latest](https://hub.docker.com/r/adfreiburg/qlever) | 7019 | 7819 | olympics | |
63
- | 🛑 | [graphdb](http://localhost:7200) | graphdb-omnigraph | [Q58425577](https://www.wikidata.org/wiki/Q58425577) | [ontotext/graphdb:11.2.0](https://hub.docker.com/r/ontotext/graphdb) | 7200 | 7700 | repo1 | |
64
- | 🛑 | [millenniumdb](http://localhost:1234)| millenniumdb-omnigraph| [Q118954995](https://www.wikidata.org/wiki/Q118954995) | [imfd/millenniumdb](https://hub.docker.com/r/imfd/millenniumdb) | 1234 | 1734 | default | |
65
- | 🛑 | [stardog](http://localhost:5820) | stardog-omnigraph | [Q91147741](https://www.wikidata.org/wiki/Q91147741) | [stardog/stardog:latest](https://hub.docker.com/r/stardog/stardog) | 5820 | 5320 | mydb | admin |
66
- | 🛑 | [virtuoso](http://localhost:8890) | virtuoso-omnigraph | [Q7935239](https://www.wikidata.org/wiki/Q7935239) | [openlink/virtuoso-opensource-7:latest](https://hub.docker.com/r/openlink/virtuoso-opensource-7) | 8890 | 8390 | KB | dba |
71
+ | Active | Name | Container Name | Wikidata | Image | Port | Test Port | Dataset | User |
72
+ |----------|----------------------------------------|------------------------|--------------------------------------------------------|--------------------------------------------------------------------------------------------------|--------|-------------|-----------|--------|
73
+ | 🟢️ | [allegrograph](http://localhost:10035) | allegrograph-omnigraph | [Q4731408](https://www.wikidata.org/wiki/Q4731408) | [franzinc/agraph:latest](https://hub.docker.com/r/franzinc/agraph) | 10035 | 10535 | omnigraph | admin |
74
+ | 🟢️ | [blazegraph](http://localhost:9898) | blazegraph-omnigraph | [Q20127748](https://www.wikidata.org/wiki/Q20127748) | [lyrasis/blazegraph:2.1.5](https://hub.docker.com/r/lyrasis/blazegraph) | 9898 | 7898 | kb | |
75
+ | 🟢️ | [graphdb](http://localhost:7200) | graphdb-omnigraph | [Q58425577](https://www.wikidata.org/wiki/Q58425577) | [ontotext/graphdb:11.2.0](https://hub.docker.com/r/ontotext/graphdb) | 7200 | 7700 | repo1 | |
76
+ | 🟢️ | [jena](http://localhost:3030) | jena-omnigraph | [Q109376461](https://www.wikidata.org/wiki/Q109376461) | [stain/jena-fuseki:latest](https://hub.docker.com/r/stain/jena-fuseki) | 3030 | 7030 | ds | admin |
77
+ | 🟢️ | [millenniumdb](http://localhost:1234) | millenniumdb-omnigraph | [Q118954995](https://www.wikidata.org/wiki/Q118954995) | [imfd/millenniumdb](https://hub.docker.com/r/imfd/millenniumdb) | 1234 | 1734 | default | |
78
+ | 🟢️ | [oxigraph](http://localhost:7878) | oxigraph-omnigraph | [Q118980507](https://www.wikidata.org/wiki/Q118980507) | [oxigraph/oxigraph:latest](https://hub.docker.com/r/oxigraph/oxigraph) | 7878 | 7378 | default | |
79
+ | 🟢️ | [qlever](http://localhost:7019) | qlever-omnigraph | [Q111016295](https://www.wikidata.org/wiki/Q111016295) | [adfreiburg/qlever:latest](https://hub.docker.com/r/adfreiburg/qlever) | 7019 | 7819 | olympics | |
80
+ | 🟢️ | [virtuoso](http://localhost:8890) | virtuoso-omnigraph | [Q7935239](https://www.wikidata.org/wiki/Q7935239) | [openlink/virtuoso-opensource-7:latest](https://hub.docker.com/r/openlink/virtuoso-opensource-7) | 8890 | 8390 | KB | dba |
81
+ | 🛑 | [stardog](http://localhost:5820) | stardog-omnigraph | [Q91147741](https://www.wikidata.org/wiki/Q91147741) | [stardog/stardog:latest](https://hub.docker.com/r/stardog/stardog) | 5820 | 5320 | mydb | admin |
67
82
 
68
83
  ### Server Management
69
84
 
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -0,0 +1,176 @@
1
+ """
2
+ Created on 2026-08-05
3
+
4
+ docker compose generation from the server configurations - see issue #29
5
+
6
+ @author: wf
7
+ """
8
+
9
+ import shlex
10
+ from dataclasses import dataclass, field
11
+ from typing import Dict, List, Optional
12
+
13
+ import yaml
14
+
15
+ from omnigraph.server_config import ServerConfig
16
+
17
+
18
+ @dataclass
19
+ class ComposeService:
20
+ """
21
+ one service of a docker compose file
22
+ """
23
+
24
+ image: str
25
+ container_name: str
26
+ ports: List[str] = field(default_factory=list)
27
+ volumes: List[str] = field(default_factory=list)
28
+ environment: List[str] = field(default_factory=list)
29
+ command: Optional[str] = None
30
+ platform: Optional[str] = None
31
+ user: Optional[str] = None
32
+ shm_size: Optional[str] = None
33
+
34
+ def as_dict(self) -> Dict:
35
+ """
36
+ Get the compose representation.
37
+
38
+ Returns:
39
+ dict of the compose keys that are set
40
+ """
41
+ service = {"image": self.image, "container_name": self.container_name}
42
+ if self.platform:
43
+ service["platform"] = self.platform
44
+ if self.user:
45
+ service["user"] = self.user
46
+ if self.shm_size:
47
+ service["shm_size"] = self.shm_size
48
+ if self.ports:
49
+ service["ports"] = self.ports
50
+ if self.volumes:
51
+ service["volumes"] = self.volumes
52
+ if self.environment:
53
+ service["environment"] = self.environment
54
+ if self.command:
55
+ service["command"] = self.command
56
+ service["restart"] = "unless-stopped"
57
+ return service
58
+
59
+
60
+ class ComposeGenerator:
61
+ """
62
+ Generate a docker compose file from the docker run commands of the server
63
+ configurations, so that the compose file and the CLI cannot drift apart.
64
+ """
65
+
66
+ def __init__(self, project_name: str = "omnigraph"):
67
+ """
68
+ Initialize the generator.
69
+
70
+ Args:
71
+ project_name: name of the compose project
72
+ """
73
+ self.project_name = project_name
74
+
75
+ def service_of_run_command(self, run_command: str) -> ComposeService:
76
+ """
77
+ Translate a docker run command into a compose service.
78
+
79
+ Args:
80
+ run_command: the complete docker run command
81
+
82
+ Returns:
83
+ the ComposeService or None if the command can not be translated
84
+ """
85
+ service = None
86
+ tokens = shlex.split(run_command)
87
+ if len(tokens) > 2 and tokens[0] == "docker" and tokens[1] == "run":
88
+ tokens = tokens[2:]
89
+ ports = []
90
+ volumes = []
91
+ environment = []
92
+ platform = None
93
+ user = None
94
+ shm_size = None
95
+ image = None
96
+ container_name = None
97
+ command_parts = []
98
+ index = 0
99
+ while index < len(tokens):
100
+ token = tokens[index]
101
+ if image is not None:
102
+ command_parts.append(token)
103
+ elif token in ("-d", "--rm", "--init"):
104
+ pass
105
+ elif token == "--name":
106
+ index += 1
107
+ container_name = tokens[index]
108
+ elif token in ("-e", "--env"):
109
+ index += 1
110
+ environment.append(tokens[index])
111
+ elif token in ("-p", "--publish"):
112
+ index += 1
113
+ ports.append(tokens[index])
114
+ elif token in ("-v", "--volume"):
115
+ index += 1
116
+ volumes.append(tokens[index])
117
+ elif token == "--platform":
118
+ index += 1
119
+ platform = tokens[index]
120
+ elif token in ("-u", "--user"):
121
+ index += 1
122
+ user = tokens[index]
123
+ elif token == "--shm-size":
124
+ index += 1
125
+ shm_size = tokens[index]
126
+ elif token.startswith("-"):
127
+ # unknown flag with a value, e.g. --restart=x is self contained
128
+ if "=" not in token and index + 1 < len(tokens) and not tokens[index + 1].startswith("-"):
129
+ index += 1
130
+ else:
131
+ image = token
132
+ index += 1
133
+ if image and container_name:
134
+ service = ComposeService(
135
+ image=image,
136
+ container_name=container_name,
137
+ ports=ports,
138
+ volumes=volumes,
139
+ environment=environment,
140
+ command=" ".join(command_parts) if command_parts else None,
141
+ platform=platform,
142
+ user=user,
143
+ shm_size=shm_size,
144
+ )
145
+ return service
146
+
147
+ def as_compose(self, configs: Dict[str, ServerConfig], data_dir_of) -> str:
148
+ """
149
+ Generate the compose file for the given server configurations.
150
+
151
+ Args:
152
+ configs: server name to ServerConfig
153
+ data_dir_of: callable returning the data directory of a config
154
+
155
+ Returns:
156
+ the compose file as yaml
157
+ """
158
+ services = {}
159
+ skipped = []
160
+ for name, config in configs.items():
161
+ run_command = None
162
+ try:
163
+ run_command = config.get_docker_run_command(data_dir=data_dir_of(config))
164
+ except Exception:
165
+ run_command = None
166
+ service = self.service_of_run_command(run_command) if run_command else None
167
+ if service:
168
+ services[name] = service.as_dict()
169
+ else:
170
+ skipped.append(name)
171
+ compose = {"name": self.project_name, "services": services}
172
+ markup = yaml.dump(compose, sort_keys=False, default_flow_style=False)
173
+ if skipped:
174
+ names = ", ".join(sorted(skipped))
175
+ markup += f"\n# not expressible as a compose service: {names}\n"
176
+ return markup
@@ -11,6 +11,7 @@ from typing import Dict, List
11
11
  from lodstorage.prefix_config import PrefixConfigs
12
12
 
13
13
  from omnigraph.basecmd import BaseCmd
14
+ from omnigraph.compose import ComposeGenerator
14
15
  from omnigraph.ominigraph_paths import OmnigraphPaths
15
16
  from omnigraph.omniserver import OmniServer
16
17
  from omnigraph.rdf_dataset import RdfDataset
@@ -71,6 +72,11 @@ class OmnigraphCmd(BaseCmd):
71
72
  parser.add_argument(
72
73
  "-l", "--list-servers", action="store_true", help="List available servers [default: %(default)s]"
73
74
  )
75
+ parser.add_argument(
76
+ "--compose",
77
+ action="store_true",
78
+ help="generate a docker compose file for the servers [default: %(default)s]",
79
+ )
74
80
  parser.add_argument(
75
81
  "--test",
76
82
  action="store_true",
@@ -98,7 +104,16 @@ class OmnigraphCmd(BaseCmd):
98
104
  servers = {}
99
105
  server_names = self.args.servers
100
106
  if "all" in server_names:
101
- server_names = list(self.all_servers.keys())
107
+ # all means all that can run on this machine - see #61; an explicitly
108
+ # named server is still tried and reports what it lacks
109
+ server_names = []
110
+ for name, candidate in self.all_servers.items():
111
+ support = candidate.config.support_status
112
+ if support.can_start():
113
+ server_names.append(name)
114
+ else:
115
+ unmet = ", ".join(candidate.config.unmet_prerequisites()) or support.value
116
+ self.log.log("🛑", "omnigraph", f"skipping {name}: {unmet}")
102
117
  for server_name in server_names:
103
118
  server = self.all_servers.get(server_name)
104
119
  if server:
@@ -220,6 +235,18 @@ class OmnigraphCmd(BaseCmd):
220
235
  config = server.config
221
236
  print(config.to_apache_config(version=self.version, domain=self.args.apache))
222
237
 
238
+ if self.args.compose:
239
+ compose_generator = ComposeGenerator()
240
+ # the whole stack, not the -s selection, with a data directory derived
241
+ # per server - relying on the side effects of server selection left a
242
+ # None volume in the file, found trying it out on fur
243
+ configs = {name: server.config for name, server in self.all_servers.items()}
244
+ markup = compose_generator.as_compose(
245
+ configs,
246
+ lambda config: config.base_data_dir or str(self.ogp.omnigraph_dir / config.name / "data"),
247
+ )
248
+ print(markup)
249
+
223
250
  if self.args.list_servers:
224
251
  table_format = self.args.doc_format if self.args.doc_format != "plain" else "simple"
225
252
  markup = self.omni_server.list_servers(self.all_servers, table_format)
@@ -12,7 +12,8 @@ from lodstorage.prefix_config import PrefixConfigs
12
12
  from tabulate import tabulate
13
13
 
14
14
  from omnigraph.ominigraph_paths import OmnigraphPaths
15
- from omnigraph.server_config import ServerCmd, ServerConfig, ServerConfigs, ServerEnv
15
+ from omnigraph.server_config import LoadPath, ServerCmd, ServerConfig, ServerConfigs, ServerEnv
16
+ from omnigraph.servers.allegrograph import AllegroGraph, AllegroGraphConfig
16
17
  from omnigraph.servers.blazegraph import Blazegraph, BlazegraphConfig
17
18
  from omnigraph.servers.graphdb import GraphDB, GraphDBConfig
18
19
  from omnigraph.servers.jena import Jena, JenaConfig
@@ -64,6 +65,18 @@ class OmniServer:
64
65
  "count": lambda s: ServerCmd(title("triple count", s), s.count_triples),
65
66
  "info": lambda s: ServerCmd(title("info", s), s.docker_info),
66
67
  "load": lambda s: ServerCmd(title("load dumps", s), s.load_dump_files),
68
+ "liveload": lambda s: ServerCmd(
69
+ title("liveload dumps into the running server", s),
70
+ lambda: s.load_dump_files(path=LoadPath.LIVELOAD),
71
+ ),
72
+ "bulkload": lambda s: ServerCmd(
73
+ title("bulkload dumps with the native loader", s),
74
+ lambda: s.load_dump_files(path=LoadPath.BULKLOAD),
75
+ ),
76
+ "build": lambda s: ServerCmd(
77
+ title("build the store from the dumps", s),
78
+ lambda: s.load_dump_files(path=LoadPath.BUILD),
79
+ ),
67
80
  "logs": lambda s: ServerCmd(title("logs of", s), s.logs),
68
81
  "needed": lambda s: ServerCmd(title("check needed software for", s), s.check_needed_software),
69
82
  "rm": lambda s: ServerCmd(title("remove", s), s.rm),
@@ -90,6 +103,7 @@ class OmniServer:
90
103
  config_dict = asdict(config)
91
104
 
92
105
  server_mappings = {
106
+ "allegrograph": (AllegroGraphConfig, AllegroGraph),
93
107
  "blazegraph": (BlazegraphConfig, Blazegraph),
94
108
  "graphdb": (GraphDBConfig, GraphDB),
95
109
  "jena": (JenaConfig, Jena),
@@ -161,7 +175,9 @@ class OmniServer:
161
175
  else:
162
176
  return text
163
177
 
164
- for server in servers.values():
178
+ # active servers first, then alphabetically - the config order is not meaningful
179
+ sorted_servers = sorted(servers.values(), key=lambda s: (not s.config.active, s.name))
180
+ for server in sorted_servers:
165
181
  active_str = server.flag
166
182
 
167
183
  wikidata_id = getattr(server.config, "wikidata_id", "")
@@ -218,24 +234,29 @@ class OmniServer:
218
234
  auth = "\n auth: BASIC" if server.config.auth_user else ""
219
235
  user = f"\n user: {server.config.auth_user}" if server.config.auth_user else ""
220
236
  passwd = f"\n passwd: {server.config.auth_password}" if server.config.auth_password else ""
221
- # Indent prefixes for literal block scalar (4 spaces)
222
- indented_prefixes = "\n".join(f" {line}" for line in prefixes_text.split("\n") if line.strip())
223
-
224
- entry = f"""{server.name}:
225
- method: {getattr(server.config, 'method', 'POST')}
226
- lang: sparql
227
- name: {server.name}
228
- endpoint: {server.config.sparql_url}
229
- website: {server.config.base_url}
230
- database: {server.config.server}{auth}{user}{passwd}
231
- prefixes: |
237
+ # Indent prefixes for literal block scalar (6 spaces below the endpoints: wrapper)
238
+ indented_prefixes = "\n".join(f" {line}" for line in prefixes_text.split("\n") if line.strip())
239
+ # optional elements indented for the endpoints: wrapper
240
+ auth = auth.replace("\n ", "\n ")
241
+ user = user.replace("\n ", "\n ")
242
+ passwd = passwd.replace("\n ", "\n ")
243
+
244
+ # entry below the endpoints: wrapper lodstorage EndpointManager expects - see issue #39
245
+ entry = f""" {server.name}:
246
+ method: {getattr(server.config, 'method', 'POST')}
247
+ lang: sparql
248
+ name: {server.name}
249
+ endpoint: {server.config.sparql_url}
250
+ website: {server.config.base_url}
251
+ database: {server.config.server}{auth}{user}{passwd}
252
+ prefixes: |
232
253
  {indented_prefixes}"""
233
254
 
234
255
  yaml_entries.append(entry)
235
256
 
236
257
  yaml_header = "# SPARQL endpoints for snapquery, sparqlquery and omnigraph tools\n"
237
258
  yaml_header += server.config.generator_header() + "\n"
238
- yaml_content = yaml_header + "\n".join(yaml_entries)
259
+ yaml_content = yaml_header + "endpoints:\n" + "\n".join(yaml_entries)
239
260
 
240
261
  if output_path:
241
262
  with open(output_path, "w") as f: