iolanta 2.0.5__py3-none-any.whl → 2.0.7__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iolanta
3
- Version: 2.0.5
3
+ Version: 2.0.7
4
4
  Summary: Semantic Web browser
5
5
  License: MIT
6
6
  Author: Anatoly Scherbakov
@@ -32,7 +32,7 @@ Requires-Dist: rich (>=13.3.1)
32
32
  Requires-Dist: textual (>=0.83.0)
33
33
  Requires-Dist: typer (>=0.9.0)
34
34
  Requires-Dist: watchfiles (>=1.0.4,<2.0.0)
35
- Requires-Dist: yaml-ld (>=1.1.7)
35
+ Requires-Dist: yaml-ld (>=1.1.9)
36
36
  Requires-Dist: yarl (>=1.9.4)
37
37
  Description-Content-Type: text/markdown
38
38
 
@@ -1,4 +1,4 @@
1
- iolanta/__init__.py,sha256=BvLP-LYFmNYS5F8VPXdzpEEMk8zcm9NMmlUWw9yEtj0,153
1
+ iolanta/__init__.py,sha256=d6a_MUaocIgrpgC49Bu5N3DHLP0uBc07CLTjPRpKArI,111
2
2
  iolanta/base_plugin.py,sha256=vI4DRSIITlKZw8x7Q58BFkChWlg1h8zV-tuNvIKURBI,86
3
3
  iolanta/cli/__init__.py,sha256=IV6_RPmrbpPWbdKojuFczwaJAaKm1DIDQh5aZWbeR1U,69
4
4
  iolanta/cli/formatters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -6,7 +6,7 @@ iolanta/cli/formatters/choose.py,sha256=LWzsO_9IBSSgYNIyLlItkp8TNvpW3v6YCQ8-6kbI
6
6
  iolanta/cli/formatters/csv.py,sha256=ceJ_DTz0beqeK-d6FPBQqqjXrziEfF0FRSLoGZCt_fs,760
7
7
  iolanta/cli/formatters/json.py,sha256=Og5B9UrSM_0NWqW5Afpsy6WH8ZfYgPMVXYvT3i-43Jc,748
8
8
  iolanta/cli/formatters/pretty.py,sha256=IypZRAr2vNqcXFY6NOIc75mpyfpFWh56aCBlOPDDieQ,2901
9
- iolanta/cli/main.py,sha256=exDWPljllufn7DiGC9lS1pvojFX9YZJciQ2RPCedKMU,3127
9
+ iolanta/cli/main.py,sha256=W1SoQK9kncaSUsy4X5yMrzu8lQUlu-eE6YyIu4dNnec,3167
10
10
  iolanta/cli/models.py,sha256=cjbpowdzI4wAP0DUk3qoVHyimk6AZwlXi9CGmusZTuM,159
11
11
  iolanta/cli/pretty_print.py,sha256=M6E3TmhzA6JY5GeUVmDZLmOh5u70-393PVit4voFKDI,977
12
12
  iolanta/context.py,sha256=bZR-tbZIrDQ-Vby01PMDZ6ifxM-0YMK68RJvAsyqCTs,507
@@ -16,7 +16,7 @@ iolanta/data/context.yaml,sha256=LUBasiBKgQeGAYjYV_T5XvgPlrdzACeKaZwY_rKzjtI,163
16
16
  iolanta/data/graph-triples.yamlld,sha256=oACKRwqWcMp6TBobXMG6gbbnaauyd6wlK-gH2vVkPps,241
17
17
  iolanta/data/html.yaml,sha256=hVFdLWLy8FMY8xpOrJMYc-tE3S0Nq83xuxVkjRW_7rI,517
18
18
  iolanta/data/iolanta.yaml,sha256=xubIFBNU02lmFXhgOSuyQwUcZD3xCqVfeVAZMvOxKbI,1433
19
- iolanta/data/textual-browser.yaml,sha256=hWN-EtLCoA8FCuihZWIAvLktara6O6BB7_E_bNukEhk,3947
19
+ iolanta/data/textual-browser.yaml,sha256=SajFDOdqISVLsBVneS3uh3s8vuYAUIuC0nWvVqIWMZ0,4141
20
20
  iolanta/ensure_is_context.py,sha256=9aok8asyEx7KPesOR28VBDb3Ch9kfc3eoCpQSJwj07U,717
21
21
  iolanta/entry_points.py,sha256=DZbf-udlEwELFGqeWENj0M2BOUPOWlmGJdqyaEtnot0,504
22
22
  iolanta/errors.py,sha256=t_RltahnoEvcytVa1BOq2MADgHps3JNd_h5-SFu7_wM,1250
@@ -28,7 +28,7 @@ iolanta/facets/cli/record.py,sha256=lBsECxLkwVSXC-yHmUwfosxAdLBI-0UoqHe8GOCablY,
28
28
  iolanta/facets/cli/sparql/link.sparql,sha256=WtWEfLAvdGc2gP0IhZil6Vglkydc3VO24vk4GwRnR5I,163
29
29
  iolanta/facets/cli/sparql/record.sparql,sha256=GBWkmNelvaSDbvl7v0-LsJHdjFPbsSAW49kNFOUeoGQ,63
30
30
  iolanta/facets/errors.py,sha256=sEBmnzhFcRIgOT18hfNwyMMjry0waa6IB-jC2NVTA50,4124
31
- iolanta/facets/facet.py,sha256=QByUVrvt_XVBoVhfuyk6lKO--pauhMEN79GWmgejdIo,2822
31
+ iolanta/facets/facet.py,sha256=gAOkosh9B_Lufh3ia9W24hgt4LEIhG-UJcQF4BzMvno,2600
32
32
  iolanta/facets/foaf_person_title/__init__.py,sha256=oj4MNVQvv8Dysb27xiWjtZCii8-nT7-WFa3WMWUwbtU,67
33
33
  iolanta/facets/foaf_person_title/facet.py,sha256=Q9TajjGp1v729quDzAM_Lfzawls3yujp1Z_6jXrG3gY,632
34
34
  iolanta/facets/foaf_person_title/sparql/names.sparql,sha256=p_2hHZXhEaJ8IwGlvLoN0vb0vhGqo44uAVRpDyTzflU,107
@@ -42,7 +42,7 @@ iolanta/facets/html/base.py,sha256=JcpK7YM_QQE9vwH5w5F_EgPkPv-XRzOrcZlVSy4LhIs,1
42
42
  iolanta/facets/html/code_literal.py,sha256=qCddzBrg6Y5XMIKohFQ52Tf9GPOcU7bj83tfAU1FLLU,394
43
43
  iolanta/facets/html/default.py,sha256=Dmf_kYiL2M954iigyYsiWrMstwZ1nvxKetuR6aW3xYY,647
44
44
  iolanta/facets/icon.py,sha256=ddZcolx1Q5_wo3w0jqiCzcc5Lsru6-jA7s7oAd1T8Og,494
45
- iolanta/facets/locator.py,sha256=oiktgFGOkhVNDh4KzFY1Os--9-MSY32NYlGr-OXXxqY,8561
45
+ iolanta/facets/locator.py,sha256=b9TEK5_5SklTfEc6lUt7DJt1mKXsiL_urA2zxMk59EY,9152
46
46
  iolanta/facets/page_title.py,sha256=TwgZK2g_e5UoWYjKNgDzzkmq1EI3cY58680iC8N9kZI,1407
47
47
  iolanta/facets/qname.py,sha256=ztyBbjjcW8dNZzuiNMqhcWfAUxk-gSjbseVGrQE7kVY,531
48
48
  iolanta/facets/textual_browser/__init__.py,sha256=sKgDvXOwib9n9d63kdtKCEv26-FoL0VN6zxDmfcheZ8,104
@@ -79,6 +79,7 @@ iolanta/facets/textual_nanopublication/models.py,sha256=MphggSvKmYac6v866GOKW5lc
79
79
  iolanta/facets/textual_nanopublication/nanopublication_widget.py,sha256=sQPgEx25XJ15VqB5zjZuz2q2bf_B5GWXEp32FAufxQ4,2387
80
80
  iolanta/facets/textual_nanopublication/term_list_widget.py,sha256=NZNATKjFmgI2sE5R0ebyQh5Qk2g-kf-MT4YZ-Vw78M4,992
81
81
  iolanta/facets/textual_nanopublication/term_widget.py,sha256=uI5msCTSIYumAIS3I8nBfUz57_vRzvCKkouevrS3Qwk,3536
82
+ iolanta/facets/textual_no_facet_found.py,sha256=xNAQslZuMU-HLZh6250Siz3EnSE_ZVt-YhksUTiU_Ag,2485
82
83
  iolanta/facets/textual_ontology/__init__.py,sha256=3H6bfYaEbXFr90C6ZpGWC4O-24uaO18tnTXx7mckQSA,94
83
84
  iolanta/facets/textual_ontology/facets.py,sha256=60g8ANmePb9_i_-d4ui-FdtNwg9aktrOKXHkTQtLp1w,3338
84
85
  iolanta/facets/textual_ontology/sparql/terms.sparql,sha256=oVLxN452nqog_95qRaTWnvar6rxPNxPrRonSo7oFFTg,324
@@ -94,43 +95,27 @@ iolanta/facets/title/sparql/title.sparql,sha256=4rz47tjwX2OJavWMzftaYKil1-ZHB76Z
94
95
  iolanta/facets/wikibase_statement_title/__init__.py,sha256=_yk1akxgSJOiUBJIc8QGrD2vovvmx_iw_vJDuv1rD7M,91
95
96
  iolanta/facets/wikibase_statement_title/facets.py,sha256=mUH7twlAgoeX7DgLQuRBQv4ORT6GWbN-0eJ1aliSfiQ,724
96
97
  iolanta/facets/wikibase_statement_title/sparql/statement-title.sparql,sha256=n07DQWxKqB5c3CA4kacq2HSN0R0dLgnMnLP1AxMo5YA,320
97
- iolanta/iolanta.py,sha256=wCt_7aUz9TOvZTJvpJQ-qVWkOpmM531uyxIk9l3n-lk,14229
98
- iolanta/loaders/__init__.py,sha256=QTiKCsQc1BTS-IlY2CQsN9iVpEIPqYFvI9ERMYVZCbU,99
99
- iolanta/loaders/base.py,sha256=-DxYwqG1bfDXB2p_S-mKpkc_3Sh14OHhePbe65Iq3-s,3381
100
- iolanta/loaders/data_type_choice.py,sha256=zRUXBIzjvuW28P_dhMDVevE9C8EFEIx2_X39WydWrtM,1982
101
- iolanta/loaders/dict_loader.py,sha256=8eklbLFsNoQNuR7UDXY6oMXH2nekaK1WQLb42MjjYuk,1696
102
- iolanta/loaders/errors.py,sha256=EfalDnabuC2YjmJUOhTfs-5yeskVclhYcSoMuxJCRL4,472
103
- iolanta/loaders/http.py,sha256=mJMmprcl2X7a8p1YjfuWcOaJQm3xSM00E9sfDK_PMNo,3654
104
- iolanta/loaders/local_directory.py,sha256=tYOmtKkct8lcWM_dqm75932mPyc6ulMc4_9mseWCd9I,4557
105
- iolanta/loaders/local_file.py,sha256=MCbcu7DtiuoJye1xvTfuts-b2KCYjfg4tAYRZqgFelo,3064
106
- iolanta/loaders/scheme_choice.py,sha256=GHA4JAD-Qq3uNdej4vs_bCrN1WMRshVRvOMxaYyPHsE,2104
107
- iolanta/models.py,sha256=L0iFaga4-PCaWP18WmT03NLK_oT7q49V0WMTQskoffI,2824
108
- iolanta/namespaces.py,sha256=fyWDCGPwU8Cs8d-Vmci4H0-fuIe7BMSevvEKFvG7Gf4,1153
98
+ iolanta/iolanta.py,sha256=y95nu0BbllQqWIBv19FUAbS-l4-qNFA4LWcbZdseTyU,11330
99
+ iolanta/models.py,sha256=M-1dTxPwjTyUgQ4VCOiH6Q7ltNJiDPG0l1XQH430Omo,3250
100
+ iolanta/namespaces.py,sha256=VJ3BLTji3UmqD3N1Om2voAoyYPtF4o-8FXN2Uq_4sPc,1194
109
101
  iolanta/node_to_qname.py,sha256=a82_qpgT87cbekY_76tTkl4Z-6Rz6am4UGIQChUf9Y0,794
110
- iolanta/parse_quads.py,sha256=2UydATeDassOrpjWfjwWnMjnSS5RZw5KBFIeKHGJ36M,2660
111
- iolanta/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
- iolanta/parsers/base.py,sha256=ZFjj0BLzW4985BdC6PwbngenhMuSYW5mNLpprZRWjhA,1048
113
- iolanta/parsers/dict_parser.py,sha256=t_OK2meUh49DqSaOYkSgEwxMKUNNgjJY8rZAyL4NQKI,4546
114
- iolanta/parsers/errors.py,sha256=4Tqi9WD8-AmnZ2efHGty9VSLb--Efd4A6tMqGF3ffDY,600
115
- iolanta/parsers/json.py,sha256=3VrDiz-SaVXP9_B03Gl62VSIk2fKeRrrb2hpTLxqBk4,967
116
- iolanta/parsers/markdown.py,sha256=wv-PhszgoDOzWdisykIeZyYBD1edCVmw7UQ8jEn4siw,1669
117
- iolanta/parsers/yaml.py,sha256=MeTe5_OaDK27XB38AzxjqWfCxybAm4tqgqn7LLh-YB0,1244
102
+ iolanta/parse_quads.py,sha256=w1GlfABG6EYKQ9iFFQHKFp8juPGdsjYmadQE6hH0gC8,4499
118
103
  iolanta/plugin.py,sha256=MSxpuOIx93AgBahfS8bYh31MEgcwtUSQhj4Js7fgdSI,1096
119
104
  iolanta/query_result.py,sha256=VLLBkewUEymtzfB0jeIeRE3Np6pAgo959RPgNsEmiq8,1545
120
105
  iolanta/reformat_blank_nodes.py,sha256=MAVcXusUioKzAoTEHAMume5Gt9vBEpxJGrngqFzmkJI,712
121
106
  iolanta/resolvers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
107
  iolanta/resolvers/base.py,sha256=cc9bcrVZ0wTwn85I-IYCwcIRU5Lwaph8D00C0dwSOm8,302
123
108
  iolanta/resolvers/python_import.py,sha256=kDOhApBDFfxnrDgK9M7ztMkqXfcny81Zo86FgPFb-LI,1301
124
- iolanta/shortcuts.py,sha256=j8b0E_yeoas8GumsPOfxO2v2jO0noqpmKJ6LFxFfsu4,1892
125
109
  iolanta/sparqlspace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
110
  iolanta/sparqlspace/cli.py,sha256=pb6q03lzrU8OaZ4A3QAQEmaFYcQ_-sHUrPhRs6GE88w,1590
127
111
  iolanta/sparqlspace/inference/wikibase-claim.sparql,sha256=JSawj3VTc9ZPoU9mvh1w1AMYb3cWyZ3x1rEYWUsKZ6A,209
128
112
  iolanta/sparqlspace/inference/wikibase-statement-property.sparql,sha256=SkSHZZlxWVDwBM3aLo0Q7hLuOj9BsIQnXtcuAuwaxqU,240
129
- iolanta/sparqlspace/processor.py,sha256=CXxdi7ORpddXxPh0UlnRG7oqNT9q-WdSPMxqB4CgOis,23570
113
+ iolanta/sparqlspace/processor.py,sha256=tYIGIqh-1kbzIfYgirU77QoyFwsF8t6jv5BdHAEXjM0,23191
130
114
  iolanta/sparqlspace/sparqlspace.py,sha256=Y8_ZPXwuGEXbEes6XQjaQWA2Zv9y8SWxMPDFdqVBGFo,796
131
115
  iolanta/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
+ iolanta/widgets/description.py,sha256=98Qd3FwT9r8sYqKjl9ZEptaVX9jJ2ULWf0uy3j52p5o,800
132
117
  iolanta/widgets/mixin.py,sha256=nDRCOc-gizCf1a5DAcYs4hW8eZEd6pHBPFsfm0ncv7E,251
133
- iolanta-2.0.5.dist-info/METADATA,sha256=YSkBOGK0TGD_gdzzJKXxbm52W-4qtlVFpAumPPweabE,2230
134
- iolanta-2.0.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
135
- iolanta-2.0.5.dist-info/entry_points.txt,sha256=z9RPLmGuz3tYGPV7Qf4nNjuSJYdTUS9enC4595poe-M,221
136
- iolanta-2.0.5.dist-info/RECORD,,
118
+ iolanta-2.0.7.dist-info/METADATA,sha256=CjOyQqdHPRnClEWEC0nllINZGGdUznLk_C04P0_B5aI,2230
119
+ iolanta-2.0.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
120
+ iolanta-2.0.7.dist-info/entry_points.txt,sha256=z9RPLmGuz3tYGPV7Qf4nNjuSJYdTUS9enC4595poe-M,221
121
+ iolanta-2.0.7.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- from iolanta.loaders.base import Loader
2
- from iolanta.loaders.local_directory import LocalDirectory
iolanta/loaders/base.py DELETED
@@ -1,124 +0,0 @@
1
- from abc import ABC
2
- from dataclasses import dataclass
3
- from logging import Logger
4
- from typing import (
5
- Any,
6
- Dict,
7
- Generic,
8
- Iterable,
9
- Optional,
10
- TextIO,
11
- TypedDict,
12
- TypeVar,
13
- )
14
-
15
- from rdflib import URIRef
16
- from yarl import URL
17
-
18
- from iolanta.conversions import url_to_iri
19
- from iolanta.ensure_is_context import ensure_is_context
20
- from iolanta.models import LDContext, LDDocument, Quad
21
- from iolanta.namespaces import PYTHON
22
-
23
- SourceType = TypeVar('SourceType')
24
-
25
-
26
- PyLDOptions = Dict[str, Any] # type: ignore
27
-
28
- PyLDResponse = TypedDict(
29
- 'PyLDResponse', {
30
- 'contentType': str,
31
- 'contextUrl': Optional[str],
32
- 'documentUrl': str,
33
- 'document': LDDocument,
34
- },
35
- )
36
-
37
-
38
- def term_for_python_class(cls: type) -> URIRef:
39
- """Construct term for Python class."""
40
- return PYTHON.term(f'{cls.__module__}.{cls.__qualname__}')
41
-
42
-
43
- # noinspection TaskProblemsInspection
44
- @dataclass(frozen=True)
45
- class Loader(ABC, Generic[SourceType]):
46
- """
47
- Base class for loaders.
48
-
49
- Loader receives a URL (or a path) to certain location. It is responsible for
50
- reading data from that location and returning it as a stream of RDF quads.
51
-
52
- Usually, depending on the data format, Loader leverages Parsers for that
53
- purpose.
54
- """
55
-
56
- logger: Logger
57
-
58
- @classmethod
59
- def loader_class_iri(cls) -> URIRef:
60
- """Import path to the loader class."""
61
- return term_for_python_class(cls)
62
-
63
- def choose_parser_class(self, source: SourceType):
64
- """Find which parser class to use for this URL."""
65
- raise NotImplementedError(
66
- f'{self}.choose_parser_class() is not implemented.',
67
- )
68
-
69
- def as_jsonld_document(
70
- self,
71
- source: SourceType,
72
- iri: Optional[URIRef] = None,
73
- ) -> LDDocument:
74
- """Represent a file as a JSON-LD document."""
75
- raise NotImplementedError(
76
- f'{self}.as_jsonld_document() is not implemented.',
77
- )
78
-
79
- def as_file(self, source: SourceType) -> TextIO:
80
- """Construct a file-like object."""
81
- raise NotImplementedError()
82
-
83
- def as_quad_stream(
84
- self,
85
- source: SourceType,
86
- iri: Optional[URIRef],
87
- root_loader: 'Loader[SourceType]',
88
- context: Optional[LDContext] = None,
89
- ) -> Iterable[Quad]:
90
- """Convert data into a stream of RDF quads."""
91
- raise NotImplementedError(
92
- f'{self}.as_quad_stream() is not implemented.',
93
- )
94
-
95
- def find_context(self, source: SourceType) -> LDContext:
96
- """Find context for the file."""
97
- raise NotImplementedError(
98
- f'{self}.find_context() is not implemented.',
99
- )
100
-
101
- def __call__(self, source: str, options: PyLDOptions) -> PyLDResponse:
102
- """
103
- Call the loader to retrieve the document in a PYLD friendly format.
104
-
105
- Used to resolve remote contexts.
106
-
107
- The type of `source` parameter is intentionally `str`: that's the only
108
- thing which pyld can do.
109
- """
110
- source = URL(source)
111
-
112
- document = ensure_is_context(
113
- self.as_jsonld_document(
114
- source=URL(source),
115
- iri=url_to_iri(source),
116
- ),
117
- )
118
-
119
- return {
120
- 'document': document,
121
- 'contextUrl': None,
122
- 'documentUrl': source,
123
- 'contentType': 'application/ld+json',
124
- }
@@ -1,66 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Any, Dict, Iterable, Optional, TextIO
3
-
4
- from rdflib import URIRef
5
- from yarl import URL
6
-
7
- from iolanta.loaders.base import Loader, SourceType
8
- from iolanta.models import LDContext, LDDocument, Quad
9
-
10
-
11
- @dataclass(frozen=True)
12
- class DataTypeChoiceLoader(Loader[Any]): # type: ignore
13
- """Try to load a file via several loaders."""
14
-
15
- loader_by_data_type: Dict[type, Loader[Any]] # type: ignore
16
-
17
- def choose_parser_class(self, source: SourceType):
18
- raise ValueError('choose_parser_class')
19
-
20
- def as_file(self, source: SourceType) -> TextIO:
21
- raise ValueError('as_file')
22
-
23
- def find_context(self, source: SourceType) -> LDContext:
24
- raise ValueError('find_context')
25
-
26
- def resolve_loader(self, source: Any): # type: ignore
27
- """Find loader instance by URL."""
28
- for source_type, loader in self.loader_by_data_type.items():
29
- if isinstance(source, source_type):
30
- return loader
31
-
32
- source_type = type(source)
33
- raise ValueError(
34
- f'Cannot find a loader for source: {source} '
35
- f'of type: {source_type}',
36
- )
37
-
38
- def as_jsonld_document(
39
- self,
40
- source: URL,
41
- iri: Optional[URIRef] = None,
42
- ) -> LDDocument:
43
- """Represent a file as a JSON-LD document."""
44
- return self.resolve_loader(
45
- source=source,
46
- ).as_jsonld_document(
47
- source=source,
48
- iri=iri,
49
- )
50
-
51
- def as_quad_stream(
52
- self,
53
- source: str,
54
- iri: Optional[URIRef],
55
- root_loader: Optional[Loader[URL]] = None,
56
- context: Optional[LDContext] = None,
57
- ) -> Iterable[Quad]:
58
- """Convert data into a stream of RDF quads."""
59
- return self.resolve_loader(
60
- source=source,
61
- ).as_quad_stream(
62
- source=source,
63
- iri=iri,
64
- root_loader=root_loader or self,
65
- context=context,
66
- )
@@ -1,57 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Iterable, Optional, TextIO, Type
3
-
4
- from rdflib import Literal, URIRef
5
-
6
- from iolanta.conversions import url_to_iri
7
- from iolanta.loaders.base import Loader
8
- from iolanta.loaders.errors import IsAContext, ParserNotFound
9
- from iolanta.models import LDContext, LDDocument, Quad
10
- from iolanta.namespaces import IOLANTA
11
- from iolanta.parsers.base import Parser
12
- from iolanta.parsers.dict_parser import DictParser
13
- from iolanta.parsers.json import JSON
14
- from iolanta.parsers.markdown import Markdown
15
- from iolanta.parsers.yaml import YAML
16
-
17
-
18
- @dataclass(frozen=True)
19
- class DictLoader(Loader[LDDocument]):
20
- """
21
- Retrieve Linked Data from a file on local disk.
22
-
23
- Requires a dict of raw JSON-LD data.
24
- """
25
-
26
- def find_context(self, source: str) -> LDContext:
27
- raise ValueError('???WTF?')
28
-
29
- def choose_parser_class(self, source: LDDocument) -> Type[Parser]:
30
- return DictParser(source)
31
-
32
- def as_quad_stream(
33
- self,
34
- source: LDDocument,
35
- root_loader: Loader[LDDocument],
36
- iri: Optional[URIRef] = None,
37
- context: Optional[LDContext] = None,
38
- ) -> Iterable[Quad]:
39
- """Extract a sequence of quads."""
40
- yield from DictParser().as_quad_stream(
41
- raw_data=source,
42
- iri=iri,
43
- context=context,
44
- root_loader=root_loader,
45
- )
46
-
47
- def as_file(self, source: LDDocument) -> TextIO:
48
- """Construct a file-like object."""
49
- raise ValueError('FOO')
50
-
51
- def as_jsonld_document(
52
- self,
53
- source: LDDocument,
54
- iri: Optional[URIRef] = None,
55
- ) -> LDDocument:
56
- """As JSON-LD document."""
57
- return source
iolanta/loaders/errors.py DELETED
@@ -1,29 +0,0 @@
1
- from dataclasses import dataclass
2
- from pathlib import Path
3
-
4
- from documented import DocumentedError
5
- from yarl import URL
6
-
7
-
8
- @dataclass
9
- class IsAContext(DocumentedError):
10
- """
11
- The provided file is a context.
12
-
13
- - Path: {self.path}
14
-
15
- This file is not a piece of data and cannot be loaded into the graph.
16
- """
17
-
18
- path: URL
19
-
20
-
21
- @dataclass
22
- class ParserNotFound(DocumentedError):
23
- """
24
- Parser not found.
25
-
26
- Path: {self.path}
27
- """
28
-
29
- path: Path
iolanta/loaders/http.py DELETED
@@ -1,127 +0,0 @@
1
- import json
2
- import re
3
- from dataclasses import dataclass, field
4
- from functools import reduce
5
- from io import StringIO
6
- from pathlib import Path
7
- from typing import Iterable, List, Optional, TextIO, Type, Union
8
-
9
- from documented import DocumentedError
10
- from rdflib import URIRef
11
- from rdflib.parser import URLInputSource
12
- from requests import Response
13
- from yarl import URL
14
-
15
- from iolanta.context import merge
16
- from iolanta.conversions import url_to_iri, url_to_path
17
- from iolanta.loaders.base import Loader
18
- from iolanta.loaders.errors import IsAContext, ParserNotFound
19
- from iolanta.loaders.local_file import choose_parser_by_extension
20
- from iolanta.models import LDContext, LDDocument, Quad
21
- from iolanta.parsers.base import Parser
22
- from iolanta.parsers.json import JSON
23
- from iolanta.parsers.markdown import Markdown
24
- from iolanta.parsers.yaml import YAML
25
-
26
-
27
- @dataclass(frozen=True)
28
- class HTTP(Loader[URL]):
29
- """
30
- Retrieve Linked Data from a file on the Web.
31
- """
32
-
33
- context: LDContext = field(default_factory=dict)
34
-
35
- def choose_parser_class(self, source: URL, response: Response):
36
- # FIXME hard code. Make this extensible.
37
- try:
38
- return choose_parser_by_extension(source)
39
- except ParserNotFound:
40
- content_type = response.headers['Content-Type']
41
-
42
- raise ValueError(f'Content type: {content_type}')
43
-
44
- def extract_alternate_url(
45
- self,
46
- source: URL,
47
- response: Response,
48
- ) -> URL | None:
49
- link = response.headers.get('Link')
50
-
51
- if link is None:
52
- return None
53
-
54
- match = re.match(
55
- r'<([^>]+)>; rel="alternate"; type="application/ld\+json"',
56
- link,
57
- )
58
- if match is None:
59
- return None
60
-
61
- return source / match.group(1)
62
-
63
- def as_jsonld_document(
64
- self,
65
- source: URL,
66
- iri: Optional[URIRef] = None,
67
- ) -> LDDocument:
68
- if iri is None:
69
- iri = url_to_iri(source)
70
-
71
- response = source.get()
72
- response.raise_for_status()
73
- alternate_url = self.extract_alternate_url(
74
- source=source,
75
- response=response,
76
- )
77
- if alternate_url is not None:
78
- return self.as_jsonld_document(
79
- source=alternate_url,
80
- iri=iri,
81
- )
82
-
83
- # `response.text` doesn't work.
84
- # Reasoning: https://stackoverflow.com/a/72621231/1245471
85
- response_as_file = StringIO(response.content.decode('utf-8'))
86
-
87
- parser_class: Type[Parser] = self.choose_parser_class(
88
- source=source,
89
- response=response,
90
- )
91
- try:
92
- document = parser_class().as_jsonld_document(response_as_file)
93
- except Exception:
94
- raise ValueError(response)
95
-
96
- if iri is not None and isinstance(document, dict):
97
- document.setdefault('@id', str(iri))
98
-
99
- return document
100
-
101
- def as_file(self, source: URL) -> TextIO:
102
- raise ValueError('!!!')
103
-
104
- def as_quad_stream(
105
- self,
106
- source: URL,
107
- iri: Optional[URIRef],
108
- root_loader: 'Loader[URL]',
109
- ) -> Iterable[Quad]:
110
- try:
111
- parser_class = self.choose_parser_class(source)
112
- except ParserNotFound:
113
- return []
114
-
115
- if iri is None:
116
- iri = url_to_iri(source)
117
-
118
- with source.open() as text_io:
119
- return parser_class().as_quad_stream(
120
- raw_data=text_io,
121
- iri=iri,
122
- context=self.context,
123
- root_loader=root_loader,
124
- )
125
-
126
- def find_context(self, source: str) -> LDContext:
127
- raise ValueError('??!!?')
@@ -1,148 +0,0 @@
1
- import dataclasses
2
- from dataclasses import dataclass, field
3
- from functools import reduce
4
- from pathlib import Path
5
- from typing import Iterable, List, Optional, TextIO, Type
6
-
7
- from rdflib import URIRef
8
-
9
- from iolanta.context import merge
10
- from iolanta.conversions import path_to_iri
11
- from iolanta.ensure_is_context import NotAContext, ensure_is_context
12
- from iolanta.loaders.base import Loader, SourceType
13
- from iolanta.loaders.local_file import LocalFile
14
- from iolanta.models import LDContext, LDDocument, Quad
15
- from iolanta.namespaces import IOLANTA
16
- from iolanta.parsers.base import Parser
17
-
18
-
19
- def merge_contexts(*contexts: LDContext) -> LDContext:
20
- return reduce(
21
- merge,
22
- filter(bool, contexts),
23
- {},
24
- )
25
-
26
-
27
- @dataclass(frozen=True)
28
- class LocalDirectory(Loader[Path]):
29
- """
30
- Retrieve Linked Data from a file on local disk.
31
-
32
- Requires Path with file:// scheme as input.
33
- """
34
-
35
- context_filenames: List[str] = field(
36
- default_factory=lambda: [
37
- 'context.yaml',
38
- 'context.yml',
39
- 'context.json',
40
- ],
41
- )
42
- include_hidden_directories: bool = False
43
-
44
- def find_context(self, source: SourceType) -> LDContext:
45
- raise ValueError('?!!!???')
46
-
47
- def directory_level_context(self, path: Path) -> Optional[LDContext]:
48
- for file_name in self.context_filenames:
49
- if (context_path := path / file_name).is_file():
50
- document = LocalFile(logger=self.logger).as_jsonld_document(
51
- source=context_path,
52
- )
53
-
54
- if document:
55
- try:
56
- return ensure_is_context(document)
57
- except NotAContext as err:
58
- raise dataclasses.replace(
59
- err,
60
- path=context_path,
61
- )
62
- return None
63
-
64
- def choose_parser_class(self, source: Path) -> Type[Parser]:
65
- """Choose parser class based on file extension."""
66
- raise ValueError('This is a directory')
67
-
68
- def as_quad_stream(
69
- self,
70
- source: Path,
71
- iri: Optional[URIRef],
72
- root_loader: Loader[Path],
73
- context: Optional[LDContext] = None,
74
- ) -> Iterable[Quad]:
75
- """Extract a sequence of quads from a local file."""
76
- if iri is None:
77
- iri = path_to_iri(source.absolute())
78
-
79
- if not source.is_dir():
80
- yield from LocalFile(logger=self.logger).as_quad_stream(
81
- source=source,
82
- root_loader=root_loader,
83
- iri=iri,
84
- context=context,
85
- )
86
- return
87
-
88
- context = merge_contexts(
89
- context,
90
- self.directory_level_context(source),
91
- )
92
-
93
- for child in source.iterdir():
94
- if not iri.endswith('/'):
95
- iri = URIRef(f'{iri}/')
96
-
97
- child_iri = URIRef(f'{iri}{child.name}')
98
-
99
- if child.is_dir():
100
- if (
101
- not self.include_hidden_directories
102
- and child.name.startswith('.')
103
- ):
104
- self.logger.info(
105
- 'Skipping a hidden directory: %s',
106
- child,
107
- )
108
- continue
109
-
110
- child_iri += '/'
111
-
112
- yield from LocalDirectory(logger=self.logger).as_quad_stream(
113
- source=child,
114
- iri=child_iri,
115
- root_loader=root_loader,
116
- context=context,
117
- )
118
-
119
- elif child.stem != 'context':
120
- yield from LocalFile(logger=self.logger).as_quad_stream(
121
- source=child,
122
- iri=child_iri,
123
- root_loader=root_loader,
124
- context=context,
125
- )
126
-
127
- if iri is not None:
128
- yield Quad(
129
- subject=child_iri,
130
- predicate=IOLANTA.isChildOf,
131
- object=iri,
132
- graph=URIRef(
133
- 'https://iolanta.tech/loaders/local-directory',
134
- ),
135
- )
136
-
137
- def as_file(self, source: Path) -> TextIO:
138
- """Construct a file-like object."""
139
- with source.open() as text_io:
140
- return text_io
141
-
142
- def as_jsonld_document(
143
- self,
144
- source: Path,
145
- iri: Optional[URIRef] = None,
146
- ) -> LDDocument:
147
- """As JSON-LD document."""
148
- raise ValueError('This is a directory.')