├── rust-toolchain ├── lightrdf ├── python_module │ ├── __init__.py │ ├── utils.py │ ├── nt.py │ ├── parse.py │ ├── xml.py │ ├── turtle.py │ └── terms.py └── __init__.py ├── assets ├── benchmark1.png └── benchmark2.png ├── .gitignore ├── src ├── lib.rs ├── parser_macro.rs ├── nt.rs ├── xml.rs ├── turtle.rs └── common.rs ├── tox.ini ├── pyproject.toml ├── Cargo.toml ├── CHANGELOG.md ├── util └── benchmark_plot.py ├── .github └── workflows │ └── ci.yaml ├── README.md ├── tests └── test_lightrdf.py ├── LICENSE ├── poetry.lock └── Cargo.lock /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable 2 | -------------------------------------------------------------------------------- /lightrdf/python_module/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/benchmark1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozekik/lightrdf/HEAD/assets/benchmark1.png -------------------------------------------------------------------------------- /assets/benchmark2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ozekik/lightrdf/HEAD/assets/benchmark2.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | venv*/ 3 | .venv/ 4 | .pytest_cache/ 5 | .tox/ 6 | *.so 7 | __pycache__/ 8 | *.egg-info/ 9 | *.egg 10 | dist/ 11 | *.whl 12 | build 13 | dist 14 | -------------------------------------------------------------------------------- /lightrdf/python_module/utils.py: -------------------------------------------------------------------------------- 1 | def is_iri(term: str): 2 | return term.startswith("<") 3 | 4 | 5 | def is_blank(term: str): 6 | return term.startswith("_:") 7 | 8 | 9 | def is_literal(term: str): 10 | return term.startswith('"') 11 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use pyo3::prelude::*; 2 | use pyo3::wrap_pymodule; 3 | 4 | mod nt; 5 | mod turtle; 6 | mod xml; 7 | 8 | mod common; 9 | mod parser_macro; 10 | 11 | #[pymodule] 12 | fn lightrdf(_py: Python<'_>, m: &PyModule) -> PyResult<()> { 13 | m.add_wrapped(wrap_pymodule!(nt::nt))?; 14 | m.add_wrapped(wrap_pymodule!(turtle::turtle))?; 15 | m.add_wrapped(wrap_pymodule!(xml::xml))?; 16 | 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # tox (https://tox.readthedocs.io/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py38,py39,py310,py311 8 | skip_missing_interpreters = true 9 | 10 | [testenv] 11 | allowlist_externals = maturin 12 | deps = 13 | pytest ~= 7.4.2 14 | commands_pre = 15 | maturin develop --release 16 | commands = 17 | pytest --cache-clear tests/ 18 | -------------------------------------------------------------------------------- /src/parser_macro.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! gen_create_iter { 3 | ($error:ident) => { 4 | fn create_iter>( 5 | parser: P, 6 | ) -> impl iter::Iterator> { 7 | let mut it = parser.into_iter(move |t| { 8 | let st = common::triple_to_striple(t); 9 | Ok(st) as Result<_, common::ParserError> 10 | }); 11 | iter::from_fn(move || match it.next() { 12 | Some(Ok(v)) => Some(Ok(v)), 13 | Some(Err(e)) => Some(Err(e)), 14 | _ => None, 15 | }) 16 | } 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "lightrdf" 3 | version = "0.4.0" 4 | description = "A fast and lightweight Python RDF parser which wraps bindings to Rust's Rio using PyO3" 5 | authors = ["Kentaro Ozeki "] 6 | license = "Apache-2.0" 7 | homepage = "https://github.com/ozekik/lightrdf" 8 | repository = "https://github.com/ozekik/lightrdf" 9 | 10 | [tool.poetry.dependencies] 11 | python = "^3.8" 12 | 13 | [tool.poetry.dev-dependencies] 14 | tox = "^4.11.3" 15 | maturin = "^1.2.3" 16 | pytest = "^7.4.2" 17 | black = {version = "^22.8.0", allow-prereleases = true} 18 | 19 | [build-system] 20 | requires = ["maturin>=1.2,<2.0"] 21 | build-backend = "maturin" 22 | 23 | [tool.maturin] 24 | features = ["pyo3/extension-module"] 25 | -------------------------------------------------------------------------------- /lightrdf/python_module/nt.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from ..lightrdf import nt as _nt 4 | 5 | 6 | class Parser: 7 | def __init__(self): 8 | pass 9 | 10 | def parse(self, filelike_or_filename): 11 | if isinstance(filelike_or_filename, io.BufferedIOBase): 12 | return _nt.Parser().parse(filelike_or_filename) 13 | else: 14 | return _nt.Parser().parse_from_filename(filelike_or_filename) 15 | 16 | 17 | class PatternParser: 18 | def __init__(self, pattern): 19 | self.pattern = pattern 20 | 21 | def parse(self, filelike_or_filename): 22 | if isinstance(filelike_or_filename, io.BufferedIOBase): 23 | return _nt.PatternParser(self.pattern).parse(filelike_or_filename) 24 | else: 25 | return _nt.PatternParser(self.pattern).parse_from_filename(filelike_or_filename) 26 | -------------------------------------------------------------------------------- /lightrdf/python_module/parse.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import Tuple, Optional 3 | 4 | iri_re = re.compile(r"<(.*)>") 5 | blank_re = re.compile(r"_:(.*)") 6 | literal_re = re.compile(r'"(.*)"(?:\^\^([^@]*))?(?:@(.*))?') 7 | 8 | 9 | def iri(term: str) -> str: 10 | """Returns an IRI, i.e., the value inside `<` and `>`.""" 11 | (iri,) = iri_re.match(term).groups() 12 | return iri 13 | 14 | 15 | def blank(term: str) -> str: 16 | """Returns [a blank node label](https://www.w3.org/TR/n-triples/#h3_BNodes), i.e., the value after `_:`.""" 17 | (id,) = blank_re.match(term).groups() 18 | return id 19 | 20 | 21 | def literal(term: str) -> Tuple[Optional[str], Optional[str], Optional[str]]: 22 | """Returns a tuple `(value, datatype, language)`.""" 23 | value, datatype, language = literal_re.match(term).groups() 24 | return value, datatype, language 25 | -------------------------------------------------------------------------------- /lightrdf/python_module/xml.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from ..lightrdf import xml as _xml 4 | 5 | 6 | class Parser: 7 | def __init__(self): 8 | pass 9 | 10 | def parse(self, filelike_or_filename, base_iri=None): 11 | if isinstance(filelike_or_filename, io.BufferedIOBase): 12 | return _xml.Parser().parse(filelike_or_filename, base_iri) 13 | else: 14 | return _xml.Parser().parse_from_filename(filelike_or_filename, base_iri) 15 | 16 | 17 | class PatternParser: 18 | def __init__(self, pattern): 19 | self.pattern = pattern 20 | 21 | def parse(self, filelike_or_filename, base_iri=None): 22 | if isinstance(filelike_or_filename, io.BufferedIOBase): 23 | return _xml.PatternParser(self.pattern).parse(filelike_or_filename, base_iri) 24 | else: 25 | return _xml.PatternParser(self.pattern).parse_from_filename(filelike_or_filename, base_iri) 26 | -------------------------------------------------------------------------------- /lightrdf/python_module/turtle.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from ..lightrdf import turtle as _turtle 4 | 5 | 6 | class Parser: 7 | def __init__(self): 8 | pass 9 | 10 | def parse(self, filelike_or_filename, base_iri=None): 11 | if isinstance(filelike_or_filename, io.BufferedIOBase): 12 | return _turtle.Parser().parse(filelike_or_filename, base_iri) 13 | else: 14 | return _turtle.Parser().parse_from_filename(filelike_or_filename, base_iri) 15 | 16 | 17 | class PatternParser: 18 | def __init__(self, pattern): 19 | self.pattern = pattern 20 | 21 | def parse(self, filelike_or_filename, base_iri=None): 22 | if isinstance(filelike_or_filename, io.BufferedIOBase): 23 | return _turtle.PatternParser(self.pattern).parse(filelike_or_filename, base_iri) 24 | else: 25 | return _turtle.PatternParser(self.pattern).parse_from_filename(filelike_or_filename, base_iri) 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lightrdf" 3 | version = "0.4.0" 4 | authors = ["Kentaro Ozeki <32771324+ozekik@users.noreply.github.com>"] 5 | description = "A fast and lightweight Python RDF parser which wraps bindings to Rust's Rio using PyO3" 6 | edition = "2021" 7 | license = "Apache-2.0" 8 | homepage = "https://github.com/ozekik/lightrdf" 9 | repository = "https://github.com/ozekik/lightrdf" 10 | readme = "README.md" 11 | 12 | [lib] 13 | name = "lightrdf" 14 | crate-type = ["cdylib"] 15 | 16 | [dependencies] 17 | oxiri = "0.2.2" 18 | pyo3-file = "0.7.0" 19 | rio_api = "0.8.4" 20 | rio_turtle = "0.8.4" 21 | rio_xml = "0.8.4" 22 | signal-hook = "0.3.17" 23 | pyo3 = "0.19.2" 24 | regex = "1" 25 | lazy_static = "1" 26 | itertools = "0.11.0" 27 | 28 | [features] 29 | # https://github.com/PyO3/pyo3/blob/c6abf24cca6a74a59d9a88715944f56313e1de21/guide/src/faq.md#i-cant-run-cargo-test-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror 30 | default = ["pyo3/extension-module"] 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | v0.4.0 / 2023-09-26 3 | =================== 4 | 5 | * Update Rio to 0.8.4 6 | * Update PyO3 to v0.19.x 7 | 8 | v0.3.2 / 2023-09-26 9 | =================== 10 | 11 | * Update dependencies 12 | * [INTERNAL] Set Rust edition to 2021 13 | * [INTERNAL] Update maturin and GitHub Actions workflow (using maturin-action@v1) 14 | * [INTERNAL] Update tests in CI 15 | 16 | v0.3.1 / 2022-09-04 17 | =================== 18 | 19 | * Remove debug 20 | 21 | v0.3.0 / 2022-09-04 22 | =================== 23 | 24 | * [BREAKING] Change the return value format to ntriples 25 | * Add regex support 26 | 27 | v0.2.2 / 2022-09-02 28 | =================== 29 | 30 | * [BREAKING] Drop Python 3.6 support 31 | * Update Rio to 0.7 32 | 33 | v0.2.1 / 2020-12-23 34 | =================== 35 | 36 | * Support Python 3.9 37 | 38 | v0.2.0 / 2020-10-24 39 | =================== 40 | 41 | * Support file-like objects 42 | 43 | v0.1.1 / 2020-03-21 44 | =================== 45 | 46 | * Fix version compatibility 47 | 48 | v0.1.0 / 2020-03-21 49 | =================== 50 | 51 | * Initial release 52 | -------------------------------------------------------------------------------- /lightrdf/python_module/terms.py: -------------------------------------------------------------------------------- 1 | import re 2 | import string 3 | 4 | 5 | def escape(x: str): 6 | # TODO: Use Rust regex escape 7 | x = re.escape(x) 8 | for w in string.whitespace: 9 | x = x.replace(f"\\{w}", w) 10 | return x 11 | 12 | 13 | class Term: 14 | def __str__(self): 15 | return self.pattern 16 | 17 | 18 | class Regex(Term): 19 | def __init__(self, pattern): 20 | self.pattern = pattern 21 | 22 | 23 | class IRI(Term): 24 | def __init__(self, iri): 25 | self.iri = iri 26 | iri = iri if type(iri) is Regex or iri is None else escape(iri) 27 | self.pattern = "^<{}>$".format(iri if iri else "(.*)") 28 | 29 | 30 | class Blank(Term): 31 | def __init__(self, id): 32 | self.id = id 33 | id = id if type(id) is Regex or id is None else escape(id) 34 | self.pattern = "^_:{}$".format(id if id else "(.*)") 35 | 36 | 37 | class Literal(Term): 38 | def __init__(self, value, datatype, language): 39 | self.value = value 40 | self.datatype = datatype 41 | self.language = language 42 | value = value if type(value) is Regex or value is None else escape(value) 43 | datatype = ( 44 | datatype 45 | if type(datatype) is Regex or datatype is None 46 | else escape(datatype) 47 | ) 48 | language = ( 49 | language 50 | if type(language) is Regex or language is None 51 | else escape(language) 52 | ) 53 | self.pattern = '^"{}"{}{}$'.format( 54 | value if value else "(.*)", 55 | f"^^{datatype}" if datatype else r"(?:\^\^([^@]*))?", 56 | f"@{language}" if language else r"(?:@(.*))?", 57 | ) 58 | 59 | 60 | class Triple(Term): 61 | # TODO 62 | pass 63 | -------------------------------------------------------------------------------- /util/benchmark_plot.py: -------------------------------------------------------------------------------- 1 | import matplotlib 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | import datetime 5 | from matplotlib.ticker import FuncFormatter 6 | 7 | # matplotlib.use("Agg") 8 | 9 | 10 | def format_timedelta(x, pos): 11 | minutes = int((x % (60 * 60)) // 60) 12 | seconds = float(x % 60) 13 | 14 | return "{:d}m{:.0f}s".format(minutes, seconds) 15 | 16 | 17 | formatter = FuncFormatter(format_timedelta) 18 | 19 | labels = ["RDFLib 4.2.2", "LightRDF 0.1.1\n(RDFDocument)", "LightRDF 0.1.1\n(Parser)"] 20 | values = ["3:59.56", "0:08.27", "0:08.47"] 21 | values = list( 22 | map( 23 | lambda x: datetime.timedelta( 24 | minutes=int(x.split(":")[0]), seconds=float(x.split(":")[1]) 25 | ), 26 | values, 27 | ) 28 | ) 29 | values = list(map(lambda x: x.seconds, values)) 30 | 31 | with plt.xkcd(): 32 | fig, ax = plt.subplots() 33 | 34 | ax.set_title("Parsing 1436427 triples (go.owl)", fontsize=14) 35 | ax.bar(labels, values) 36 | ax.yaxis.set_major_formatter(formatter) 37 | ax.yaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=30)) 38 | ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=1)) 39 | 40 | fig.tight_layout() 41 | # plt.show() 42 | fig.savefig("benchmark1.png") 43 | 44 | 45 | labels = ["RDFLib 4.2.2", "LightRDF 0.1.1\n(NTTriplesParser)"] 46 | values = ["0:02.47", "0:00.36"] 47 | values = list( 48 | map( 49 | lambda x: datetime.timedelta( 50 | minutes=int(x.split(":")[0]), seconds=float(x.split(":")[1]) 51 | ), 52 | values, 53 | ) 54 | ) 55 | values = list(map(lambda x: x.seconds, values)) 56 | 57 | with plt.xkcd(): 58 | fig, ax = plt.subplots() 59 | 60 | ax.set_title("Parsing 31050 triples (dbpedia_2016-10.nt)", fontsize=14) 61 | ax.bar(labels, values) 62 | ax.yaxis.set_major_formatter(formatter) 63 | ax.set_ylim(0, 60) 64 | ax.yaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=10)) 65 | ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=1)) 66 | 67 | fig.tight_layout() 68 | # plt.show() 69 | fig.savefig("benchmark2.png") 70 | -------------------------------------------------------------------------------- /src/nt.rs: -------------------------------------------------------------------------------- 1 | extern crate signal_hook; 2 | use itertools::Itertools; 3 | use pyo3::prelude::*; 4 | use pyo3::types::PyTuple; 5 | use pyo3_file::PyFileLikeObject; 6 | use regex::{escape, Regex}; 7 | use rio_api::parser::TriplesParser; 8 | use rio_turtle::{NTriplesParser, TurtleError}; 9 | use std::fs::File; 10 | use std::io::BufReader; 11 | use std::iter; 12 | use std::sync::atomic::AtomicBool; 13 | use std::sync::Arc; 14 | 15 | use super::common; 16 | use super::gen_create_iter; 17 | 18 | gen_create_iter!(TurtleError); 19 | 20 | #[pyclass] 21 | struct Parser {} 22 | 23 | #[pymethods] 24 | impl Parser { 25 | #[new] 26 | fn new() -> Self { 27 | Parser {} 28 | } 29 | fn parse(&self, file: PyObject) -> PyResult { 30 | let f = PyFileLikeObject::with_requirements(file, true, false, false)?; 31 | let buf = BufReader::new(f); 32 | let parser = NTriplesParser::new(buf); 33 | let term = Arc::new(AtomicBool::new(false)); 34 | Ok(common::TriplesIterator { 35 | it: Box::new(create_iter(parser)), 36 | pattern: (None, None, None) as common::RegexTriplePattern, 37 | term: term, 38 | }) 39 | } 40 | fn parse_from_filename(&self, filename: &str) -> PyResult { 41 | let f = File::open(filename)?; 42 | let buf = BufReader::new(f); 43 | let parser = NTriplesParser::new(buf); 44 | let term = Arc::new(AtomicBool::new(false)); 45 | Ok(common::TriplesIterator { 46 | it: Box::new(create_iter(parser)), 47 | pattern: (None, None, None) as common::RegexTriplePattern, 48 | term: term, 49 | }) 50 | } 51 | } 52 | 53 | #[pyclass] 54 | struct PatternParser { 55 | // pattern: common::TriplePattern, 56 | pattern_re: common::RegexTriplePattern, 57 | } 58 | 59 | #[pymethods] 60 | impl PatternParser { 61 | #[new] 62 | #[pyo3(signature = (pattern, regex = true))] 63 | fn new(pattern: &PyTuple, regex: bool) -> Self { 64 | let _pattern = pattern.extract::().unwrap(); 65 | let pattern_re: (Option, Option, Option) = 66 | vec![_pattern.0.clone(), _pattern.1.clone(), _pattern.2.clone()] 67 | .iter() 68 | .map(|x| match x { 69 | None => None, 70 | Some(term) => { 71 | if regex { 72 | Some(Regex::new(term).unwrap()) 73 | } else { 74 | Some(Regex::new(&escape(term)).unwrap()) 75 | } 76 | } 77 | }) 78 | .collect_tuple() 79 | .unwrap(); 80 | PatternParser { 81 | // pattern: _pattern, 82 | pattern_re, 83 | } 84 | } 85 | fn parse(&self, file: PyObject) -> PyResult { 86 | let f = PyFileLikeObject::with_requirements(file, true, false, false)?; 87 | let buf = BufReader::new(f); 88 | let parser = NTriplesParser::new(buf); 89 | let term = Arc::new(AtomicBool::new(false)); 90 | 91 | Ok(common::TriplesIterator { 92 | it: Box::new(create_iter(parser)), 93 | pattern: self.pattern_re.clone(), 94 | term: term, 95 | }) 96 | } 97 | fn parse_from_filename(&self, filename: &str) -> PyResult { 98 | let f = File::open(filename)?; 99 | let buf = BufReader::new(f); 100 | let parser = NTriplesParser::new(buf); 101 | let term = Arc::new(AtomicBool::new(false)); 102 | Ok(common::TriplesIterator { 103 | it: Box::new(create_iter(parser)), 104 | pattern: self.pattern_re.clone(), 105 | term: term, 106 | }) 107 | } 108 | } 109 | 110 | #[pymodule] 111 | pub(crate) fn nt(_py: Python<'_>, m: &PyModule) -> PyResult<()> { 112 | m.add_class::()?; 113 | m.add_class::()?; 114 | 115 | Ok(()) 116 | } 117 | -------------------------------------------------------------------------------- /src/xml.rs: -------------------------------------------------------------------------------- 1 | extern crate signal_hook; 2 | use itertools::Itertools; 3 | use oxiri::Iri; 4 | use pyo3::prelude::*; 5 | use pyo3::types::PyTuple; 6 | use pyo3_file::PyFileLikeObject; 7 | use regex::{escape, Regex}; 8 | use rio_api::parser::TriplesParser; 9 | use rio_xml::{RdfXmlError, RdfXmlParser}; 10 | use std::fs::File; 11 | use std::io::BufReader; 12 | use std::iter; 13 | use std::sync::atomic::AtomicBool; 14 | use std::sync::Arc; 15 | 16 | use super::common; 17 | use super::gen_create_iter; 18 | 19 | gen_create_iter!(RdfXmlError); 20 | 21 | #[pyclass] 22 | struct Parser {} 23 | 24 | #[pymethods] 25 | impl Parser { 26 | #[new] 27 | fn new() -> Self { 28 | Parser {} 29 | } 30 | fn parse(&self, file: PyObject, base_iri: Option<&str>) -> PyResult { 31 | let f = PyFileLikeObject::with_requirements(file, true, false, false)?; 32 | let buf = BufReader::new(f); 33 | let parser = RdfXmlParser::new( 34 | buf, 35 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 36 | ); 37 | let term = Arc::new(AtomicBool::new(false)); 38 | Ok(common::TriplesIterator { 39 | it: Box::new(create_iter(parser)), 40 | pattern: (None, None, None) as common::RegexTriplePattern, 41 | term: term, 42 | }) 43 | } 44 | fn parse_from_filename( 45 | &self, 46 | filename: &str, 47 | base_iri: Option<&str>, 48 | ) -> PyResult { 49 | let f = File::open(filename)?; 50 | let buf = BufReader::new(f); 51 | let parser = RdfXmlParser::new( 52 | buf, 53 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 54 | ); 55 | let term = Arc::new(AtomicBool::new(false)); 56 | Ok(common::TriplesIterator { 57 | it: Box::new(create_iter(parser)), 58 | pattern: (None, None, None) as common::RegexTriplePattern, 59 | term: term, 60 | }) 61 | } 62 | } 63 | 64 | #[pyclass] 65 | struct PatternParser { 66 | // pattern: common::TriplePattern, 67 | pattern_re: common::RegexTriplePattern, 68 | } 69 | 70 | #[pymethods] 71 | impl PatternParser { 72 | #[new] 73 | #[pyo3(signature = (pattern, regex = true))] 74 | fn new(pattern: &PyTuple, regex: bool) -> Self { 75 | let _pattern = pattern.extract::().unwrap(); 76 | let pattern_re: (Option, Option, Option) = 77 | vec![_pattern.0.clone(), _pattern.1.clone(), _pattern.2.clone()] 78 | .iter() 79 | .map(|x| match x { 80 | None => None, 81 | Some(term) => { 82 | if regex { 83 | Some(Regex::new(term).unwrap()) 84 | } else { 85 | Some(Regex::new(&escape(term)).unwrap()) 86 | } 87 | } 88 | }) 89 | .collect_tuple() 90 | .unwrap(); 91 | PatternParser { 92 | // pattern: _pattern, 93 | pattern_re, 94 | } 95 | } 96 | fn parse(&self, file: PyObject, base_iri: Option<&str>) -> PyResult { 97 | let f = PyFileLikeObject::with_requirements(file, true, false, false)?; 98 | let buf = BufReader::new(f); 99 | let parser = RdfXmlParser::new( 100 | buf, 101 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 102 | ); 103 | let term = Arc::new(AtomicBool::new(false)); 104 | Ok(common::TriplesIterator { 105 | it: Box::new(create_iter(parser)), 106 | pattern: self.pattern_re.clone(), 107 | term: term, 108 | }) 109 | } 110 | fn parse_from_filename( 111 | &self, 112 | filename: &str, 113 | base_iri: Option<&str>, 114 | ) -> PyResult { 115 | let f = File::open(filename)?; 116 | let buf = BufReader::new(f); 117 | let parser = RdfXmlParser::new( 118 | buf, 119 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 120 | ); 121 | let term = Arc::new(AtomicBool::new(false)); 122 | Ok(common::TriplesIterator { 123 | it: Box::new(create_iter(parser)), 124 | pattern: self.pattern_re.clone(), 125 | term: term, 126 | }) 127 | } 128 | } 129 | 130 | #[pymodule] 131 | pub(crate) fn xml(_py: Python<'_>, m: &PyModule) -> PyResult<()> { 132 | m.add_class::()?; 133 | m.add_class::()?; 134 | 135 | Ok(()) 136 | } 137 | -------------------------------------------------------------------------------- /src/turtle.rs: -------------------------------------------------------------------------------- 1 | extern crate signal_hook; 2 | use itertools::Itertools; 3 | use oxiri::Iri; 4 | use pyo3::prelude::*; 5 | use pyo3::types::PyTuple; 6 | use pyo3_file::PyFileLikeObject; 7 | use regex::{escape, Regex}; 8 | use rio_api::parser::TriplesParser; 9 | use rio_turtle::{TurtleError, TurtleParser}; 10 | use std::fs::File; 11 | use std::io::BufReader; 12 | use std::iter; 13 | use std::sync::atomic::AtomicBool; 14 | use std::sync::Arc; 15 | 16 | use super::common; 17 | use super::gen_create_iter; 18 | 19 | gen_create_iter!(TurtleError); 20 | 21 | #[pyclass] 22 | struct Parser {} 23 | 24 | #[pymethods] 25 | impl Parser { 26 | #[new] 27 | fn new() -> Self { 28 | Parser {} 29 | } 30 | fn parse(&self, file: PyObject, base_iri: Option<&str>) -> PyResult { 31 | let f = PyFileLikeObject::with_requirements(file, true, false, false)?; 32 | let buf = BufReader::new(f); 33 | let parser = TurtleParser::new( 34 | buf, 35 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 36 | ); 37 | let term = Arc::new(AtomicBool::new(false)); 38 | Ok(common::TriplesIterator { 39 | it: Box::new(create_iter(parser)), 40 | pattern: (None, None, None) as common::RegexTriplePattern, 41 | term: term, 42 | }) 43 | } 44 | fn parse_from_filename( 45 | &self, 46 | filename: &str, 47 | base_iri: Option<&str>, 48 | ) -> PyResult { 49 | let f = File::open(filename)?; 50 | let buf = BufReader::new(f); 51 | let parser = TurtleParser::new( 52 | buf, 53 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 54 | ); 55 | let term = Arc::new(AtomicBool::new(false)); 56 | Ok(common::TriplesIterator { 57 | it: Box::new(create_iter(parser)), 58 | pattern: (None, None, None) as common::RegexTriplePattern, 59 | term: term, 60 | }) 61 | } 62 | } 63 | 64 | #[pyclass] 65 | struct PatternParser { 66 | // pattern: common::TriplePattern, 67 | pattern_re: common::RegexTriplePattern, 68 | } 69 | 70 | #[pymethods] 71 | impl PatternParser { 72 | #[new] 73 | #[pyo3(signature = (pattern, regex = true))] 74 | fn new(pattern: &PyTuple, regex: bool) -> Self { 75 | let _pattern = pattern.extract::().unwrap(); 76 | let pattern_re: (Option, Option, Option) = 77 | vec![_pattern.0.clone(), _pattern.1.clone(), _pattern.2.clone()] 78 | .iter() 79 | .map(|x| match x { 80 | None => None, 81 | Some(term) => { 82 | if regex { 83 | Some(Regex::new(term).unwrap()) 84 | } else { 85 | Some(Regex::new(&escape(term)).unwrap()) 86 | } 87 | } 88 | }) 89 | .collect_tuple() 90 | .unwrap(); 91 | PatternParser { 92 | // pattern: _pattern, 93 | pattern_re, 94 | } 95 | } 96 | fn parse(&self, file: PyObject, base_iri: Option<&str>) -> PyResult { 97 | let f = PyFileLikeObject::with_requirements(file, true, false, false)?; 98 | let buf = BufReader::new(f); 99 | let parser = TurtleParser::new( 100 | buf, 101 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 102 | ); 103 | let term = Arc::new(AtomicBool::new(false)); 104 | Ok(common::TriplesIterator { 105 | it: Box::new(create_iter(parser)), 106 | pattern: self.pattern_re.clone(), 107 | term: term, 108 | }) 109 | } 110 | fn parse_from_filename( 111 | &self, 112 | filename: &str, 113 | base_iri: Option<&str>, 114 | ) -> PyResult { 115 | let f = File::open(filename)?; 116 | let buf = BufReader::new(f); 117 | let parser = TurtleParser::new( 118 | buf, 119 | base_iri.and_then(|iri| Iri::parse(iri.to_string()).ok()), 120 | ); 121 | let term = Arc::new(AtomicBool::new(false)); 122 | Ok(common::TriplesIterator { 123 | it: Box::new(create_iter(parser)), 124 | pattern: self.pattern_re.clone(), 125 | term: term, 126 | }) 127 | } 128 | } 129 | 130 | #[pymodule] 131 | pub(crate) fn turtle(_py: Python<'_>, m: &PyModule) -> PyResult<()> { 132 | m.add_class::()?; 133 | m.add_class::()?; 134 | 135 | Ok(()) 136 | } 137 | -------------------------------------------------------------------------------- /lightrdf/__init__.py: -------------------------------------------------------------------------------- 1 | import io 2 | from pathlib import Path 3 | 4 | from .python_module import nt 5 | from .python_module import turtle 6 | from .python_module import xml 7 | from .python_module import parse 8 | from .python_module.utils import is_iri, is_blank, is_literal 9 | from .python_module.terms import Regex, IRI, Blank, Literal 10 | 11 | __all__ = [ 12 | "nt", 13 | "turtle", 14 | "xml", 15 | "parse", 16 | "is_iri", 17 | "is_blank", 18 | "is_literal", 19 | "Regex", 20 | "IRI", 21 | "Blank", 22 | "Literal", 23 | "Parser", 24 | "PatternParser", 25 | "RDFDocument", 26 | ] 27 | 28 | # Derived from https://github.com/rubensworks/rdf-parse.js/blob/master/lib/RdfParser.ts 29 | CONTENT_MAPPINGS = { 30 | ".ttl": "text/turtle", 31 | ".turtle": "text/turtle", 32 | ".nt": "application/n-triples", 33 | ".ntriples": "application/n-triples", 34 | ".nq": "application/n-quads", 35 | ".nquads": "application/n-quads", 36 | ".rdf": "application/rdf+xml", 37 | ".rdfxml": "application/rdf+xml", 38 | ".xml": "application/rdf+xml", 39 | ".owl": "application/rdf+xml", 40 | ".n3": "text/n3", 41 | ".trig": "application/trig", 42 | ".jsonld": "application/ld+json", 43 | ".json": "application/json", 44 | } 45 | 46 | 47 | class Error(Exception): 48 | pass 49 | 50 | 51 | def guess_module(suffix): 52 | if CONTENT_MAPPINGS[suffix] == "application/n-triples": 53 | module = nt 54 | elif CONTENT_MAPPINGS[suffix] == "text/turtle": 55 | module = turtle 56 | elif CONTENT_MAPPINGS[suffix] == "application/rdf+xml": 57 | module = xml 58 | else: 59 | # TODO: Unimplemented error before this 60 | # Raise error, or fallback 61 | raise Error("cannot guess content type from extension") 62 | return module 63 | 64 | 65 | def build_pattern(pattern: str): 66 | new_pattern = [] 67 | for term in pattern: 68 | if type(term) is str and not any( 69 | (is_iri(term), is_blank(term), is_literal(term)) 70 | ): 71 | term = str(IRI(term)) 72 | term = str(term) if term is not None else term 73 | new_pattern.append(term) 74 | return tuple(new_pattern) 75 | 76 | 77 | class Parser: 78 | def __init__(self): 79 | pass 80 | 81 | def parse(self, filelike_or_filename, format=None, base_iri=None): 82 | if isinstance(filelike_or_filename, io.BufferedIOBase): 83 | if format is None: 84 | raise Error("Format must be specified") 85 | parser = guess_module(f".{format}").Parser 86 | else: 87 | path = Path(filelike_or_filename) 88 | parser = guess_module(path.suffix).Parser 89 | if base_iri: 90 | return parser().parse(filelike_or_filename, base_iri) 91 | else: 92 | return parser().parse(filelike_or_filename) 93 | 94 | 95 | class PatternParser: 96 | def __init__(self, pattern): 97 | pattern = build_pattern(pattern) 98 | self.pattern = pattern 99 | 100 | def parse(self, filelike_or_filename, format=None, base_iri=None): 101 | if isinstance(filelike_or_filename, io.BufferedIOBase): 102 | if format is None: 103 | raise Error("Format must be specified") 104 | parser = guess_module(f".{format}").PatternParser 105 | else: 106 | path = Path(filelike_or_filename) 107 | parser = guess_module(path.suffix).PatternParser 108 | if base_iri: 109 | return parser(self.pattern).parse(filelike_or_filename, base_iri) 110 | else: 111 | return parser(self.pattern).parse(filelike_or_filename) 112 | 113 | 114 | class RDFDocument: 115 | def __init__(self, filelike_or_filename, base_iri=None, parser=None): 116 | self.filelike_or_filename = filelike_or_filename 117 | self.base_iri = base_iri 118 | if isinstance(filelike_or_filename, io.BufferedIOBase): 119 | if parser is None: 120 | raise Error("Parser must be specified") 121 | self.parser = parser 122 | else: 123 | if parser is None: 124 | path = Path(filelike_or_filename) 125 | parser = guess_module(path.suffix).PatternParser 126 | self.parser = parser 127 | 128 | def search_triples(self, s, p, o): 129 | pattern = build_pattern((s, p, o)) 130 | if isinstance(self.filelike_or_filename, io.BufferedIOBase): 131 | self.filelike_or_filename.seek(0) 132 | if self.base_iri: 133 | return self.parser(pattern).parse(self.filelike_or_filename, self.base_iri) 134 | else: 135 | return self.parser(pattern).parse(self.filelike_or_filename) 136 | -------------------------------------------------------------------------------- /src/common.rs: -------------------------------------------------------------------------------- 1 | extern crate signal_hook; 2 | use pyo3::create_exception; 3 | use pyo3::exceptions; 4 | use pyo3::prelude::*; 5 | use regex::Regex; 6 | use rio_api::model::{Subject, Term, Triple}; 7 | use rio_turtle::TurtleError; 8 | use rio_xml::RdfXmlError; 9 | use std::convert::From; 10 | use std::error; 11 | use std::fmt; 12 | use std::iter; 13 | use std::sync::atomic::{AtomicBool, Ordering}; 14 | use std::sync::Arc; 15 | 16 | pub type StringTriple = (String, String, String); 17 | pub type TriplePattern = (Option, Option, Option); 18 | pub type RegexTriplePattern = (Option, Option, Option); 19 | 20 | // // https://docs.rs/rio_turtle/0.4.0/src/rio_turtle/error.rs.html 21 | // create_exception!(turtle, IO, exceptions::Exception); 22 | // create_exception!(turtle, UnknownPrefix, exceptions::Exception); 23 | // create_exception!(turtle, PrematureEOF, exceptions::Exception); 24 | // create_exception!(turtle, UnexpectedByte, exceptions::Exception); 25 | // create_exception!(turtle, InvalidUnicodeCodePoint, exceptions::Exception); 26 | // create_exception!(turtle, InvalidIri, exceptions::Exception); 27 | 28 | // // https://docs.rs/rio_xml/0.2.0/src/rio_xml/error.rs.html 29 | // create_exception!(xml, Xml, exceptions::Exception); 30 | // create_exception!(xml, InvalidIri, exceptions::Exception); 31 | // create_exception!(xml, Other, exceptions::Exception); 32 | 33 | create_exception!(lightrdf, Error, exceptions::PyException); 34 | 35 | #[derive(Debug)] 36 | pub enum ParserError { 37 | TurtleError(TurtleError), 38 | RdfXmlError(RdfXmlError), 39 | } 40 | 41 | impl fmt::Display for ParserError { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | match *self { 44 | ParserError::TurtleError(ref e) => write!(f, "{}", e), 45 | ParserError::RdfXmlError(ref e) => write!(f, "{}", e), 46 | } 47 | } 48 | } 49 | 50 | impl error::Error for ParserError {} 51 | 52 | impl From for ParserError { 53 | fn from(e: TurtleError) -> Self { 54 | match &e { 55 | _ => ParserError::TurtleError(e), 56 | } 57 | } 58 | } 59 | 60 | impl From for ParserError { 61 | fn from(e: RdfXmlError) -> Self { 62 | match &e { 63 | _ => ParserError::RdfXmlError(e), 64 | } 65 | } 66 | } 67 | 68 | pub fn triple_to_striple(t: Triple) -> StringTriple { 69 | let subj = match t.subject { 70 | Subject::NamedNode(node) => node.to_string(), 71 | Subject::BlankNode(node) => node.to_string(), 72 | Subject::Triple(triple) => triple.to_string(), // TODO: Handle RDF-star 73 | }; 74 | let pred = t.predicate.to_string(); 75 | let obj = match t.object { 76 | Term::NamedNode(node) => node.to_string(), 77 | Term::BlankNode(node) => node.to_string(), 78 | Term::Literal(literal) => literal.to_string(), 79 | Term::Triple(triple) => triple.to_string(), // TODO: Handle RDF-star 80 | }; 81 | (subj, pred, obj) as StringTriple 82 | } 83 | 84 | #[pyclass] 85 | pub struct TriplesIterator { 86 | pub it: Box> + Send>, 87 | pub pattern: RegexTriplePattern, 88 | pub term: Arc, 89 | } 90 | 91 | #[pymethods] 92 | impl TriplesIterator { 93 | fn __iter__(slf: PyRefMut) -> PyResult { 94 | let py = unsafe { Python::assume_gil_acquired() }; 95 | signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&slf.term))?; 96 | Ok(slf.into_py(py)) 97 | } 98 | 99 | fn __next__(mut slf: PyRefMut) -> PyResult> { 100 | while !slf.term.load(Ordering::Relaxed) { 101 | match slf.it.next() { 102 | Some(Ok(t)) => { 103 | if (slf.pattern.0.is_some() && !slf.pattern.0.as_ref().unwrap().is_match(&t.0)) 104 | || (slf.pattern.1.is_some() 105 | && !slf.pattern.1.as_ref().unwrap().is_match(&t.1)) 106 | || (slf.pattern.2.is_some() 107 | && !slf.pattern.2.as_ref().unwrap().is_match(&t.2)) 108 | { 109 | continue; 110 | } 111 | return Ok(Some(t)); 112 | } 113 | Some(Err(e)) => { 114 | // Rio can recover from error in case of ntriples/nquads 115 | // continue; 116 | return match e { 117 | ParserError::TurtleError(_) => Err(Error::new_err(e.to_string())), 118 | ParserError::RdfXmlError(_) => Err(Error::new_err(e.to_string())), 119 | }; 120 | } 121 | _ => { 122 | return Err(exceptions::PyStopIteration::new_err("")); 123 | } 124 | } 125 | } 126 | Err(exceptions::PyKeyboardInterrupt::new_err("")) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # This file is autogenerated by maturin v1.2.3 2 | # To update, run 3 | # 4 | # maturin generate-ci github 5 | # 6 | name: CI 7 | 8 | on: 9 | push: 10 | branches: 11 | - main 12 | - master 13 | tags: 14 | - '*' 15 | pull_request: 16 | workflow_dispatch: 17 | 18 | permissions: 19 | contents: read 20 | 21 | jobs: 22 | # https://srz-zumix.blogspot.com/2019/10/github-actions-ci-skip.html 23 | prepare: 24 | runs-on: ubuntu-latest 25 | if: "! contains(github.event.head_commit.message, '[skip ci]')" 26 | steps: 27 | - run: echo "[skip ci] ${{ contains(github.event.head_commit.message, '[skip ci]') }}" 28 | 29 | lint: 30 | runs-on: ubuntu-latest 31 | needs: [prepare] 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v2 35 | 36 | - name: Install latest stable 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | toolchain: stable 40 | override: true 41 | components: rustfmt 42 | 43 | - name: Lint with rustfmt 44 | uses: actions-rs/cargo@v1 45 | with: 46 | command: fmt 47 | 48 | # - name: Lint with clippy 49 | # uses: actions-rs/cargo@v1 50 | # with: 51 | # command: clippy 52 | # args: --all-targets --all-features 53 | 54 | # - name: Test with cargo 55 | # uses: actions-rs/cargo@v1.0.1 56 | # with: 57 | # command: test 58 | # toolchain: nightly 59 | 60 | linux: 61 | runs-on: ubuntu-latest 62 | needs: [prepare] 63 | strategy: 64 | matrix: 65 | # target: [x86_64, x86, aarch64, armv7, s390x, ppc64le] 66 | target: [x86_64, aarch64] 67 | steps: 68 | - uses: actions/checkout@v3 69 | - uses: actions/setup-python@v4 70 | with: 71 | python-version: '3.10' 72 | - name: Build wheels 73 | uses: PyO3/maturin-action@v1 74 | with: 75 | target: ${{ matrix.target }} 76 | args: --release --out dist --find-interpreter 77 | sccache: 'true' 78 | manylinux: auto 79 | - name: Run tests 80 | # NOTE: aarch64 is built on docker, so we cannot run tests on it 81 | if: "matrix.target != 'aarch64'" 82 | shell: bash 83 | # TODO: Run on all Python versions 84 | run: | 85 | python3.10 -m pip install --upgrade pip 86 | python3.10 -m pip install pytest~=7.4.2 87 | python3.10 -m pip install dist/*cp310*${{ matrix.wheel_arch || matrix.target }}*.whl 88 | cd tests/ 89 | python3.10 -m pytest . 90 | - name: Upload wheels 91 | uses: actions/upload-artifact@v3 92 | with: 93 | name: wheels 94 | path: dist 95 | 96 | windows: 97 | runs-on: windows-latest 98 | needs: [prepare] 99 | strategy: 100 | matrix: 101 | # target: [x64, x86] 102 | target: [x64] 103 | include: 104 | - target: x64 105 | wheel_arch: amd64 106 | steps: 107 | - uses: actions/checkout@v3 108 | - uses: actions/setup-python@v4 109 | with: 110 | python-version: '3.10' 111 | architecture: ${{ matrix.target }} 112 | - name: Build wheels 113 | uses: PyO3/maturin-action@v1 114 | with: 115 | target: ${{ matrix.target }} 116 | args: --release --out dist --find-interpreter 117 | sccache: 'true' 118 | - name: Run tests 119 | shell: bash 120 | # TODO: Run on all Python versions 121 | # NOTE: There is no versioned Python executables on Windows 122 | run: | 123 | python -m pip install --upgrade pip 124 | python -m pip install pytest~=7.4.2 125 | python -m pip install dist/*cp310*${{ matrix.wheel_arch || matrix.target }}*.whl 126 | cd tests/ 127 | python -m pytest . 128 | - name: Upload wheels 129 | uses: actions/upload-artifact@v3 130 | with: 131 | name: wheels 132 | path: dist 133 | 134 | macos: 135 | runs-on: macos-latest 136 | needs: [prepare] 137 | strategy: 138 | matrix: 139 | target: [x86_64, aarch64] 140 | include: 141 | - target: aarch64 142 | wheel_arch: arm64 143 | steps: 144 | - uses: actions/checkout@v3 145 | - uses: actions/setup-python@v4 146 | with: 147 | python-version: '3.10' 148 | - name: Build wheels 149 | uses: PyO3/maturin-action@v1 150 | with: 151 | target: ${{ matrix.target }} 152 | args: --release --out dist --find-interpreter 153 | sccache: 'true' 154 | - name: Run tests 155 | # NOTE: We cannnot run tests for aarch64 (arm64) 156 | if: "matrix.target != 'aarch64'" 157 | shell: bash 158 | # TODO: Run on all Python versions 159 | run: | 160 | python3.10 -m pip install --upgrade pip 161 | python3.10 -m pip install pytest~=7.4.2 162 | python3.10 -m pip install dist/*cp310*${{ matrix.wheel_arch || matrix.target }}*.whl 163 | cd tests/ 164 | python3.10 -m pytest . 165 | - name: Upload wheels 166 | uses: actions/upload-artifact@v3 167 | with: 168 | name: wheels 169 | path: dist 170 | 171 | sdist: 172 | runs-on: ubuntu-latest 173 | needs: [prepare] 174 | steps: 175 | - uses: actions/checkout@v3 176 | - name: Build sdist 177 | uses: PyO3/maturin-action@v1 178 | with: 179 | command: sdist 180 | args: --out dist 181 | - name: Upload sdist 182 | uses: actions/upload-artifact@v3 183 | with: 184 | name: wheels 185 | path: dist 186 | 187 | release: 188 | name: Release 189 | runs-on: ubuntu-latest 190 | if: "startsWith(github.ref, 'refs/tags/')" 191 | needs: [linux, windows, macos, sdist] 192 | steps: 193 | - uses: actions/download-artifact@v3 194 | with: 195 | name: wheels 196 | - name: Publish to PyPI 197 | uses: PyO3/maturin-action@v1 198 | env: 199 | MATURIN_PYPI_TOKEN: ${{ secrets.PYPI }} 200 | with: 201 | command: upload 202 | args: --non-interactive --skip-existing * 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LightRDF 2 | 3 | [![](https://github.com/ozekik/lightrdf/workflows/CI/badge.svg)](https://github.com/ozekik/lightrdf/actions) 4 | [![PyPI](https://img.shields.io/pypi/v/lightrdf.svg)](https://pypi.python.org/pypi/lightrdf/) 5 | [![PyPI - Downloads](https://img.shields.io/pypi/dm/lightrdf.svg)](https://pypistats.org/packages/lightrdf) 6 | 7 | A fast and lightweight Python RDF parser which wraps bindings to Rust's [Rio](https://github.com/Tpt/rio) using [PyO3](https://github.com/PyO3/pyo3). 8 | 9 | ## Contents 10 | 11 | - [Features](#features) 12 | - [Install](#install) 13 | - [Basic Usage](#basic-usage) 14 | - [Iterate over all triples](#iterate-over-all-triples) 15 | - [Search triples with a triple pattern](#search-triples-with-a-triple-pattern) 16 | - [Search triples with a triple pattern (Regex)](#search-triples-with-a-triple-pattern-regex) 17 | 18 | - [Load file objects / texts](#load-file-objects--parse-texts) 19 | - [Benchmark (WIP)](#benchmark-wip) 20 | - [Alternatives](#alternatives) 21 | - [Todo](#todo) 22 | - [License](#license) 23 | 24 | ## Features 25 | 26 | - Supports N-Triples, Turtle, and RDF/XML 27 | - Handles large-size RDF documents 28 | - Simple interfaces for parsing and searching triples 29 | - Support regex in triple patterns 30 | 31 | ## Install 32 | 33 | ``` 34 | pip install lightrdf 35 | ``` 36 | 37 | ## Basic Usage 38 | 39 | ### Iterate over all triples 40 | 41 | With `Parser`: 42 | 43 | ```python 44 | import lightrdf 45 | 46 | parser = lightrdf.Parser() 47 | 48 | for triple in parser.parse("./go.owl", base_iri=None): 49 | print(triple) 50 | ``` 51 | 52 | With `RDFDocument`: 53 | 54 | ```python 55 | import lightrdf 56 | 57 | doc = lightrdf.RDFDocument("./go.owl") 58 | 59 | # `None` matches arbitrary term 60 | for triple in doc.search_triples(None, None, None): 61 | print(triple) 62 | ``` 63 | 64 | ### Search triples with a triple pattern 65 | 66 | ```python 67 | import lightrdf 68 | 69 | doc = lightrdf.RDFDocument("./go.owl") 70 | 71 | for triple in doc.search_triples("http://purl.obolibrary.org/obo/GO_0005840", None, None): 72 | print(triple) 73 | 74 | # Output: 75 | # ('', '', '') 76 | # ('', '', '') 77 | # ... 78 | # ('', '', '') 79 | # ('', '', '"ribosome"^^') 80 | ``` 81 | 82 | ### Search triples with a triple pattern (Regex) 83 | 84 | ```python 85 | import lightrdf 86 | from lightrdf import Regex 87 | 88 | doc = lightrdf.RDFDocument("./go.owl") 89 | 90 | for triple in doc.search_triples(Regex("^$"), None, Regex(".*amino[\w]+?transferase")): 91 | print(triple) 92 | 93 | # Output: 94 | # ('', '', '"O-acetylhomoserine aminocarboxypropyltransferase activity"^^') 95 | # ('', '', '"S-aminomethyldihydrolipoylprotein:(6S)-tetrahydrofolate aminomethyltransferase (ammonia-forming) activity"^^') 96 | # ... 97 | # ('', '', '"zeatin 9-aminocarboxyethyltransferase activity"^^') 98 | # ('', '', '"spermidine:putrescine 4-aminobutyltransferase (propane-1,3-diamine-forming)"^^') 99 | ``` 100 | 101 | 113 | 114 | ### Load file objects / texts 115 | 116 | Load file objects with `Parser`: 117 | 118 | ```python 119 | import lightrdf 120 | 121 | parser = lightrdf.Parser() 122 | 123 | with open("./go.owl", "rb") as f: 124 | for triple in parser.parse(f, format="owl", base_iri=None): 125 | print(triple) 126 | ``` 127 | 128 | Load file objects with `RDFDocument`: 129 | 130 | ```python 131 | import lightrdf 132 | 133 | with open("./go.owl", "rb") as f: 134 | doc = lightrdf.RDFDocument(f, parser=lightrdf.xml.PatternParser) 135 | 136 | for triple in doc.search_triples("http://purl.obolibrary.org/obo/GO_0005840", None, None): 137 | print(triple) 138 | ``` 139 | 140 | Load texts: 141 | 142 | ```python 143 | import io 144 | import lightrdf 145 | 146 | data = """ . 147 | _:subject1 "object1" . 148 | _:subject2 "object2" .""" 149 | 150 | doc = lightrdf.RDFDocument(io.BytesIO(data.encode()), parser=lightrdf.turtle.PatternParser) 151 | 152 | for triple in doc.search_triples("http://one.example/subject1", None, None): 153 | print(triple) 154 | ``` 155 | 156 | ## Benchmark (WIP) 157 | 158 | > On MacBook Air (13-inch, 2017), 1.8 GHz Intel Core i5, 8 GB 1600 MHz DDR3 159 | 160 | 161 | 162 | ```bash 163 | $ wget -q http://purl.obolibrary.org/obo/go.owl 164 | $ gtime python3 count_triples_rdflib_graph.py ./go.owl # RDFLib 4.2.2 165 | 1436427 166 | 235.29user 2.30system 3:59.56elapsed 99%CPU (0avgtext+0avgdata 1055816maxresident)k 167 | 0inputs+0outputs (283major+347896minor)pagefaults 0swaps 168 | $ gtime python3 count_triples_lightrdf_rdfdocument.py ./go.owl # LightRDF 0.1.1 169 | 1436427 170 | 7.90user 0.22system 0:08.27elapsed 98%CPU (0avgtext+0avgdata 163760maxresident)k 171 | 0inputs+0outputs (106major+41389minor)pagefaults 0swaps 172 | $ gtime python3 count_triples_lightrdf_parser.py ./go.owl # LightRDF 0.1.1 173 | 1436427 174 | 8.00user 0.24system 0:08.47elapsed 97%CPU (0avgtext+0avgdata 163748maxresident)k 175 | 0inputs+0outputs (106major+41388minor)pagefaults 0swaps 176 | ``` 177 | 178 |

179 | 180 |

181 | 182 | 183 | 184 | ```bash 185 | $ wget -q http://downloads.dbpedia.org/2016-10/dbpedia_2016-10.nt 186 | $ gtime python3 count_triples_rdflib_ntparser.py dbpedia_2016-10.nt # RDFLib 4.2.2 187 | 31050 188 | 1.63user 0.23system 0:02.47elapsed 75%CPU (0avgtext+0avgdata 26568maxresident)k 189 | 0inputs+0outputs (1140major+6118minor)pagefaults 0swaps 190 | $ gtime python3 count_triples_lightrdf_ntparser.py dbpedia_2016-10.nt # LightRDF 0.1.1 191 | 31050 192 | 0.21user 0.04system 0:00.36elapsed 71%CPU (0avgtext+0avgdata 7628maxresident)k 193 | 0inputs+0outputs (534major+1925minor)pagefaults 0swaps 194 | ``` 195 | 196 |

197 | 198 |

199 | 200 | ## Alternatives 201 | 202 | - [RDFLib](https://github.com/RDFLib/rdflib) – (Pros) pure-Python, matured, feature-rich / (Cons) takes some time to load triples 203 | - [pyHDT](https://github.com/Callidon/pyHDT) – (Pros) extremely fast and efficient / (Cons) requires pre-conversion into HDT 204 | 205 | ## Todo 206 | 207 | - [x] Push to PyPI 208 | - [x] Adopt CI 209 | - [x] Handle Base IRI 210 | - [x] Add basic tests 211 | - [ ] Switch to maturin-action from cibuildwheel 212 | - [ ] Support NQuads and TriG 213 | - [ ] Add docs 214 | - [ ] Add tests for [w3c/rdf-tests](https://github.com/w3c/rdf-tests) 215 | - [ ] Resume on error 216 | - [x] Allow opening fp 217 | - [ ] Support and test on RDF-star 218 | 219 | ## License 220 | 221 | [Rio](https://github.com/Tpt/rio) and [PyO3](https://github.com/PyO3/pyo3) are licensed under the Apache-2.0 license. 222 | 223 | Copyright 2020 Kentaro Ozeki 224 | 225 | Licensed under the Apache License, Version 2.0 (the "License"); 226 | you may not use this file except in compliance with the License. 227 | You may obtain a copy of the License at 228 | 229 | http://www.apache.org/licenses/LICENSE-2.0 230 | 231 | Unless required by applicable law or agreed to in writing, software 232 | distributed under the License is distributed on an "AS IS" BASIS, 233 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 234 | See the License for the specific language governing permissions and 235 | limitations under the License. 236 | -------------------------------------------------------------------------------- /tests/test_lightrdf.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | from pathlib import Path 4 | 5 | import lightrdf 6 | import pytest 7 | 8 | current_dir = os.path.abspath(os.path.dirname(__file__)) 9 | 10 | # fmt: off 11 | testcases = [ 12 | ("fao.nt", [ 13 | ('', '', ''), 14 | ('', '', ''), 15 | ('', '', '"A hypha that emerges from a yeast-form cell upon stimulation by the pheromone of a compatible mating partner. An example is observed in Cryptococcus species."'), 16 | ('', '', '"midori"'), 17 | ('', '', '"2020-01-23T16:29:54Z"'), 18 | ('', '', '"fungal_anatomy_ontology"'), 19 | ('', '', '"FAO:0002011"'), 20 | ('', '', '"mating filament"'), 21 | ('_:Bf2541d9418081488bf51b62519d99390', '', ''), 22 | ('_:Bf2541d9418081488bf51b62519d99390', '', ''), 23 | ('_:Bf2541d9418081488bf51b62519d99390', '', ''), 24 | ('_:Bf2541d9418081488bf51b62519d99390', '', '"A hypha that emerges from a yeast-form cell upon stimulation by the pheromone of a compatible mating partner. An example is observed in Cryptococcus species."'), 25 | ('_:Bf2541d9418081488bf51b62519d99390', '', '"FAO:doi"'), 26 | ('', '', ''), 27 | ]), 28 | ("fao.ttl", [ 29 | ('', '', '"mating filament"^^'), 30 | ('', '', '"FAO:0002011"^^'), 31 | ('', '', '"fungal_anatomy_ontology"^^'), 32 | ('', '', '"2020-01-23T16:29:54Z"^^'), 33 | ('', '', '"midori"^^'), 34 | ('', '', '"A hypha that emerges from a yeast-form cell upon stimulation by the pheromone of a compatible mating partner. An example is observed in Cryptococcus species."^^'), 35 | ('', '', ''), 36 | ('', '', ''), 37 | ('_:riog00000173', '', '"FAO:doi"^^'), 38 | ('_:riog00000173', '', '"A hypha that emerges from a yeast-form cell upon stimulation by the pheromone of a compatible mating partner. An example is observed in Cryptococcus species."^^'), 39 | ('_:riog00000173', '', ''), 40 | ('_:riog00000173', '', ''), 41 | ('_:riog00000173', '', ''), 42 | ('', '', ''), 43 | ]), 44 | ("fao.owl", [ 45 | ('', '', ''), 46 | ('', '', ''), 47 | ('', '', '"A hypha that emerges from a yeast-form cell upon stimulation by the pheromone of a compatible mating partner. An example is observed in Cryptococcus species."^^'), 48 | ('', '', '"midori"^^'), 49 | ('', '', '"2020-01-23T16:29:54Z"^^'), 50 | ('', '', '"fungal_anatomy_ontology"^^'), 51 | ('', '', '"FAO:0002011"^^'), 52 | ('', '', '"mating filament"^^'), 53 | ('_:riog00000314', '', ''), 54 | ('_:riog00000314', '', ''), 55 | ('_:riog00000314', '', ''), 56 | ('_:riog00000314', '', '"A hypha that emerges from a yeast-form cell upon stimulation by the pheromone of a compatible mating partner. An example is observed in Cryptococcus species."^^'), 57 | ('_:riog00000314', '', '"FAO:doi"^^'), 58 | ('', '', ''), 59 | ]), 60 | ] 61 | # fmt: on 62 | 63 | # fmt: off 64 | testcases_pattern = [ 65 | ("fao.nt", [ 66 | ('', '', ''), 67 | ]), 68 | ("fao.ttl", [ 69 | ('', '', ''), 70 | ]), 71 | ("fao.owl", [ 72 | ('', '', ''), 73 | ]), 74 | ] 75 | # fmt: on 76 | 77 | 78 | @pytest.mark.parametrize("filename,expected", testcases) 79 | def test_parser_from_filename(filename, expected): 80 | path = os.path.join(current_dir, filename) 81 | parser = lightrdf.Parser() 82 | triples = [] 83 | for triple in parser.parse(path): 84 | triples.append(triple) 85 | assert len(triples) == 1840 and triples[-len(expected) :] == expected 86 | 87 | 88 | @pytest.mark.parametrize("filename,expected", testcases) 89 | def test_parser(filename, expected): 90 | path = Path(os.path.join(current_dir, filename)) 91 | with open(path, "rb") as f: 92 | file = io.BytesIO(f.read()) 93 | parser = lightrdf.Parser() 94 | triples = [] 95 | for triple in parser.parse(file, format=path.suffix.lstrip(".")): 96 | triples.append(triple) 97 | assert len(triples) == 1840 and triples[-len(expected) :] == expected 98 | 99 | 100 | @pytest.mark.parametrize("filename,expected", testcases_pattern) 101 | def test_pattern_parser_from_filename(filename, expected): 102 | path = os.path.join(current_dir, filename) 103 | parser = lightrdf.PatternParser( 104 | ( 105 | "http://purl.obolibrary.org/obo/FAO_0002011", 106 | "http://www.w3.org/2000/01/rdf-schema#subClassOf", 107 | None, 108 | ) 109 | ) 110 | triples = [] 111 | for triple in parser.parse(path): 112 | triples.append(triple) 113 | assert triples == expected 114 | 115 | 116 | @pytest.mark.parametrize("filename,expected", testcases_pattern) 117 | def test_pattern_parser(filename, expected): 118 | path = Path(os.path.join(current_dir, filename)) 119 | with open(path, "rb") as f: 120 | file = io.BytesIO(f.read()) 121 | parser = lightrdf.PatternParser( 122 | ( 123 | "http://purl.obolibrary.org/obo/FAO_0002011", 124 | "http://www.w3.org/2000/01/rdf-schema#subClassOf", 125 | None, 126 | ) 127 | ) 128 | triples = [] 129 | for triple in parser.parse(file, format=path.suffix.lstrip(".")): 130 | triples.append(triple) 131 | assert triples == expected 132 | 133 | 134 | @pytest.mark.parametrize("filename,expected", testcases_pattern) 135 | def test_rdf_document_from_filename(filename, expected): 136 | path = os.path.join(current_dir, filename) 137 | doc = lightrdf.RDFDocument(path) 138 | triples = [] 139 | pattern = ( 140 | "http://purl.obolibrary.org/obo/FAO_0002011", 141 | "http://www.w3.org/2000/01/rdf-schema#subClassOf", 142 | None, 143 | ) 144 | for triple in doc.search_triples(*pattern): 145 | triples.append(triple) 146 | # Repeat 147 | for triple in doc.search_triples(*pattern): 148 | triples.append(triple) 149 | assert triples == expected * 2 150 | 151 | 152 | @pytest.mark.parametrize("filename,expected", testcases_pattern) 153 | def test_rdf_document(filename, expected): 154 | path = Path(os.path.join(current_dir, filename)) 155 | with open(path, "rb") as f: 156 | file = io.BytesIO(f.read()) 157 | if path.suffix == ".nt": 158 | parser = lightrdf.nt.PatternParser 159 | elif path.suffix == ".ttl": 160 | parser = lightrdf.turtle.PatternParser 161 | elif path.suffix == ".owl": 162 | parser = lightrdf.xml.PatternParser 163 | doc = lightrdf.RDFDocument(file, parser=parser) 164 | triples = [] 165 | pattern = ( 166 | "http://purl.obolibrary.org/obo/FAO_0002011", 167 | "http://www.w3.org/2000/01/rdf-schema#subClassOf", 168 | None, 169 | ) 170 | for triple in doc.search_triples(*pattern): 171 | triples.append(triple) 172 | # Repeat 173 | for triple in doc.search_triples(*pattern): 174 | triples.append(triple) 175 | assert triples == expected * 2 176 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "black" 5 | version = "22.12.0" 6 | description = "The uncompromising code formatter." 7 | optional = false 8 | python-versions = ">=3.7" 9 | files = [ 10 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 11 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 12 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 13 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 14 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 15 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 16 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 17 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 18 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 19 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 20 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 21 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 22 | ] 23 | 24 | [package.dependencies] 25 | click = ">=8.0.0" 26 | mypy-extensions = ">=0.4.3" 27 | pathspec = ">=0.9.0" 28 | platformdirs = ">=2" 29 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 30 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 31 | 32 | [package.extras] 33 | colorama = ["colorama (>=0.4.3)"] 34 | d = ["aiohttp (>=3.7.4)"] 35 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 36 | uvloop = ["uvloop (>=0.15.2)"] 37 | 38 | [[package]] 39 | name = "cachetools" 40 | version = "5.3.1" 41 | description = "Extensible memoizing collections and decorators" 42 | optional = false 43 | python-versions = ">=3.7" 44 | files = [ 45 | {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, 46 | {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, 47 | ] 48 | 49 | [[package]] 50 | name = "chardet" 51 | version = "5.2.0" 52 | description = "Universal encoding detector for Python 3" 53 | optional = false 54 | python-versions = ">=3.7" 55 | files = [ 56 | {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, 57 | {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, 58 | ] 59 | 60 | [[package]] 61 | name = "click" 62 | version = "8.1.7" 63 | description = "Composable command line interface toolkit" 64 | optional = false 65 | python-versions = ">=3.7" 66 | files = [ 67 | {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, 68 | {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, 69 | ] 70 | 71 | [package.dependencies] 72 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 73 | 74 | [[package]] 75 | name = "colorama" 76 | version = "0.4.6" 77 | description = "Cross-platform colored terminal text." 78 | optional = false 79 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 80 | files = [ 81 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 82 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 83 | ] 84 | 85 | [[package]] 86 | name = "distlib" 87 | version = "0.3.7" 88 | description = "Distribution utilities" 89 | optional = false 90 | python-versions = "*" 91 | files = [ 92 | {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, 93 | {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, 94 | ] 95 | 96 | [[package]] 97 | name = "exceptiongroup" 98 | version = "1.1.3" 99 | description = "Backport of PEP 654 (exception groups)" 100 | optional = false 101 | python-versions = ">=3.7" 102 | files = [ 103 | {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, 104 | {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, 105 | ] 106 | 107 | [package.extras] 108 | test = ["pytest (>=6)"] 109 | 110 | [[package]] 111 | name = "filelock" 112 | version = "3.12.4" 113 | description = "A platform independent file lock." 114 | optional = false 115 | python-versions = ">=3.8" 116 | files = [ 117 | {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, 118 | {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, 119 | ] 120 | 121 | [package.extras] 122 | docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] 123 | testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] 124 | typing = ["typing-extensions (>=4.7.1)"] 125 | 126 | [[package]] 127 | name = "iniconfig" 128 | version = "2.0.0" 129 | description = "brain-dead simple config-ini parsing" 130 | optional = false 131 | python-versions = ">=3.7" 132 | files = [ 133 | {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, 134 | {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, 135 | ] 136 | 137 | [[package]] 138 | name = "maturin" 139 | version = "1.2.3" 140 | description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" 141 | optional = false 142 | python-versions = ">=3.7" 143 | files = [ 144 | {file = "maturin-1.2.3-py3-none-linux_armv6l.whl", hash = "sha256:7b6484d7c94d6d6188ccf4ed8a6167cb8f1e98f13c653bfa715c9ee9eac4be0c"}, 145 | {file = "maturin-1.2.3-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b44fb4d1d116d69ce7c713c22b322debd5fc222db09eb1cdfa0e1c1b7f3e2e9c"}, 146 | {file = "maturin-1.2.3-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:735375559c8c75bdc910c377f6dcc9197637ee2a312a60e361ef0e08fb31fcb5"}, 147 | {file = "maturin-1.2.3-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:619f4f7b7e3a842a4f6cbae1d138a71d67aeba460f6217b38f2150ad53bb4dc1"}, 148 | {file = "maturin-1.2.3-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:4e1035c102f87aa3e6733d28c2248b7303afa11f93a21f2ac88636e0430b0258"}, 149 | {file = "maturin-1.2.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:aef8ddb9e775dd3781e6f56e10cc3d26f648735723ab5c47ce938542b9b5bbb6"}, 150 | {file = "maturin-1.2.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:23c6fdc5750b96fd10d28c125dd795e9b75cd5cd768c8a403dc91dfde641243a"}, 151 | {file = "maturin-1.2.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:40b4d69f9e5be5eacedd80ae496fae67cfd71d386b5604f7ce2e9ac9d34d0460"}, 152 | {file = "maturin-1.2.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:826e90533789ff6dd3f3f0541fbe46c3549ec985d19edceff7913f9bdf9c3131"}, 153 | {file = "maturin-1.2.3-py3-none-win32.whl", hash = "sha256:e414e56896d904c255e80190ac81fa8299b1d7df52f7e2e3f10df33f92784fd8"}, 154 | {file = "maturin-1.2.3-py3-none-win_amd64.whl", hash = "sha256:1f5516dbe68491bf4bf7e047caf139596a3cd9d4a5ec8bb43034980e3710e550"}, 155 | {file = "maturin-1.2.3-py3-none-win_arm64.whl", hash = "sha256:7d47e9a0fe56d25de98a2bed7d1c75975516e3a25fa5b552b2ee61fb1add41c0"}, 156 | {file = "maturin-1.2.3.tar.gz", hash = "sha256:ef3f42af453d64f233b99543c3001bee645019a9c2022c7972210a9cacb5301f"}, 157 | ] 158 | 159 | [package.dependencies] 160 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 161 | 162 | [package.extras] 163 | patchelf = ["patchelf"] 164 | zig = ["ziglang (>=0.10.0,<0.11.0)"] 165 | 166 | [[package]] 167 | name = "mypy-extensions" 168 | version = "1.0.0" 169 | description = "Type system extensions for programs checked with the mypy type checker." 170 | optional = false 171 | python-versions = ">=3.5" 172 | files = [ 173 | {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, 174 | {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, 175 | ] 176 | 177 | [[package]] 178 | name = "packaging" 179 | version = "23.1" 180 | description = "Core utilities for Python packages" 181 | optional = false 182 | python-versions = ">=3.7" 183 | files = [ 184 | {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, 185 | {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, 186 | ] 187 | 188 | [[package]] 189 | name = "pathspec" 190 | version = "0.11.2" 191 | description = "Utility library for gitignore style pattern matching of file paths." 192 | optional = false 193 | python-versions = ">=3.7" 194 | files = [ 195 | {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, 196 | {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, 197 | ] 198 | 199 | [[package]] 200 | name = "platformdirs" 201 | version = "3.10.0" 202 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 203 | optional = false 204 | python-versions = ">=3.7" 205 | files = [ 206 | {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, 207 | {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, 208 | ] 209 | 210 | [package.extras] 211 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] 212 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] 213 | 214 | [[package]] 215 | name = "pluggy" 216 | version = "1.3.0" 217 | description = "plugin and hook calling mechanisms for python" 218 | optional = false 219 | python-versions = ">=3.8" 220 | files = [ 221 | {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, 222 | {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, 223 | ] 224 | 225 | [package.extras] 226 | dev = ["pre-commit", "tox"] 227 | testing = ["pytest", "pytest-benchmark"] 228 | 229 | [[package]] 230 | name = "pyproject-api" 231 | version = "1.6.1" 232 | description = "API to interact with the python pyproject.toml based projects" 233 | optional = false 234 | python-versions = ">=3.8" 235 | files = [ 236 | {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, 237 | {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, 238 | ] 239 | 240 | [package.dependencies] 241 | packaging = ">=23.1" 242 | tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} 243 | 244 | [package.extras] 245 | docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] 246 | testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] 247 | 248 | [[package]] 249 | name = "pytest" 250 | version = "7.4.2" 251 | description = "pytest: simple powerful testing with Python" 252 | optional = false 253 | python-versions = ">=3.7" 254 | files = [ 255 | {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, 256 | {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, 257 | ] 258 | 259 | [package.dependencies] 260 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 261 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 262 | iniconfig = "*" 263 | packaging = "*" 264 | pluggy = ">=0.12,<2.0" 265 | tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} 266 | 267 | [package.extras] 268 | testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 269 | 270 | [[package]] 271 | name = "tomli" 272 | version = "2.0.1" 273 | description = "A lil' TOML parser" 274 | optional = false 275 | python-versions = ">=3.7" 276 | files = [ 277 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 278 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 279 | ] 280 | 281 | [[package]] 282 | name = "tox" 283 | version = "4.11.3" 284 | description = "tox is a generic virtualenv management and test command line tool" 285 | optional = false 286 | python-versions = ">=3.8" 287 | files = [ 288 | {file = "tox-4.11.3-py3-none-any.whl", hash = "sha256:599af5e5bb0cad0148ac1558a0b66f8fff219ef88363483b8d92a81e4246f28f"}, 289 | {file = "tox-4.11.3.tar.gz", hash = "sha256:5039f68276461fae6a9452a3b2c7295798f00a0e92edcd9a3b78ba1a73577951"}, 290 | ] 291 | 292 | [package.dependencies] 293 | cachetools = ">=5.3.1" 294 | chardet = ">=5.2" 295 | colorama = ">=0.4.6" 296 | filelock = ">=3.12.3" 297 | packaging = ">=23.1" 298 | platformdirs = ">=3.10" 299 | pluggy = ">=1.3" 300 | pyproject-api = ">=1.6.1" 301 | tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} 302 | virtualenv = ">=20.24.3" 303 | 304 | [package.extras] 305 | docs = ["furo (>=2023.8.19)", "sphinx (>=7.2.4)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.24)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] 306 | testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=1)", "diff-cover (>=7.7)", "distlib (>=0.3.7)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.18)", "psutil (>=5.9.5)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.12)", "wheel (>=0.41.2)"] 307 | 308 | [[package]] 309 | name = "typing-extensions" 310 | version = "4.8.0" 311 | description = "Backported and Experimental Type Hints for Python 3.8+" 312 | optional = false 313 | python-versions = ">=3.8" 314 | files = [ 315 | {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, 316 | {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, 317 | ] 318 | 319 | [[package]] 320 | name = "virtualenv" 321 | version = "20.24.5" 322 | description = "Virtual Python Environment builder" 323 | optional = false 324 | python-versions = ">=3.7" 325 | files = [ 326 | {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, 327 | {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, 328 | ] 329 | 330 | [package.dependencies] 331 | distlib = ">=0.3.7,<1" 332 | filelock = ">=3.12.2,<4" 333 | platformdirs = ">=3.9.1,<4" 334 | 335 | [package.extras] 336 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] 337 | test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] 338 | 339 | [metadata] 340 | lock-version = "2.0" 341 | python-versions = "^3.8" 342 | content-hash = "417a18225998f5b95bc70299df76df211484c004787da9b68c407670b22afab7" 343 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "autocfg" 16 | version = "1.1.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 19 | 20 | [[package]] 21 | name = "bitflags" 22 | version = "1.3.2" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "2.4.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" 31 | 32 | [[package]] 33 | name = "bytecount" 34 | version = "0.6.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" 37 | 38 | [[package]] 39 | name = "camino" 40 | version = "1.1.6" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" 43 | dependencies = [ 44 | "serde", 45 | ] 46 | 47 | [[package]] 48 | name = "cargo-platform" 49 | version = "0.1.3" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" 52 | dependencies = [ 53 | "serde", 54 | ] 55 | 56 | [[package]] 57 | name = "cargo_metadata" 58 | version = "0.14.2" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" 61 | dependencies = [ 62 | "camino", 63 | "cargo-platform", 64 | "semver", 65 | "serde", 66 | "serde_json", 67 | ] 68 | 69 | [[package]] 70 | name = "cc" 71 | version = "1.0.83" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 74 | dependencies = [ 75 | "libc", 76 | ] 77 | 78 | [[package]] 79 | name = "cfg-if" 80 | version = "1.0.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 83 | 84 | [[package]] 85 | name = "either" 86 | version = "1.9.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 89 | 90 | [[package]] 91 | name = "errno" 92 | version = "0.3.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" 95 | dependencies = [ 96 | "errno-dragonfly", 97 | "libc", 98 | "windows-sys", 99 | ] 100 | 101 | [[package]] 102 | name = "errno-dragonfly" 103 | version = "0.1.2" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 106 | dependencies = [ 107 | "cc", 108 | "libc", 109 | ] 110 | 111 | [[package]] 112 | name = "error-chain" 113 | version = "0.12.4" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" 116 | dependencies = [ 117 | "version_check", 118 | ] 119 | 120 | [[package]] 121 | name = "fastrand" 122 | version = "2.0.1" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" 125 | 126 | [[package]] 127 | name = "glob" 128 | version = "0.3.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 131 | 132 | [[package]] 133 | name = "indoc" 134 | version = "1.0.9" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" 137 | 138 | [[package]] 139 | name = "itertools" 140 | version = "0.11.0" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" 143 | dependencies = [ 144 | "either", 145 | ] 146 | 147 | [[package]] 148 | name = "itoa" 149 | version = "1.0.9" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 152 | 153 | [[package]] 154 | name = "lazy_static" 155 | version = "1.4.0" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 158 | 159 | [[package]] 160 | name = "libc" 161 | version = "0.2.148" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" 164 | 165 | [[package]] 166 | name = "lightrdf" 167 | version = "0.4.0" 168 | dependencies = [ 169 | "itertools", 170 | "lazy_static", 171 | "oxiri", 172 | "pyo3", 173 | "pyo3-file", 174 | "regex", 175 | "rio_api", 176 | "rio_turtle", 177 | "rio_xml", 178 | "signal-hook", 179 | ] 180 | 181 | [[package]] 182 | name = "linux-raw-sys" 183 | version = "0.4.7" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" 186 | 187 | [[package]] 188 | name = "lock_api" 189 | version = "0.4.10" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" 192 | dependencies = [ 193 | "autocfg", 194 | "scopeguard", 195 | ] 196 | 197 | [[package]] 198 | name = "memchr" 199 | version = "2.6.3" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" 202 | 203 | [[package]] 204 | name = "memoffset" 205 | version = "0.9.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" 208 | dependencies = [ 209 | "autocfg", 210 | ] 211 | 212 | [[package]] 213 | name = "once_cell" 214 | version = "1.18.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 217 | 218 | [[package]] 219 | name = "oxilangtag" 220 | version = "0.1.3" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "8d91edf4fbb970279443471345a4e8c491bf05bb283b3e6c88e4e606fd8c181b" 223 | 224 | [[package]] 225 | name = "oxiri" 226 | version = "0.2.2" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "bb175ec8981211357b7b379869c2f8d555881c55ea62311428ec0de46d89bd5c" 229 | 230 | [[package]] 231 | name = "parking_lot" 232 | version = "0.12.1" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 235 | dependencies = [ 236 | "lock_api", 237 | "parking_lot_core", 238 | ] 239 | 240 | [[package]] 241 | name = "parking_lot_core" 242 | version = "0.9.8" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" 245 | dependencies = [ 246 | "cfg-if", 247 | "libc", 248 | "redox_syscall", 249 | "smallvec", 250 | "windows-targets", 251 | ] 252 | 253 | [[package]] 254 | name = "proc-macro2" 255 | version = "1.0.67" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" 258 | dependencies = [ 259 | "unicode-ident", 260 | ] 261 | 262 | [[package]] 263 | name = "pulldown-cmark" 264 | version = "0.9.3" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" 267 | dependencies = [ 268 | "bitflags 1.3.2", 269 | "memchr", 270 | "unicase", 271 | ] 272 | 273 | [[package]] 274 | name = "pyo3" 275 | version = "0.19.2" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "e681a6cfdc4adcc93b4d3cf993749a4552018ee0a9b65fc0ccfad74352c72a38" 278 | dependencies = [ 279 | "cfg-if", 280 | "indoc", 281 | "libc", 282 | "memoffset", 283 | "parking_lot", 284 | "pyo3-build-config", 285 | "pyo3-ffi", 286 | "pyo3-macros", 287 | "unindent", 288 | ] 289 | 290 | [[package]] 291 | name = "pyo3-build-config" 292 | version = "0.19.2" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "076c73d0bc438f7a4ef6fdd0c3bb4732149136abd952b110ac93e4edb13a6ba5" 295 | dependencies = [ 296 | "once_cell", 297 | "target-lexicon", 298 | ] 299 | 300 | [[package]] 301 | name = "pyo3-ffi" 302 | version = "0.19.2" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "e53cee42e77ebe256066ba8aa77eff722b3bb91f3419177cf4cd0f304d3284d9" 305 | dependencies = [ 306 | "libc", 307 | "pyo3-build-config", 308 | ] 309 | 310 | [[package]] 311 | name = "pyo3-file" 312 | version = "0.7.0" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "9c655228ceebe0180211c009dade97a1d5f3e86704caec35ddeb2c7661543654" 315 | dependencies = [ 316 | "pyo3", 317 | "skeptic", 318 | ] 319 | 320 | [[package]] 321 | name = "pyo3-macros" 322 | version = "0.19.2" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "dfeb4c99597e136528c6dd7d5e3de5434d1ceaf487436a3f03b2d56b6fc9efd1" 325 | dependencies = [ 326 | "proc-macro2", 327 | "pyo3-macros-backend", 328 | "quote", 329 | "syn 1.0.109", 330 | ] 331 | 332 | [[package]] 333 | name = "pyo3-macros-backend" 334 | version = "0.19.2" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "947dc12175c254889edc0c02e399476c2f652b4b9ebd123aa655c224de259536" 337 | dependencies = [ 338 | "proc-macro2", 339 | "quote", 340 | "syn 1.0.109", 341 | ] 342 | 343 | [[package]] 344 | name = "quick-xml" 345 | version = "0.28.2" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" 348 | dependencies = [ 349 | "memchr", 350 | ] 351 | 352 | [[package]] 353 | name = "quote" 354 | version = "1.0.33" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 357 | dependencies = [ 358 | "proc-macro2", 359 | ] 360 | 361 | [[package]] 362 | name = "redox_syscall" 363 | version = "0.3.5" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" 366 | dependencies = [ 367 | "bitflags 1.3.2", 368 | ] 369 | 370 | [[package]] 371 | name = "regex" 372 | version = "1.9.5" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" 375 | dependencies = [ 376 | "aho-corasick", 377 | "memchr", 378 | "regex-automata", 379 | "regex-syntax", 380 | ] 381 | 382 | [[package]] 383 | name = "regex-automata" 384 | version = "0.3.8" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" 387 | dependencies = [ 388 | "aho-corasick", 389 | "memchr", 390 | "regex-syntax", 391 | ] 392 | 393 | [[package]] 394 | name = "regex-syntax" 395 | version = "0.7.5" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" 398 | 399 | [[package]] 400 | name = "rio_api" 401 | version = "0.8.4" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "1924fa1f0e6d851f9b73b3c569e607c368a0d92995d99d563ad7bf1414696603" 404 | 405 | [[package]] 406 | name = "rio_turtle" 407 | version = "0.8.4" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "5cec59971eafd99b9c7e3544bfcabafea81a7072ac51c9f46985ca0bd7ba6016" 410 | dependencies = [ 411 | "oxilangtag", 412 | "oxiri", 413 | "rio_api", 414 | ] 415 | 416 | [[package]] 417 | name = "rio_xml" 418 | version = "0.8.4" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "d2edda57b877119dc326c612ba822e3ca1ee22bfc86781a4e9dc0884756b58c3" 421 | dependencies = [ 422 | "oxilangtag", 423 | "oxiri", 424 | "quick-xml", 425 | "rio_api", 426 | ] 427 | 428 | [[package]] 429 | name = "rustix" 430 | version = "0.38.14" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" 433 | dependencies = [ 434 | "bitflags 2.4.0", 435 | "errno", 436 | "libc", 437 | "linux-raw-sys", 438 | "windows-sys", 439 | ] 440 | 441 | [[package]] 442 | name = "ryu" 443 | version = "1.0.15" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 446 | 447 | [[package]] 448 | name = "same-file" 449 | version = "1.0.6" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 452 | dependencies = [ 453 | "winapi-util", 454 | ] 455 | 456 | [[package]] 457 | name = "scopeguard" 458 | version = "1.2.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 461 | 462 | [[package]] 463 | name = "semver" 464 | version = "1.0.19" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" 467 | dependencies = [ 468 | "serde", 469 | ] 470 | 471 | [[package]] 472 | name = "serde" 473 | version = "1.0.188" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" 476 | dependencies = [ 477 | "serde_derive", 478 | ] 479 | 480 | [[package]] 481 | name = "serde_derive" 482 | version = "1.0.188" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" 485 | dependencies = [ 486 | "proc-macro2", 487 | "quote", 488 | "syn 2.0.37", 489 | ] 490 | 491 | [[package]] 492 | name = "serde_json" 493 | version = "1.0.107" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" 496 | dependencies = [ 497 | "itoa", 498 | "ryu", 499 | "serde", 500 | ] 501 | 502 | [[package]] 503 | name = "signal-hook" 504 | version = "0.3.17" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 507 | dependencies = [ 508 | "libc", 509 | "signal-hook-registry", 510 | ] 511 | 512 | [[package]] 513 | name = "signal-hook-registry" 514 | version = "1.4.1" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" 517 | dependencies = [ 518 | "libc", 519 | ] 520 | 521 | [[package]] 522 | name = "skeptic" 523 | version = "0.13.7" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" 526 | dependencies = [ 527 | "bytecount", 528 | "cargo_metadata", 529 | "error-chain", 530 | "glob", 531 | "pulldown-cmark", 532 | "tempfile", 533 | "walkdir", 534 | ] 535 | 536 | [[package]] 537 | name = "smallvec" 538 | version = "1.11.1" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" 541 | 542 | [[package]] 543 | name = "syn" 544 | version = "1.0.109" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 547 | dependencies = [ 548 | "proc-macro2", 549 | "quote", 550 | "unicode-ident", 551 | ] 552 | 553 | [[package]] 554 | name = "syn" 555 | version = "2.0.37" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" 558 | dependencies = [ 559 | "proc-macro2", 560 | "quote", 561 | "unicode-ident", 562 | ] 563 | 564 | [[package]] 565 | name = "target-lexicon" 566 | version = "0.12.11" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" 569 | 570 | [[package]] 571 | name = "tempfile" 572 | version = "3.8.0" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" 575 | dependencies = [ 576 | "cfg-if", 577 | "fastrand", 578 | "redox_syscall", 579 | "rustix", 580 | "windows-sys", 581 | ] 582 | 583 | [[package]] 584 | name = "unicase" 585 | version = "2.7.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 588 | dependencies = [ 589 | "version_check", 590 | ] 591 | 592 | [[package]] 593 | name = "unicode-ident" 594 | version = "1.0.12" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 597 | 598 | [[package]] 599 | name = "unindent" 600 | version = "0.1.11" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" 603 | 604 | [[package]] 605 | name = "version_check" 606 | version = "0.9.4" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 609 | 610 | [[package]] 611 | name = "walkdir" 612 | version = "2.4.0" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 615 | dependencies = [ 616 | "same-file", 617 | "winapi-util", 618 | ] 619 | 620 | [[package]] 621 | name = "winapi" 622 | version = "0.3.9" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 625 | dependencies = [ 626 | "winapi-i686-pc-windows-gnu", 627 | "winapi-x86_64-pc-windows-gnu", 628 | ] 629 | 630 | [[package]] 631 | name = "winapi-i686-pc-windows-gnu" 632 | version = "0.4.0" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 635 | 636 | [[package]] 637 | name = "winapi-util" 638 | version = "0.1.6" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 641 | dependencies = [ 642 | "winapi", 643 | ] 644 | 645 | [[package]] 646 | name = "winapi-x86_64-pc-windows-gnu" 647 | version = "0.4.0" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 650 | 651 | [[package]] 652 | name = "windows-sys" 653 | version = "0.48.0" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 656 | dependencies = [ 657 | "windows-targets", 658 | ] 659 | 660 | [[package]] 661 | name = "windows-targets" 662 | version = "0.48.5" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 665 | dependencies = [ 666 | "windows_aarch64_gnullvm", 667 | "windows_aarch64_msvc", 668 | "windows_i686_gnu", 669 | "windows_i686_msvc", 670 | "windows_x86_64_gnu", 671 | "windows_x86_64_gnullvm", 672 | "windows_x86_64_msvc", 673 | ] 674 | 675 | [[package]] 676 | name = "windows_aarch64_gnullvm" 677 | version = "0.48.5" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 680 | 681 | [[package]] 682 | name = "windows_aarch64_msvc" 683 | version = "0.48.5" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 686 | 687 | [[package]] 688 | name = "windows_i686_gnu" 689 | version = "0.48.5" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 692 | 693 | [[package]] 694 | name = "windows_i686_msvc" 695 | version = "0.48.5" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 698 | 699 | [[package]] 700 | name = "windows_x86_64_gnu" 701 | version = "0.48.5" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 704 | 705 | [[package]] 706 | name = "windows_x86_64_gnullvm" 707 | version = "0.48.5" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 710 | 711 | [[package]] 712 | name = "windows_x86_64_msvc" 713 | version = "0.48.5" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 716 | --------------------------------------------------------------------------------