From 1426e100c063f0f6627a48204cd1965ae099c628 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 1 Jul 2024 09:59:39 +0100 Subject: [PATCH] Develop (#2) * valid range of "Maximum RAM Percentage" parameter in Validate plugin * edit CHANGELOG * edit parameter validation * edits * move cleanup to utils.py, annotate inferred axioms not advanced parameter * remove "annotate inferred axioms" parameter, edit README --- CHANGELOG.md | 10 ++++ README-public.md | 2 +- README.md | 57 +++++++++++++++++-- cmem_plugin_reason/plugin_reason.py | 59 ++++--------------- cmem_plugin_reason/plugin_validate.py | 62 ++++++++------------ cmem_plugin_reason/utils.py | 20 ++++++- poetry.lock | 82 +++++++++++++-------------- 7 files changed, 156 insertions(+), 136 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e481e71..092309d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/) +## [Unreleased] + +### Fixed + +- valid range of "Maximum RAM Percentage" parameter in Validate plugin (1-100) + +### Changed + +- complete validation for IRI parameters +- Remove "Annnotate inferred subclass axioms" parameter ## [1.0.0alpha3] 2024-06-28 diff --git a/README-public.md b/README-public.md index b6d5a71..4500bbf 100644 --- a/README-public.md +++ b/README-public.md @@ -1,6 +1,6 @@ # cmem-plugin-reason -This [eccenca](https://eccenca.com) [Corporate Memory](https://documentation.eccenca.com) workflow plugin performs reasoning using [ROBOT](http://robot.obolibrary.org/). It takes an OWL ontology and a data graph as inputs and writes the reasoning result to a specified graph. +This [eccenca](https://eccenca.com) [Corporate Memory](https://documentation.eccenca.com) workflow plugin performs reasoning using [ROBOT](http://robot.obolibrary.org/). ROBOT is published under the [BSD 3-Clause "New" or "Revised" License](https://choosealicense.com/licenses/bsd-3-clause/). Copyright © 2015, the Authors diff --git a/README.md b/README.md index dc019ad..370a27b 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ # cmem-plugin-reason -Reasoning with ROBOT - -This eccenca Corporate Memory workflow plugin performs reasoning using [ROBOT](http://robot.obolibrary.org/). It takes an OWL ontology and a data graph as inputs and writes the reasoning result to a specified graph. +This [eccenca](https://eccenca.com) [Corporate Memory](https://documentation.eccenca.com) workflow plugin bundle contains plugins performing reasoning (Reason) and ontology consistency checking (Validate) using [ROBOT](http://robot.obolibrary.org/). [![eccenca Corporate Memory](https://img.shields.io/badge/eccenca-Corporate%20Memory-orange)](https://documentation.eccenca.com) [![workflow](https://github.com/eccenca/cmem-plugin-pyshacl/actions/workflows/check.yml/badge.svg)](https://github.com/eccenca/cmem-plugin-pyshacl/actions) [![pypi version](https://img.shields.io/pypi/v/cmem-plugin-reason)](https://pypi.org/project/cmem-plugin-reason/) [![license](https://img.shields.io/pypi/l/cmem-plugin-reason)](https://pypi.org/project/cmem-plugin-reasom) @@ -29,6 +27,7 @@ Alternatively, the _build_ and _installation_ process can be initiated with the ➜ task deploy ``` +# Reason ## Options ### Data graph IRI @@ -46,7 +45,6 @@ The IRI of the output graph for the reasoning result. :warning: Existing graphs will be overwritten. - ### Reasoner The following reasoner options are supported: @@ -84,3 +82,54 @@ parameters to include inferred axiom generators: - ObjectPropertyRange - ObjectPropertyDomain +### Maximum RAM Percentage + +Maximum heap size for the Java virtual machine in the DI container running the reasoning process. + +:warning: Setting the percentage too high may result in an out of memory error. + +# Validate + +In case ontology inconsistencies are found, the plugin outputs the explanation as text in Markdown format using the path "text". + +## Options + +### Ontology graph IRI + +The IRI of the input ontology graph. The graph IRI is selected from a list of graphs of type`owl:Ontology`. + +### Reasoner + +The following reasoner options are supported: +- [ELK](https://code.google.com/p/elk-reasoner/) (elk) +- [Expression Materializing Reasoner](http://static.javadoc.io/org.geneontology/expression-materializing-reasoner/0.1.3/org/geneontology/reasoner/ExpressionMaterializingReasoner.html) (emr) +- [HermiT](http://www.hermit-reasoner.com/) (hermit) +- [JFact](http://jfact.sourceforge.net/) (jfact) +- [Structural Reasoner](http://owlcs.github.io/owlapi/apidocs_4/org/semanticweb/owlapi/reasoner/structural/StructuralReasoner.html) (structural) +- [Whelk](https://github.com/balhoff/whelk) (whelk) + +### Produce output graph + +If enabled, an explanation graph is created. + +### Output graph IRI + +The IRI of the output graph for the reasoning result. + +:warning: Existing graphs will be overwritten. + +### Write markdown explanation file + +If enabled, an explanation markdown file is written to the project. + +:warning: Existing files will be overwritten. + +### Stop at inconsistencies + +Raise an error if inconsistencies are found. If enabled, the plugin does not output entities. + +### Maximum RAM Percentage + +Maximum heap size for the Java virtual machine in the DI container running the reasoning process. + +:warning: Setting the percentage too high may result in an out of memory error. \ No newline at end of file diff --git a/cmem_plugin_reason/plugin_reason.py b/cmem_plugin_reason/plugin_reason.py index 6934de7..ab5908c 100644 --- a/cmem_plugin_reason/plugin_reason.py +++ b/cmem_plugin_reason/plugin_reason.py @@ -1,7 +1,6 @@ """Reasoning workflow plugin module""" import shlex -from collections.abc import Sequence from datetime import UTC, datetime from pathlib import Path from subprocess import run @@ -12,7 +11,6 @@ from cmem.cmempy.dp.proxy.graph import get from cmem_plugin_base.dataintegration.context import ExecutionContext from cmem_plugin_base.dataintegration.description import Icon, Plugin, PluginParameter -from cmem_plugin_base.dataintegration.entity import Entities from cmem_plugin_base.dataintegration.parameter.graph import GraphParameterType from cmem_plugin_base.dataintegration.plugins import WorkflowPlugin from cmem_plugin_base.dataintegration.types import BoolParameterType, StringParameterType @@ -27,6 +25,7 @@ ROBOT, create_xml_catalog_file, get_graphs_tree, + remove_temp, send_result, ) @@ -59,7 +58,7 @@ param_type=StringParameterType(), name="result_graph_iri", label="Result graph IRI", - description="The IRI of the output graph for the reasoning result. ⚠️ Existing graph " + description="The IRI of the output graph for the reasoning result. ⚠️ Existing graphs " "will be overwritten.", ), PluginParameter( @@ -160,19 +159,10 @@ description="", default_value=False, ), - PluginParameter( - param_type=BoolParameterType(), - name="annotate_inferred_axioms", - label="Annnotate inferred subclass axioms", - description="Annotate inferred subclass axioms. ⚠️ This parameter can only be enabled " - "if the only enabled axiom generator is SubClass.", - default_value=False, - advanced=True, - ), ], ) class ReasonPlugin(WorkflowPlugin): - """Robot reasoning plugin""" + """Reason plugin""" def __init__( # noqa: PLR0913 self, @@ -194,10 +184,8 @@ def __init__( # noqa: PLR0913 sub_class: bool = True, sub_data_property: bool = False, sub_object_property: bool = False, - annotate_inferred_axioms: bool = False, max_ram_percentage: int = MAX_RAM_PERCENTAGE_DEFAULT, ) -> None: - """Init""" self.axioms = { "SubClass": sub_class, "EquivalentClass": equivalent_class, @@ -214,16 +202,13 @@ def __init__( # noqa: PLR0913 "ObjectPropertyRange": object_property_range, "ObjectPropertyDomain": object_property_domain, } - errors = "" - iris = { - "Data graph IRI": data_graph_iri, - "Ontology graph IRI": ontology_graph_iri, - "Result graph IRI": result_graph_iri, - } - not_iri = sorted([k for k, v in iris.items() if not validators.url(v)]) - if not_iri: - errors += f"Invalid IRI for parameters: {', '.join(not_iri)}. " + if not validators.url(data_graph_iri): + errors += 'Invalid IRI for parameter "Data graph IRI". ' + if not validators.url(ontology_graph_iri): + errors += 'Invalid IRI for parameter "Ontology graph IRI". ' + if not validators.url(result_graph_iri): + errors += 'Invalid IRI for parameter "Result graph IRI". ' if result_graph_iri and result_graph_iri == data_graph_iri: errors += "Result graph IRI cannot be the same as the data graph IRI. " if result_graph_iri and result_graph_iri == ontology_graph_iri: @@ -232,21 +217,14 @@ def __init__( # noqa: PLR0913 errors += 'Invalid value for parameter "Reasoner". ' if True not in self.axioms.values(): errors += "No axiom generator selected. " - if annotate_inferred_axioms and [k for k, v in self.axioms.items() if v] != ["SubClass"]: - errors += ( - 'Parameter "Annnotate inferred subclass axioms" can only be enabled if the only ' - "enabled axiom generator is SubClass. " - ) if max_ram_percentage not in range(1, 101): errors += 'Invalid value for parameter "Maximum RAM Percentage". ' if errors: raise ValueError(errors[:-1]) - self.data_graph_iri = data_graph_iri self.ontology_graph_iri = ontology_graph_iri self.result_graph_iri = result_graph_iri self.reasoner = reasoner - self.annotate_inferred_axioms = str(annotate_inferred_axioms).lower() self.max_ram_percentage = max_ram_percentage self.temp = f"reason_{uuid4().hex}" @@ -275,7 +253,6 @@ def reason(self, graphs: dict) -> None: "--collapse-import-closure false " f"reason --reasoner {self.reasoner} " f'--axiom-generators "{axioms}" ' - f"--annotate-inferred-axioms {self.annotate_inferred_axioms} " f"--include-indirect true " f"--exclude-duplicate-axioms true " f"--exclude-owl-thing true " @@ -305,21 +282,7 @@ def reason(self, graphs: dict) -> None: raise OSError(response.stderr.decode()) raise OSError("ROBOT error") - def clean_up(self, graphs: dict) -> None: - """Remove temporary files""" - files = ["catalog-v001.xml", "result.ttl"] - files += list(graphs.values()) - for file in files: - try: - (Path(self.temp) / file).unlink() - except (OSError, FileNotFoundError) as err: - self.log.warning(f"Cannot remove file {file} ({err})") - try: - Path(self.temp).rmdir() - except (OSError, FileNotFoundError) as err: - self.log.warning(f"Cannot remove directory {self.temp} ({err})") - - def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> None: # noqa: ARG002 + def execute(self, inputs: tuple, context: ExecutionContext) -> None: # noqa: ARG002 """Execute plugin""" setup_cmempy_user_access(context.user) graphs = get_graphs_tree((self.data_graph_iri, self.ontology_graph_iri)) @@ -328,4 +291,4 @@ def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> None self.reason(graphs) setup_cmempy_user_access(context.user) send_result(self.result_graph_iri, Path(self.temp) / "result.ttl") - self.clean_up(graphs) + remove_temp(self, ["catalog-v001.xml", "result.ttl", *graphs.values()]) diff --git a/cmem_plugin_reason/plugin_validate.py b/cmem_plugin_reason/plugin_validate.py index f2c8969..eea52ca 100644 --- a/cmem_plugin_reason/plugin_validate.py +++ b/cmem_plugin_reason/plugin_validate.py @@ -1,7 +1,6 @@ """Ontology consistency validation workflow plugin module""" import shlex -from collections.abc import Sequence from datetime import UTC, datetime from pathlib import Path from subprocess import run @@ -22,7 +21,7 @@ from cmem_plugin_base.dataintegration.plugins import WorkflowPlugin from cmem_plugin_base.dataintegration.types import BoolParameterType, StringParameterType from cmem_plugin_base.dataintegration.utils import setup_cmempy_user_access -from pathvalidate import validate_filename +from pathvalidate import is_valid_filename from cmem_plugin_reason.utils import ( MAX_RAM_PERCENTAGE_DEFAULT, @@ -33,6 +32,7 @@ ROBOT, create_xml_catalog_file, get_graphs_tree, + remove_temp, send_result, ) @@ -50,7 +50,8 @@ param_type=BoolParameterType(), name="write_md", label="Write Markdown explanation file", - description="Write Markdownn file with explanation to project.", + description="Write Markdown file with explanation to project. ⚠️ Existing files will " + "be overwritten.", default_value=False, ), PluginParameter( @@ -65,7 +66,7 @@ name="output_graph_iri", label="Output graph IRI", description="The IRI of the output graph for the inconsistency validation. ⚠️ Existing " - "graph will be overwritten.", + "graphs will be overwritten.", ), PluginParameter( param_type=StringParameterType(), @@ -85,7 +86,7 @@ ], ) class ValidatePlugin(WorkflowPlugin): - """Example Workflow Plugin: Random Values""" + """Validate plugin""" def __init__( # noqa: PLR0913 self, @@ -100,28 +101,26 @@ def __init__( # noqa: PLR0913 ) -> None: errors = "" if not validators.url(ontology_graph_iri): - errors += "Invalid IRI for parameter Ontology graph IRI. " - if reasoner not in REASONERS: - errors += "Invalid value for parameter Reasoner. " + errors += 'Invalid IRI for parameter "Ontology graph IRI." ' if produce_graph and not validators.url(output_graph_iri): - errors += "Invalid IRI for parameter Output graph IRI. " - if write_md: - try: - validate_filename(md_filename) - except: # noqa: E722 - errors += "Invalid filename for parameter Output filename. " - if max_ram_percentage not in range(1, 100): - errors += "Invalid value for parameter Maximum RAM Percentage. " + errors += 'Invalid IRI for parameter "Output graph IRI". ' + if produce_graph and output_graph_iri == ontology_graph_iri: + errors += "Output graph IRI cannot be the same as the Ontology graph IRI. " + if reasoner not in REASONERS: + errors += 'Invalid value for parameter "Reasoner". ' + if write_md and not is_valid_filename(md_filename): + errors += 'Invalid filename for parameter "Output filename". ' + if max_ram_percentage not in range(1, 101): + errors += 'Invalid value for parameter "Maximum RAM Percentage". ' if errors: raise ValueError(errors[:-1]) - self.ontology_graph_iri = ontology_graph_iri self.reasoner = reasoner self.produce_graph = produce_graph self.output_graph_iri = output_graph_iri self.write_md = write_md self.stop_at_inconsistencies = stop_at_inconsistencies - self.md_filename = md_filename if md_filename and write_md else "mdfile.md" + self.md_filename = md_filename if write_md else "mdfile.md" self.max_ram_percentage = max_ram_percentage self.temp = f"reason_{uuid4().hex}" @@ -176,35 +175,20 @@ def make_resource(self, context: ExecutionContext) -> None: replace=True, ) - def clean_up(self, graphs: dict) -> None: - """Remove temporary files""" - files = ["catalog-v001.xml", "output.ttl", self.md_filename] - files += list(graphs.values()) - for file in files: - try: - (Path(self.temp) / file).unlink() - except (OSError, FileNotFoundError) as err: - self.log.warning(f"Cannot remove file {file} ({err})") - try: - Path(self.temp).rmdir() - except (OSError, FileNotFoundError) as err: - self.log.warning(f"Cannot remove directory {self.temp} ({err})") - - def execute( - self, - inputs: Sequence[Entities], # noqa: ARG002 - context: ExecutionContext, - ) -> Entities | None: + def execute(self, inputs: tuple, context: ExecutionContext) -> Entities | None: # noqa: ARG002 """Run the workflow operator.""" setup_cmempy_user_access(context.user) graphs = get_graphs_tree((self.ontology_graph_iri,)) self.get_graphs(graphs, context) create_xml_catalog_file(self.temp, graphs) self.validate(graphs) + files = ["catalog-v001.xml", self.md_filename, *graphs.values()] + if self.produce_graph: + files.append("output.ttl") text = (Path(self.temp) / self.md_filename).read_text() if text == "No explanations found.": - self.clean_up(graphs) + remove_temp(self, files) return None if self.produce_graph: @@ -215,7 +199,7 @@ def execute( setup_cmempy_user_access(context.user) self.make_resource(context) - self.clean_up(graphs) + remove_temp(self, files) if self.stop_at_inconsistencies: raise RuntimeError("Inconsistencies found in Ontology.") diff --git a/cmem_plugin_reason/utils.py b/cmem_plugin_reason/utils.py index 0b06e25..eea73a7 100644 --- a/cmem_plugin_reason/utils.py +++ b/cmem_plugin_reason/utils.py @@ -1,4 +1,4 @@ -"""Common functions""" +"""Common constants and functions""" import re import unicodedata @@ -10,6 +10,7 @@ from cmem_plugin_base.dataintegration.description import PluginParameter from cmem_plugin_base.dataintegration.parameter.choice import ChoiceParameterType from cmem_plugin_base.dataintegration.parameter.graph import GraphParameterType +from cmem_plugin_base.dataintegration.plugins import WorkflowPlugin from cmem_plugin_base.dataintegration.types import IntParameterType from defusedxml import minidom @@ -66,9 +67,9 @@ def convert_iri_to_filename(value: str) -> str: return value + ".nt" -def create_xml_catalog_file(temp: str, graphs: dict) -> None: +def create_xml_catalog_file(dir_: str, graphs: dict) -> None: """Create XML catalog file""" - file_name = Path(temp) / "catalog-v001.xml" + file_name = Path(dir_) / "catalog-v001.xml" catalog = Element("catalog") catalog.set("prefer", "public") catalog.set("xmlns", "urn:oasis:names:tc:entity:xmlns:xml:catalog") @@ -105,3 +106,16 @@ def send_result(iri: str, filepath: Path) -> None: replace=True, content_type="text/turtle", ) + + +def remove_temp(plugin: WorkflowPlugin, files: list) -> None: + """Remove temproray files""" + for file in files: + try: + (Path(plugin.temp) / file).unlink() + except (OSError, FileNotFoundError) as err: + plugin.log.warning(f"Cannot remove file {file} ({err})") + try: + Path(plugin.temp).rmdir() + except (OSError, FileNotFoundError) as err: + plugin.log.warning(f"Cannot remove directory {plugin.temp} ({err})") diff --git a/poetry.lock b/poetry.lock index ce33ffb..61c0e7c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -485,48 +485,48 @@ files = [ [[package]] name = "memray" -version = "1.13.1" +version = "1.13.2" description = "A memory profiler for Python applications" optional = false python-versions = ">=3.7.0" files = [ - {file = "memray-1.13.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:5aa567b5f7a3f67b40194dcaa3ef06557ebf088e67f7ee41e7154b9747dbbd8d"}, - {file = "memray-1.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:94eefd57a5e4f27ab97e976d732027957b15e3b9b561a643388dd1417380461d"}, - {file = "memray-1.13.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:22263e294faffda1f8097b0d6e1d2283ca35a537d3e8f608244f48fd3f58d490"}, - {file = "memray-1.13.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e3a5eb9bc01cdc2f916bae1615301347aaa252deb983d8277b0c9e4d4410e73"}, - {file = "memray-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4917e333290802d91d1823f34365670628b2b0804dba93150c79bf8b855f8d5"}, - {file = "memray-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4270998b8fcd5f1a9c8faff2ad4f46fed57191160e78183357eba7dc32d93a1b"}, - {file = "memray-1.13.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:71c5c8bb5d7ad19b3027efc48a792cb35bda7b9e1662b2c5f23af3f8f7374eae"}, - {file = "memray-1.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dadd7afa44df07074f4d887587e8241e085d9727fcf5b5097930c05ad054027a"}, - {file = "memray-1.13.1-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9d9e2604dd2b2025d5f0012e848cbc5357a15a4d27297cde5f76dd2fccb1873f"}, - {file = "memray-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74871ef2047f30d0f98994f56563fa8be85515679fbc398006246339ff4ace7d"}, - {file = "memray-1.13.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d6bdaf8a4499f8dc3e1f6781cab13f95ed052f77b0a2ca0ef63ac851296cc9b"}, - {file = "memray-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:748c7ba3180d84a5ce6dfc413e7f6127d887341ea75ddbffee150e0124cf810c"}, - {file = "memray-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:659c8ddcae04bd8bc627836663a90eec077d831d904dc8ed7ce42a64a96fe8d2"}, - {file = "memray-1.13.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:a86f80ced0bdf49e2cc637e112664dce6ded43a802c0fe99ea826f1a717fa252"}, - {file = "memray-1.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d5f9ab8b4952631c030bfba74a385686f2da11471e13d838184b32d6582edb"}, - {file = "memray-1.13.1-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e33f8320e8578d16e371b90cd2c792e1414bfe28173c7f977d6b440686a66625"}, - {file = "memray-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2c9896719d82240291f7ac81ba2509a8065f4a7829fc8219e0890e68877f497"}, - {file = "memray-1.13.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c652762aa761c7b546ad5ef180b44f204ebc995e0e7efe86b21086b0bc3c665e"}, - {file = "memray-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a94a258acc32366ef635bb0870f0d3e80279153012c3655482da8c5c8454da"}, - {file = "memray-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:77689e659b22e39b585cf166037ece95734d55b35a581a19db0eb5769005819c"}, - {file = "memray-1.13.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fbf4d86148483c284e107829b43853ad07fbcc49ea2ad16d34aeb7467edb2d4a"}, - {file = "memray-1.13.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:45ecf8242128737502be96e4933269ef76d1c6a6e652e4a18f31e95de73e48af"}, - {file = "memray-1.13.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be195ef8c6b80792930331b74731362df06bb4c7c8f184b2d63263853e31f75c"}, - {file = "memray-1.13.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:16d36a6c422acd939545e6d78e6d7290beb317d95cd4ef0d30d2218fa1dc7b39"}, - {file = "memray-1.13.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5ddbac56b11bf7053dbec99f1c4bad20269504e5cad2791f88fcb552d3338c73"}, - {file = "memray-1.13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:919655abc25a6dd846f6a38d0df178edf31e4011552f8545865413e5bd2e5646"}, - {file = "memray-1.13.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:380cb52917a939e11e3e08f685c911ae47bbd71a0e71d4d6e07ee67a6429ff30"}, - {file = "memray-1.13.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:80e500ea2e21e3eb0b8c1ac938be70f1b1ce7f9c994cfe82700cad46c0744a6a"}, - {file = "memray-1.13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50e4aa2ad22abbb8e5a47b77cc8a62e0ed7864be154e2c87fc603699bc65db6"}, - {file = "memray-1.13.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b2b65ac79215f1f588da5fbbecd76b40821265a0192308b0c4c64f624e0bd3d0"}, - {file = "memray-1.13.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8aa27831a9ae93414965dd90ca7a3eb54b01f8bf0eb563598aa1f7bad1e88901"}, - {file = "memray-1.13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2890417e055b2d9dc9b8b5aab17c40d585f7f9767041e8e101553b1ca044c0aa"}, - {file = "memray-1.13.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f55fa2849e187ef6cf6aa5d765de58ca50b9afc45950834c65cb95b371b1654"}, - {file = "memray-1.13.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c54e5cdd500f8d8bb79a0c2e9e8569a1002083ba0ec72ccbd300f0cc61669af"}, - {file = "memray-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12decb3b154e335d67179332ff7818add233fc8cd94bc023af281656fca6c679"}, - {file = "memray-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31203c80094b3189ece8dbde39fbbef03bfa2306dd3b84a3463c6bbed1d8eff1"}, - {file = "memray-1.13.1.tar.gz", hash = "sha256:2149450064392aad2df9ba641b78bba979d1443084c074ff454830a71aa9f095"}, + {file = "memray-1.13.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:a773eabc0a7dad693eec193e28027130ba1d82ffa5345143f49f4a0bdc1c6c65"}, + {file = "memray-1.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb1c75ae62c8aea7010df4d4ec5ddf057ebe0ac6e694d852bf1c732dc6b9febc"}, + {file = "memray-1.13.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9caec18ec308d126229053d366240271cca2186f3c1941ba9cba5ab787259a4e"}, + {file = "memray-1.13.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fa2efe628159035ae39a458de81b6f0816eced2c10d66700cf84dacbe0a3bd6"}, + {file = "memray-1.13.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9852484e199c7111e6d571f4fef0e0360dde8f88449d9c5c8e6f1358bdb0ba10"}, + {file = "memray-1.13.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:99865a5e913337abaff03627be96c1924e5bdf41d716c3f3e30a29dae0c34dbc"}, + {file = "memray-1.13.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:6f59d65392d3725770c136d2773b78d90a67a84041570773fed253991adc7f7a"}, + {file = "memray-1.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1b8d4f2bb000f526767004e065db59e403a1bf428c216576a168e54dac3f1e4"}, + {file = "memray-1.13.2-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ab90162027af022b14fa03420112efee7b1dbf110591d16b442701932c95f2c"}, + {file = "memray-1.13.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74c076899614eaae7e5b00473980dd75fecf09c22206db49f0861e076b460793"}, + {file = "memray-1.13.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39a1dd4a2004dfaa254bc5469cddfead716001290ad18705e9a39197ee3f529d"}, + {file = "memray-1.13.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd3bfb4d51d36d826fdec40079e527012e167ba1a4ff0dac843749c631164ed6"}, + {file = "memray-1.13.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b295a5046b9327852b9f2010cb721317ef61b2f5ed816651d135e971ece3044d"}, + {file = "memray-1.13.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:3855b81d1d8307a2209540011fb683f65854b01c92bf4fe3d34866a96bcd7fb9"}, + {file = "memray-1.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c87b688359de63a22308bb0879693cf9c81577a3c535d4a08d4c7130ed01871"}, + {file = "memray-1.13.2-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f441e05dc472a1be07fbcf1217f4572d682c5814fac083523d603bc418b70f07"}, + {file = "memray-1.13.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b9e35f1bdebe63bb719eac3866fd07ed8b9ffc1bdcbfff3e0d29140d335b8f1"}, + {file = "memray-1.13.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7880f21a2df374d6d95004e7f55075516e57892e4ab1e77f5df65dd2ad382a86"}, + {file = "memray-1.13.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9787b9f1a85fe29b0eb7b963201a4a608f8ec7de9b84bb72d672f2308a59cf33"}, + {file = "memray-1.13.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:326acdae17de17e7632598853774a7e09ad1b72eae2d3755f88a212e17ba7813"}, + {file = "memray-1.13.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8c7ba0bd98323c71a5f2cafa81e74d232e9d1c7437682fdf1cb8833ec2c70f08"}, + {file = "memray-1.13.2-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d00ba714d6f8fe32fbfdf75f2d1b6755edcd7c6835efc4a65fa58943c0b53e62"}, + {file = "memray-1.13.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bef98aaf398cb7f47669808d5febdad4323050f32f6e73ec3bfc6f7c04aacb0"}, + {file = "memray-1.13.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:300c3710d6ddd291d6b53402e11c0de5818223e58e21ca1d42817f36dee29b24"}, + {file = "memray-1.13.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5e63552376567283a388e67382534017909891df8500d92e2dad1ebd3762d5b1"}, + {file = "memray-1.13.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f09742556da269712cc89a7acaaa0825db15547ac1d03490f09ba3c2bf381f3"}, + {file = "memray-1.13.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c5595539579f3f662acf794d8ac2fd3f23d3a24c4aea893a15b307c2727c3c8"}, + {file = "memray-1.13.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60176bf3015a155233d8cfaf7a6141eedc3ce5e89231892a96e162c073fb42cd"}, + {file = "memray-1.13.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bfa76dce73e6ee263d254345affc8bf7d4db8f899b7af396a0f5e37899f28aac"}, + {file = "memray-1.13.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:59bcb96af1a3462626575de4763c88217d9c1561d8c166c9a10f00a0c19ad8d1"}, + {file = "memray-1.13.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:cdd35bf96776c27ffb326c2c4b6f9b50d9461b25f98574df98771593768eb803"}, + {file = "memray-1.13.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48449deeb65de71c0fda4ce9b539a9f35fe2634af3bcac367167903bab25cff0"}, + {file = "memray-1.13.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:71021329db86e03840fe210c5a6581500a069736e6f8835b2dae9c8e6be96302"}, + {file = "memray-1.13.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:db87adfbf05cec117b75cdafb7e2627d649b72f15a191e106375ee0acdd54b53"}, + {file = "memray-1.13.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6305ea70ddd15b9897abcc6f234b8aa94d4135146381b3d68fc97ec2d8d3f7ec"}, + {file = "memray-1.13.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1f38100fedec5c318c00654864ff2e6e8b8ffdb472206047e300337c2d55bb37"}, + {file = "memray-1.13.2.tar.gz", hash = "sha256:355803182ea4a61dcb456c4c1e765f929123fc2b6998ba44efa4ae32b763e05b"}, ] [package.dependencies] @@ -1048,13 +1048,13 @@ files = [ [[package]] name = "textual" -version = "0.70.0" +version = "0.71.0" description = "Modern Text User Interface framework" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "textual-0.70.0-py3-none-any.whl", hash = "sha256:774bf45782193760ca273b915fd685cada37d0836237d61dc57d5bcdbe2c7ddb"}, - {file = "textual-0.70.0.tar.gz", hash = "sha256:9ca3f615b5cf442246325e40ef8255424c42b4241d3c62f9c0f96951bab82b1e"}, + {file = "textual-0.71.0-py3-none-any.whl", hash = "sha256:60500cc63ffd98a4aff8679976a1530d5e99a881e13bb040d26558a2c45f49d8"}, + {file = "textual-0.71.0.tar.gz", hash = "sha256:a38a3bd7e450ed173b59ab1c93a3aa8174d95bc5c79647f220a50243236ce70a"}, ] [package.dependencies]