Skip to content

Commit

Permalink
Improve detection of RDF/XML files when loading unknown content - Fixes
Browse files Browse the repository at this point in the history
#98

Imported type stubs and resolved ALL MyPy issues! (this was a big effort)
Logic fixes in the dataset loader (thanks to inconsistencies exposed by MyPy)
Added ability to pass a TextIO or TextIOWrapper object into the dataset loader
Bump black version
Bump version to v0.17.1
  • Loading branch information
ashleysommer committed Oct 11, 2021
1 parent d0e5f71 commit cc7863c
Show file tree
Hide file tree
Showing 12 changed files with 352 additions and 165 deletions.
27 changes: 26 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Python PEP 440 Versioning](https://www.python.org/dev/peps/pep-0440/).

## [Unreleased]

- Nothing yet


## [0.17.1] - 2021-10-11

## Fixes
- Handle transitive subclasses when evaluating sh:targetClass - @gtfierro
- Fixes #96
- Improve detection of RDF/XML files when loading unknown content
- Fixes #98
- Imported type stubs and resolved ALL MyPy issues! (this was a big effort)
- Logic fixes in the dataset loader (thanks to inconsistencies exposed by MyPy)

## Changed
- Add special cases to sh:dataclass constraint, when the given shape uses rdfs:Literal or rdfs:Dataclass as the dataclass to match on
- Fixes #71

## Added
- Add datashapes.org/schema as a built-in graph
- Fixes #98
- Added ability to pass a TextIO or TextIOWrapper object into the dataset loader

## [0.17.0.post1] - 2021-09-15

## Notice
Expand Down Expand Up @@ -828,7 +852,8 @@ just leaves the files open. Now it is up to the command-line client to close the

- Initial version, limited functionality

[Unreleased]: https://github.com/RDFLib/pySHACL/compare/v0.17.0.post1...HEAD
[Unreleased]: https://github.com/RDFLib/pySHACL/compare/v0.17.1...HEAD
[0.17.1]: https://github.com/RDFLib/pySHACL/compare/v0.17.0.post1...v0.17.1
[0.17.0.post1]: https://github.com/RDFLib/pySHACL/compare/v0.17.0...v0.17.0.post1
[0.17.0]: https://github.com/RDFLib/pySHACL/compare/v0.16.2...v0.17.0
[0.16.2]: https://github.com/RDFLib/pySHACL/compare/v0.16.1...v0.16.2
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ authors:
given-names: "Nicholas"
orcid: "http://orcid.org/0000-0002-8742-7730"
title: "pySHACL"
version: 0.17.0
version: 0.17.1
doi: 10.5281/zenodo.4750840
license: Apache-2.0
date-released: 2021-07-20
date-released: 2021-10-11
url: "https://github.com/RDFLib/pySHACL"
140 changes: 76 additions & 64 deletions poetry.lock

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "pyshacl"
version = "0.17.0.post2"
version = "0.17.1"
# Don't forget to change the version number in __init__.py and CITATION.cff along with this one
description = "Python SHACL Validator"
license = "Apache-2.0"
Expand Down Expand Up @@ -39,6 +39,7 @@ packages = [

include = [
{ path = "pyshacl/assets/*.ttl", format = "sdist" },
{ path = "pyshacl/assets/*.py", format = "sdist" },
{ path = "hooks/*", format = "sdist" },
{ path = "MANIFEST.in", format = "sdist" },
{ path = "pyproject.toml", format = "sdist" },
Expand All @@ -54,28 +55,31 @@ include = [
[tool.poetry.dependencies]
python = "^3.7.0" # Compatible python versions must be declared here
rdflib = ">=6.0.0,<7"
owlrl = "^5.2.3"
owlrl = "^5.2.3,<7"
rdflib-jsonld = { version=">=0.4.0,<0.6", optional=true}
pyduktape2 = {version="^0.4.1", optional=true}
flake8 = {version="^3.8.0", optional=true}
isort = {version="^5.7.0", optional=true}
black = {version="21.8b0", optional=true}
black = {version="21.9b0", optional=true}
mypy = {version="^0.800", optional=true}
types-setuptools = {version="*", optional=true}

[tool.poetry.dev-dependencies]
coverage = "^4.5"
pytest = "^5.0"
pytest-cov = "^2.8.1"
flake8 = {version="^3.8.0", optional=true}
isort = {version="^5.7.0", optional=true}
black = {version="21.8b0", optional=true}
black = {version="21.9b0", optional=true}
mypy = {version="^0.800", optional=true}
types-setuptools = {version="*", optional=true}


[tool.poetry.extras]
jsonld = ["rdflib-jsonld"]
js = ["pyduktape2"]
dev-lint = ["isort", "black", "flake8"]
dev-type-checking = ["mypy"]
dev-type-checking = ["mypy", "types-setuptools"]

[tool.poetry.scripts]
pyshacl = "pyshacl.cli:main"
Expand All @@ -85,7 +89,7 @@ from = {format = "poetry", path = "pyproject.toml"}
to = {format = "setuppy", path = "setup.py"}

[tool.black]
required-version = "21.8b0"
required-version = "21.9b0"
line-length = "119"
skip-string-normalization = true
target-version = ['py37']
Expand Down Expand Up @@ -163,7 +167,7 @@ commands =
[testenv:type-checking]
commands_pre =
poetry install -vv -n --no-root --extras "dev-type-checking"
poetry run pip3 install "mypy>=0.800"
poetry run pip3 install "mypy>=0.800" "types-setuptools"
commands =
- poetry show
poetry run python3 -m mypy --ignore-missing-imports pyshacl
Expand Down
2 changes: 1 addition & 1 deletion pyshacl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


# version compliant with https://www.python.org/dev/peps/pep-0440/
__version__ = '0.17.0.post2'
__version__ = '0.17.1'
# Don't forget to change the version number in pyproject.toml and CITATION.cff along with this one

__all__ = ['validate', 'Validator', '__version__', 'Shape', 'ShapesGraph']
8 changes: 4 additions & 4 deletions pyshacl/helper/expression_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def value_nodes_from_path(sg, focus, path_val, target_graph, recursion=0):

def nodes_from_node_expression(
expr, focus_node, data_graph: 'GraphLike', sg: 'ShapesGraph', recurse_depth=0
) -> Union[Set['RDFNode'], List['RDFNode']]:
) -> Union[Set[Union['RDFNode', None]], List[Union['RDFNode', None]]]:
# https://www.w3.org/TR/shacl-af/#node-expressions
if expr == SH_this:
return [focus_node]
Expand All @@ -151,15 +151,15 @@ def nodes_from_node_expression(
if len(unions):
union_list = next(iter(unions))
parts = list(sg.graph.items(union_list))
all_nodes: Set['RDFNode'] = set()
all_nodes: Set[Union['RDFNode', None]] = set()
for p in parts:
new_parts = nodes_from_node_expression(p, focus_node, data_graph, sg, recurse_depth=recurse_depth + 1)
all_nodes = all_nodes.union(new_parts)
return all_nodes
if len(intersections):
inter_list = next(iter(intersections))
parts = list(data_graph.items(inter_list))
inter_nodes: Set[RDFNode] = set()
inter_nodes: Set[Union['RDFNode', None]] = set()
new = True
for p in parts:
new_parts = nodes_from_node_expression(p, focus_node, data_graph, sg, recurse_depth=recurse_depth + 1)
Expand Down Expand Up @@ -221,7 +221,7 @@ def nodes_from_node_expression(
"The SHACLFunction {} was not defined in this SHACL Shapes file.".format(fnexpr)
)
argslist_parts = list(sg.graph.items(fnargslist))
args_sets = [
args_sets: List[Union[List[Union['RDFNode', None]], Set[Union['RDFNode', None]]]] = [
nodes_from_node_expression(p, focus_node, data_graph, sg, recurse_depth=recurse_depth + 1)
for p in argslist_parts
]
Expand Down
10 changes: 10 additions & 0 deletions pyshacl/monkey/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
RDFLIB_421 = LooseVersion("4.2.1")
RDFLIB_500 = LooseVersion("5.0.0")
RDFLIB_600 = LooseVersion("6.0.0")
RDFLIB_602 = LooseVersion("6.0.2")


def rdflib_bool_patch():
Expand Down Expand Up @@ -55,6 +56,12 @@ def __ge__(term, other):
setattr(rdflib.term.Literal, "__le__", __le__)


def empty_iterator():
if False:
# noinspection PyUnreachableCode
yield None # type: ignore[unreachable]


def apply_patches():
if RDFLIB_421 >= RDFLIB_VERSION:
rdflib_term_ge_le_patch()
Expand All @@ -63,4 +70,7 @@ def apply_patches():
if RDFLIB_421 <= RDFLIB_VERSION < RDFLIB_600:
# RDFLib 6.0.0+ comes with its own Memory2 store (called "Memory") by default
plugin.register("default", store.Store, "pyshacl.monkey.memory2", "Memory2")
if RDFLIB_602 == RDFLIB_VERSION:
# Fixes https://github.com/RDFLib/rdflib/pull/1432
setattr(store.Store, "namespaces", empty_iterator)
return True
8 changes: 6 additions & 2 deletions pyshacl/rdfutil/clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,11 @@ def mix_datasets(
base_named_graphs = list(base_ds.contexts())
if target_ds is None:
target_ds = rdflib.Dataset(default_union=default_union)
elif isinstance(target_ds, rdflib.ConjunctiveGraph):
raise RuntimeError("Cannot mix new graphs into a ConjunctiveGraph, use Dataset instead.")
elif target_ds == "inplace":
pass # do nothing here
elif not isinstance(target_ds, (rdflib.Dataset, rdflib.ConjunctiveGraph)):
elif not isinstance(target_ds, rdflib.Dataset):
raise RuntimeError("Cannot mix datasets if target_ds passed in is not a Dataset itself.")
if isinstance(extra_ds, (rdflib.Dataset, rdflib.ConjunctiveGraph)):
mixin_graphs = list(extra_ds.contexts())
Expand Down Expand Up @@ -126,7 +128,9 @@ def mix_graphs(base_graph: GraphLike, extra_graph: GraphLike, target_graph: Opti
:return: The cloned graph with mixed in triples from extra_graph
:rtype: rdflib.Graph
"""
if isinstance(base_graph, (rdflib.ConjunctiveGraph, rdflib.Dataset)):
if isinstance(base_graph, (rdflib.ConjunctiveGraph, rdflib.Dataset)) and isinstance(
target_graph, (rdflib.ConjunctiveGraph, rdflib.Dataset)
):
return mix_datasets(base_graph, extra_graph, target_ds=target_graph)
if target_graph is None:
g = clone_graph(base_graph, target_graph=None, identifier=base_graph.identifier)
Expand Down
Loading

0 comments on commit cc7863c

Please sign in to comment.