Gnarly
Gnarly is a pretty-printer for RDF 1.2, used for serializing Turtle, TriG, and related RDF syntaxes.
Usage
Install from source or PyPI (e.g. using pip). Gnarly is written in Python and uses pyoxigraph for parsing RDF.
Pass files or pipe to the CLI:
$ gnarly test/data/test-gnarly.trig
(See gnarly -h for all options.)
Pretty-Printing Turtle/TriG
By default, Turtle/TriG is written with the following features:
- Uses given prefixes to compact IRIs into PNames,
- optional base IRI to shorten IRIs;
- group descriptions by subject,
- writes types first, on subject line if short enough, then
- predicates separated by semicolon, and
- multiple objects, separated by comma; with support for
- nested blank nodes,
- compact lists (with one line per item for longer values),
- boolean keywords, pure floats and doubles, and compact triple terms;
- compact forms of reifiers (including multiple and named reifiers), and
- compact annotations on asserted triples (including multiple and named annotations);
- takes multiple references into account and avoids blank node cycles.
Example Output
prefix : <https://example.net/ns/>
prefix ctg: <https://example.net/ns/category/>
<https://example.org/a> a :Thing ;
:category ctg:CommonThings ;
:name "A" ;
:references <https://example.org/b> ,
<https://example.org/c> ;
:value 1 ,
"a" .
<https://example.org/b> a :Thing ;
:category ctg:Regular ;
:references [ a :Thing ;
:name "C" ;
:references [
:category ctg:Special ;
:name "D" ;
:references [ :name "E" ]
]
] .
<https://example.org/c>
:items ( <https://example.org/b> <https://example.org/c> ) ;
:referenceList (
<https://example.org/b>
<https://example.org/c>
<https://example.org/d>
<https://example.org/e>
) ;
:valueList ( 1 "a" ) .
<https://example.org/d> :references <https://example.org/c> .
Formatting Options
There are different style conventions for Turtle/TriG. Gnarly has options to tweak some things to adhere to different preferences.
The default settings are:
- Indent: 2 spaces
- Max column: 88 characters
There are three named styles, exemplified here with differences in output:
Modern
This is the default, described above.
prefix : <https://example.net/ns/>
<https://example.org/a> a :Thing ;
:references <https://example.org/b> ,
[ a :Thing ;
:name "C" ;
:references [
:name "D" ;
:references [ :name "E" ]
]
] ;
:value 1 ,
"a" .
Classic
This form is more compact, but at the expense of readability for deeply nested blank nodes, and/or annotations. It also uses the classic, @-sigil for prefix and base, no explicit graph keyword, and no padding space before ;, ,, and . separators.
@prefix : <https://example.net/ns/>.
<https://example.org/a> a :Thing;
:references <https://example.org/b>,
[ a :Thing;
:name "C";
:references [
:name "D";
:references [ :name "E" ] ] ];
:value 1,
"a".
Longhand
Puts predicates, including types, on new lines, and ends all statement lines with semicolon; ending a subject description with a period on its own line. Uses uppercase SPARQL keywords.
This style is useful for rapid copy&paste editing and line-based diffs:
PREFIX : <https://example.net/ns/>
<https://example.org/a>
a :Thing ;
:references <https://example.org/b> ;
:references [
a :Thing ;
:name "C" ;
:references [
:name "D" ;
:references [ :name "E" ] ;
] ;
] ;
:value 1 ;
:value "a" ;
.
Detailed Formatting
The following examples are serialized using the default formatting options. (Many are variations on examples in the main RDF 1.2 Primer.)
# Prefixes are sorted, and declared using lowercase SPARQL-style syntax by default:
prefix : <http://example.net/ns/>
prefix dcterms: <http://purl.org/dc/terms/>
prefix foaf: <http://xmlns.com/foaf/0.1/>
prefix lio: <http://purl.org/net/lio#>
prefix prov: <http://www.w3.org/ns/prov#>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
prefix wd: <http://www.wikidata.org/entity/>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
# Types are written on the subject line:
<http://example.org/bob#me> a foaf:Person ;
foaf:birthday "1990-07-04"^^xsd:date ;
foaf:knows <http://example.org/alice#me> .
# Multiple triples are written under the subject:
wd:Q12418
dcterms:creator <http://dbpedia.org/resource/Leonardo_da_Vinci> ;
dcterms:title "Mona Lisa" .
# Single statements and simple embedded blank nodes are kept on one line:
wd:Q12418 dcterms:creator <http://dbpedia.org/resource/Leonardo_da_Vinci> .
wd:Q12418 lio:shows [ a <http://dbpedia.org/resource/Cypress> ] .
# Long lines also result in line breaks:
<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619>
dcterms:subject wd:Q12418 .
<http://dbpedia.org/resource/Mona_Lisa> lio:shows [
a <http://dbpedia.org/resource/Cypress> ] .
# A single blank annotation is written as:
<http://example.org/bob#me> a foaf:Person ;
foaf:topic_interest wd:Q12418 {| a rdf:Statement ;
dcterms:creator <http://example.org/alice#me> ;
dcterms:date "2004-01-12"^^xsd:date
|} .
# and a named annotation like:
<http://example.org/bob#me> a foaf:Person ;
foaf:topic_interest wd:Q12418 ~ <http://example.org/alice#claim-1> .
# Multiple annotations are written beneath the statement:
<http://example.org/bob#me> a foaf:Person ;
foaf:topic_interest wd:Q12418
{| a rdf:Statement ;
dcterms:creator <http://example.org/alice#me> ;
dcterms:date "2004-01-12"^^xsd:date
|}
{| a prov:Influence ;
dcterms:date "1998-10-04"^^xsd:date
|} .
# As are multiple named annotations:
<http://example.org/bob#me> a foaf:Person ;
foaf:topic_interest wd:Q12418
~ <http://example.org/alice#claim-1>
~ <http://example.org/bob#interest-1> .
<http://example.org/alice#claim-1> a rdf:Statement ;
dcterms:creator <http://example.org/alice#me> ;
dcterms:date "2004-01-12"^^xsd:date .
<http://example.org/bob#interest-1> a prov:Influence ;
dcterms:date "1998-10-04"^^xsd:date .
# As shown in the example output above, lists are written on one or multiple lines, depending on resulting width:
<https://example.org/c>
:items ( <https://example.org/b> <https://example.org/c> ) ;
:referenceList (
<https://example.org/b>
<https://example.org/c>
<https://example.org/d>
<https://example.org/e>
) ;
:valueList ( 1 "a" [ :value "b" ] ) .
Maintenance
Simple round-trip run of local examples, and (optionally) the official RDF Turtle/TriG tests:
$ ./test.sh clone/of/w3c/rdf-tests # from <https://github.com/w3c/rdf-tests>
Metadata
Release files for gnarly 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| gnarly-0.1.1.tar.gz | 13.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gnarly-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.2 kB
Release files / gnarly-0.1.1.tar.gz
| Download URL | gnarly-0.1.1.tar.gz |
|---|---|
| Size | 13.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1d010fd6c444c55bc76d9af61ee047996f0d2939cedaa01d176ff31a932f92c1
|
|
BLAKE2b-256 checksum How to use checksums |
02efed29351177e6971637e5059bc0b9e4befadc25a2635b070f64bab6b0dc43
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logRelease files / gnarly-0.1.1-py3-none-any.whl
| Download URL | gnarly-0.1.1-py3-none-any.whl |
|---|---|
| Size | 17.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
86fd45146f42b3a36623beecc87234213f48cdc7b0f3e07e18aa32bf1cc8ef42
|
|
BLAKE2b-256 checksum How to use checksums |
611dd1bb0bdc9e086f7cdf0622de325ddc9c6ad1a51b19f693264e890f01bd88
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logSecurity for gnarly 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for gnarly-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.