Skip to content

# `` longturtle

LongTurtle RDF graph serializer for RDFLib. See http://www.w3.org/TeamSubmission/turtle/ for syntax specification.

This variant, longturtle as opposed to just turtle, makes some small format changes to turtle - the original turtle serializer. It:

- uses PREFIX instead of @prefix
- uses BASE instead of @base
- adds a new line at RDF.type, or ‘a’
- adds a newline and an indent for all triples with more than one object (object list)
- adds a new line and ‘;’ for the last triple in a set with ‘.’ on the start of the next line

- uses default encoding (encode()) is used instead of “latin-1”

- Nicholas Car, 2023

Classes:

- `LongTurtleSerializer` –

  LongTurtle, a Turtle serialization format.

## `` OBJECT `module-attribute`

    OBJECT = 2

## `` SUBJECT `module-attribute`

    SUBJECT = 0

## `` VERB `module-attribute`

    VERB = 1

## `` __all__ `module-attribute`

    __all__ = ['LongTurtleSerializer']

## `` LongTurtleSerializer

    LongTurtleSerializer(store)

Bases: `RecursiveSerializer`

LongTurtle, a Turtle serialization format.

When the optional parameter `canon` is set to `True`, the graph is canonicalized before serialization. This normalizes blank node identifiers and allows for deterministic serialization of the graph. Useful when consistent outputs are required.

Methods:

- `addNamespace` –
- `canonize` –

  Apply canonicalization to the store.

- `doList` –
- `endDocument` –
- `getQName` –
- `get_pname` –
- `isValidList` –

  Checks if l is a valid RDF list, i.e. no nodes have other properties.

- `label` –
- `objectList` –
- `p_default` –
- `p_squared` –
- `path` –
- `predicateList` –
- `preprocessTriple` –
- `reset` –
- `s_default` –
- `s_squared` –
- `serialize` –
- `startDocument` –
- `statement` –
- `verb` –

Attributes:

- `indentString` –
- `keywords` –
- `short_name` –
- `stream` –

Source code in `rdflib/plugins/serializers/longturtle.py`

    53
    54
    55
    56
    57
    58
    59
    60

    def __init__(self, store):
        self._ns_rewrite = {}
        self._canon = False
        super(LongTurtleSerializer, self).__init__(store)
        self.keywords = {RDF.type: "a"}
        self.reset()
        self.stream = None
        self._spacious: bool = _SPACIOUS_OUTPUT

### `` indentString `class-attribute` `instance-attribute`

    indentString = '    '

### `` keywords `instance-attribute`

    keywords = {type: 'a'}

### `` short_name `class-attribute` `instance-attribute`

    short_name = 'longturtle'

### `` stream `instance-attribute`

    stream = None

### `` addNamespace

    addNamespace(prefix, namespace)

Source code in `rdflib/plugins/serializers/longturtle.py`

    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84

    def addNamespace(self, prefix, namespace):
        # Turtle does not support prefixes that start with _
        # if they occur in the graph, rewrite to p_blah
        # this is more complicated since we need to make sure p_blah
        # does not already exist. And we register namespaces as we go, i.e.
        # we may first see a triple with prefix _9 - rewrite it to p_9
        # and then later find a triple with a "real" p_9 prefix

        # so we need to keep track of ns rewrites we made so far.

        if (prefix > "" and prefix[0] == "_") or self.namespaces.get(
            prefix, namespace
        ) != namespace:
            if prefix not in self._ns_rewrite:
                p = "p" + prefix
                while p in self.namespaces:
                    p = "p" + p
                self._ns_rewrite[prefix] = p

            prefix = self._ns_rewrite.get(prefix, prefix)

        super(LongTurtleSerializer, self).addNamespace(prefix, namespace)
        return prefix

### `` canonize

    canonize()

Apply canonicalization to the store.

This normalizes blank node identifiers and allows for deterministic serialization of the graph.

Source code in `rdflib/plugins/serializers/longturtle.py`

     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106

    def canonize(self):
        """Apply canonicalization to the store.

        This normalizes blank node identifiers and allows for deterministic
        serialization of the graph.
        """
        if not self._canon:
            return

        namespace_manager = self.store.namespace_manager
        store = to_canonical_graph(self.store)
        content = store.serialize(format="application/n-triples")
        lines = content.split("\n")
        lines.sort()
        graph = Graph()
        graph.parse(
            data="\n".join(lines), format="application/n-triples", skolemize=True
        )
        graph = graph.de_skolemize()
        graph.namespace_manager = namespace_manager
        self.store = graph

### `` doList

    doList(l_)

Source code in `rdflib/plugins/serializers/longturtle.py`

    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339

    def doList(self, l_):
        i = 0
        while l_:
            item = self.store.value(l_, RDF.first)
            if item is not None:
                if i == 0:
                    self.write(self.indent(1))
                else:
                    self.write("\n" + self.indent(1))
                self.path(item, OBJECT, newline=True)
                self.subjectDone(l_)
            l_ = self.store.value(l_, RDF.rest)
            i += 1

### `` endDocument

    endDocument()

Source code in `rdflib/plugins/serializers/longturtle.py`

    230
    231
    232

    def endDocument(self):
        if self._spacious:
            self.write("\n")

### `` getQName

    getQName(uri, gen_prefix=True)

Source code in `rdflib/plugins/serializers/longturtle.py`

    211
    212
    213
    214
    215
    216
    217

    def getQName(self, uri, gen_prefix=True):
        warnings.warn(
            "LongTurtleSerializer.getQName is deprecated, use LongTurtleSerializer.get_pname instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.get_pname(uri, gen_prefix)

### `` get_pname

    get_pname(uri, gen_prefix=True)

Source code in `rdflib/plugins/serializers/longturtle.py`

    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209

    def get_pname(self, uri, gen_prefix=True):
        if not isinstance(uri, URIRef):
            return None

        try:
            parts = self.store.compute_qname(uri, generate=gen_prefix)
        except Exception:
            # is the uri a namespace in itself?
            pfx = self.store.store.prefix(uri)

            if pfx is not None:
                parts = (pfx, uri, "")
            else:
                # nothing worked
                return None

        prefix, namespace, local = parts

        # To understand treatment of % character refer to Productions for terminal PLX at
        # https://www.w3.org/TR/turtle/#grammar-production-PLX
        # Only % NOT followed by two hex chars requires manual backslash escaping
        local = local.replace(r"(", r"\(").replace(r")", r"\)")
        local = self.LOCALNAME_PECRENT_CHARACTER_REQUIRING_ESCAPE_REGEX.sub(
            "\\%", local
        )

        # PName cannot end with .
        if local.endswith("."):
            return None

        prefix = self.addNamespace(prefix, namespace)

        return "%s:%s" % (prefix, local)

### `` isValidList

    isValidList(l_)

Checks if l is a valid RDF list, i.e. no nodes have other properties.

Source code in `rdflib/plugins/serializers/longturtle.py`

    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325

    def isValidList(self, l_):
        """
        Checks if l is a valid RDF list, i.e. no nodes have other properties.
        """
        try:
            if self.store.value(l_, RDF.first) is None:
                return False
        except Exception:
            return False
        while l_:
            if l_ != RDF.nil and len(list(self.store.predicate_objects(l_))) != 2:
                return False
            l_ = self.store.value(l_, RDF.rest)
        return True

### `` label

    label(node, position)

Source code in `rdflib/plugins/serializers/longturtle.py`

    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279

    def label(self, node, position):
        if node == RDF.nil:
            return "()"
        if position is VERB and node in self.keywords:
            return self.keywords[node]
        if isinstance(node, Literal):
            return node._literal_n3(
                use_plain=True,
                qname_callback=lambda dt: self.get_pname(dt, _GEN_QNAME_FOR_DT),
            )
        else:
            node = self.relativize(node)

            return self.get_pname(node, position == VERB) or node.n3()

### `` objectList

    objectList(objects)

Source code in `rdflib/plugins/serializers/longturtle.py`

    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377

    def objectList(self, objects):
        count = len(objects)
        if count == 0:
            return
        depthmod = (count == 1) and 0 or 1
        self.depth += depthmod
        first_nl = False
        if count > 1:
            if not isinstance(objects[0], BNode):
                self.write("\n" + self.indent(1))
            else:
                self.write(" ")
            first_nl = True
        self.path(objects[0], OBJECT, newline=first_nl)
        for obj in objects[1:]:
            self.write(" ,")
            if not isinstance(obj, BNode):
                self.write("\n" + self.indent(1))
            self.path(obj, OBJECT, newline=True)
        self.depth -= depthmod

### `` p_default

    p_default(node, position, newline=False)

Source code in `rdflib/plugins/serializers/longturtle.py`

    260
    261
    262
    263
    264

    def p_default(self, node, position, newline=False):
        if position != SUBJECT and not newline:
            self.write(" ")
        self.write(self.label(node, position))
        return True

### `` p_squared

    p_squared(node, position)

Source code in `rdflib/plugins/serializers/longturtle.py`

    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310

    def p_squared(
        self,
        node,
        position,
    ):
        if (
            not isinstance(node, BNode)
            or node in self._serialized
            or self._references[node] > 1
            or position == SUBJECT
        ):
            return False

        if self.isValidList(node):
            # this is a list
            self.depth += 2
            self.write(" (\n")
            self.depth -= 2
            self.doList(node)
            self.write("\n" + self.indent() + ")")
        else:
            # this is a Blank Node
            self.subjectDone(node)
            self.write("\n" + self.indent(1) + "[\n")
            self.depth += 1
            self.predicateList(node)
            self.depth -= 1
            self.write("\n" + self.indent(1) + "]")

        return True

### `` path

    path(node, position, newline=False)

Source code in `rdflib/plugins/serializers/longturtle.py`

    254
    255
    256
    257
    258

    def path(self, node, position, newline=False):
        if not (
            self.p_squared(node, position) or self.p_default(node, position, newline)
        ):
            raise Error("Cannot serialize node '%s'" % (node,))

### `` predicateList

    predicateList(subject, newline=False)

Source code in `rdflib/plugins/serializers/longturtle.py`

    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353

    def predicateList(self, subject, newline=False):
        properties = self.buildPredicateHash(subject)
        propList = self.sortProperties(properties)
        if len(propList) == 0:
            return
        self.write(self.indent(1))
        self.verb(propList[0], newline=True)
        self.objectList(properties[propList[0]])
        for predicate in propList[1:]:
            self.write(" ;\n" + self.indent(1))
            self.verb(predicate, newline=True)
            self.objectList(properties[predicate])
        self.write(" ;")

### `` preprocessTriple

    preprocessTriple(triple: _TripleType) -> None

Source code in `rdflib/plugins/serializers/longturtle.py`

    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175

    def preprocessTriple(self, triple: _TripleType) -> None:
        super(LongTurtleSerializer, self).preprocessTriple(triple)
        for i, node in enumerate(triple):
            if i == VERB:
                if node in self.keywords:
                    # predicate is a keyword
                    continue
                if (
                    self.base is not None
                    and isinstance(node, URIRef)
                    and node.startswith(self.base)
                    and "#" not in node.replace(self.base, "")
                    and "/" not in node.replace(self.base, "")
                ):
                    # predicate corresponds to base namespace
                    continue
            # Don't use generated prefixes for subjects and objects
            self.get_pname(node, gen_prefix=(i == VERB))
            if isinstance(node, Literal) and node.datatype:
                self.get_pname(node.datatype, gen_prefix=_GEN_QNAME_FOR_DT)
        p = triple[1]
        if isinstance(p, BNode):  # hmm - when is P ever a bnode?
            self._references[p] += 1

### `` reset

    reset()

Source code in `rdflib/plugins/serializers/longturtle.py`

    108
    109
    110
    111
    112
    113

    def reset(self):
        super(LongTurtleSerializer, self).reset()
        self._shortNames = {}
        self._started = False
        self._ns_rewrite = {}
        self.canonize()

### `` s_default

    s_default(subject)

Source code in `rdflib/plugins/serializers/longturtle.py`

    238
    239
    240
    241
    242
    243
    244

    def s_default(self, subject):
        self.write("\n" + self.indent())
        self.path(subject, SUBJECT)
        self.write("\n" + self.indent())
        self.predicateList(subject)
        self.write("\n.")
        return True

### `` s_squared

    s_squared(subject)

Source code in `rdflib/plugins/serializers/longturtle.py`

    246
    247
    248
    249
    250
    251
    252

    def s_squared(self, subject):
        if (self._references[subject] > 0) or not isinstance(subject, BNode):
            return False
        self.write("\n" + self.indent() + "[]")
        self.predicateList(subject, newline=False)
        self.write("\n.")
        return True

### `` serialize

    serialize(stream: IO[bytes], base: Optional[str] = None, encoding: Optional[str] = None, spacious: Optional[bool] = None, **kwargs: Any) -> None

Source code in `rdflib/plugins/serializers/longturtle.py`

    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151

    def serialize(
        self,
        stream: IO[bytes],
        base: Optional[str] = None,
        encoding: Optional[str] = None,
        spacious: Optional[bool] = None,
        **kwargs: Any,
    ) -> None:
        self._canon = kwargs.get("canon", False)
        self.reset()
        self.stream = stream
        # if base is given here, use, if not and a base is set for the graph use that
        if base is not None:
            self.base = base
        elif self.store.base is not None:
            self.base = self.store.base

        if spacious is not None:
            self._spacious = spacious

        self.preprocess()
        subjects_list = self.orderSubjects()

        self.startDocument()

        firstTime = True
        for subject in subjects_list:
            if self.isDone(subject):
                continue
            if firstTime:
                firstTime = False
            if self.statement(subject) and not firstTime:
                self.write("\n")

        self.endDocument()

        self.base = None

### `` startDocument

    startDocument()

Source code in `rdflib/plugins/serializers/longturtle.py`

    219
    220
    221
    222
    223
    224
    225
    226
    227
    228

    def startDocument(self):
        self._started = True
        ns_list = sorted(self.namespaces.items())

        if self.base:
            self.write(self.indent() + "BASE <%s>\n" % self.base)
        for prefix, uri in ns_list:
            self.write(self.indent() + "PREFIX %s: <%s>\n" % (prefix, uri))
        if ns_list and self._spacious:
            self.write("\n")

### `` statement

    statement(subject)

Source code in `rdflib/plugins/serializers/longturtle.py`

    234
    235
    236

    def statement(self, subject):
        self.subjectDone(subject)
        return self.s_squared(subject) or self.s_default(subject)

### `` verb

    verb(node, newline=False)

Source code in `rdflib/plugins/serializers/longturtle.py`

    355
    356

    def verb(self, node, newline=False):
        self.path(node, VERB, newline)