44
55import csv
66import gzip
7- from collections .abc import Generator , Iterable
7+ from collections .abc import Generator , Iterable , Sequence
88from contextlib import contextmanager
99from pathlib import Path
10- from typing import NamedTuple , TextIO
10+ from typing import TextIO
1111
12+ from pydantic import BaseModel
1213from typing_extensions import Self
1314
14- from curies import Reference
15+ from . api import Reference
1516
1617__all__ = [
1718 "Triple" ,
2021]
2122
2223
23- class Triple (NamedTuple ):
24- """A three-tuple of reference, useful for semantic web applications ."""
24+ class Triple (BaseModel ):
25+ """A model for a triple of subject-predicate-object triple ."""
2526
2627 subject : Reference
2728 predicate : Reference
2829 object : Reference
2930
3031 @classmethod
31- def from_curies (cls , subject_curie : str , predicate_curie : str , object_curie : str ) -> Self :
32+ def from_curies (
33+ cls ,
34+ subject_curie : str ,
35+ predicate_curie : str ,
36+ object_curie : str ,
37+ * ,
38+ reference_cls : type [Reference ] = Reference ,
39+ ) -> Self :
3240 """Construct a triple from three CURIE strings."""
3341 return cls (
34- Reference .from_curie (subject_curie ),
35- Reference .from_curie (predicate_curie ),
36- Reference .from_curie (object_curie ),
42+ subject = reference_cls .from_curie (subject_curie ),
43+ predicate = reference_cls .from_curie (predicate_curie ),
44+ object = reference_cls .from_curie (object_curie ),
3745 )
3846
3947
40- HEADER = Triple ._fields
48+ #: the default header for a three-column file representing triples
49+ HEADER = list (Triple .model_fields )
4150
4251
4352@contextmanager
@@ -49,11 +58,15 @@ def _get_file(path: str | Path, read: bool) -> Generator[TextIO, None, None]:
4958 yield open (path , mode = "r" if read else "w" )
5059
5160
52- def write_triples (triples : Iterable [Triple ], path : str | Path ) -> None :
61+ def write_triples (
62+ triples : Iterable [Triple ], path : str | Path , * , header : Sequence [str ] | None = None
63+ ) -> None :
5364 """Write triples to a file."""
65+ if header is None :
66+ header = HEADER
5467 with _get_file (path , read = False ) as file :
5568 writer = csv .writer (file , delimiter = "\t " )
56- writer .writerow (HEADER )
69+ writer .writerow (header )
5770 writer .writerows (
5871 (triple .subject .curie , triple .predicate .curie , triple .object .curie )
5972 for triple in triples
@@ -69,9 +82,9 @@ def read_triples(path: str | Path, *, reference_cls: type[Reference] | None = No
6982 _header = next (reader )
7083 return [
7184 Triple (
72- reference_cls .from_curie (subject_curie ),
73- reference_cls .from_curie (predicate_curie ),
74- reference_cls .from_curie (object_curie ),
85+ subject = reference_cls .from_curie (subject_curie ),
86+ predicate = reference_cls .from_curie (predicate_curie ),
87+ object = reference_cls .from_curie (object_curie ),
7588 )
7689 for subject_curie , predicate_curie , object_curie in reader
7790 ]
0 commit comments