Skip to content

Commit

Permalink
Merge branch 'develop_carsten' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
cknoll committed Nov 5, 2023
2 parents 51a3f3d + b270dd4 commit 2046662
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 149 deletions.
4 changes: 4 additions & 0 deletions src/pyerk/auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ class SemanticRuleError(PyERKError):
pass


class ExcplicitlyTriggeredTestException(PyERKError):
pass


class InconsistentEdgeRelations(SemanticRuleError):
pass

Expand Down
120 changes: 36 additions & 84 deletions src/pyerk/builtin_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -1380,9 +1380,8 @@ def create_expression(latex_src: str, r1: str = None, r2: str = None) -> Item:
return expression


# todo: docs: currently ordinary strings can be used where such an Item is expected
# they are interpreted as in the default language
# todo: this entity is obsolete, we use a construction with RDF-Literals instead
# TODO: this item might be obsolete, we use a construction with RDF-Literals instead
# however currently it is uses for R11__has_range_of_result in the ocse (test data)
I19 = create_builtin_item(
key_str="I19",
R1__has_label="multilingual string literal",
Expand Down Expand Up @@ -1685,18 +1684,6 @@ def new_mathematical_relation(lhs: Item, rsgn: str, rhs: Item, doc=None, scope:
return mr


# annoying: pycharm does not recognize that "str"@some_LangaguageCode_obj is valid because str does not
# implement __matmul__
# noinspection PyUnresolvedReferences
I900 = create_builtin_item(
key_str="I900",
R1__has_label="test item mit label auf deutsch" @ de,
R2__has_description="used for testing during development",
R4__is_instance_of=I2["Metaclass"],
R18__has_usage_hint="This item serves only for unittesting labels in different languages",
)


# reminder that R32["is functional for each language"] already is defined
assert R32 is not None

Expand Down Expand Up @@ -2561,8 +2548,41 @@ def get_relation_properties(rel_entity: Entity) -> List[str]:
R22__is_functional=True,
)

I50 = create_builtin_item(
key_str="I50",
R1__has_label="stub",
R2__has_description="instances of this class represent incompletly modelled items (like wikipedia stub-articles)",
R3__is_subclass_of=I2["Metaclass"],
R18__has_usage_hint="This class can be used to preliminarily introduce items and refine them later",
)


# next keys: I51, R76


# ######################################################################################################################
# auxilliary entities
# ######################################################################################################################


I000 = create_builtin_item(
key_str="I000",
R1__has_label="dummy item",
R2__has_description="used during development as placeholder for items which will be defined later",
R4__is_instance_of=I2["Metaclass"], # this means: this Item is an ordinary class
)

R000 = create_builtin_relation(
key_str="R000",
R1__has_label="dummy relation",
R2__has_description="used during development as placeholder for relations which will be defined later",
)

# this allows to use I000("with any label") witout triggering an exception in I000.idoc
I000._ignore_mismatching_adhoc_label = True
# ... same for R000
R000._ignore_mismatching_adhoc_label = True

# next keys: I50, R76

# ######################################################################################################################
# condition functions (to be used in the premise scope of a rule)
Expand Down Expand Up @@ -2694,73 +2714,5 @@ def raise_reasoning_goal_reached(self, msg_template, *args):
raise core.aux.ReasoningGoalReached(msg)


# ######################################################################################################################
# Testing and debugging entities

# I041 = create_builtin_item(
# key_str="I041",
# R1__has_label="subproperty rule 1",
# R2__has_description=(
# # "specifies the 'transitivity' of I11_mathematical_property-instances via R17_issubproperty_of"
# "specifies the 'transitivity' of R17_issubproperty_of"
# ),
# R4__is_instance_of=I41["semantic rule"],
# )
#
#
# with I041["subproperty rule 1"].scope("setting") as cm:
#
# cm.new_var(P1=instance_of(I11["mathematical property"]))
# cm.new_var(P2=instance_of(I11["mathematical property"]))
# cm.new_var(P3=instance_of(I11["mathematical property"]))
# # # A = cm.new_var(sys=instance_of(I1["general item"]))
# #
# with I041["subproperty rule 1"].scope("premise") as cm:
# cm.new_rel(cm.P2, R17["is subproperty of"], cm.P1)
# cm.new_rel(cm.P3, R17["is subproperty of"], cm.P2)
# # todo: state that all variables are different from each other
#
# with I041["subproperty rule 1"].scope("assertion") as cm:
# cm.new_rel(cm.P3, R17["is subproperty of"], cm.P1)

# noinspection PyUnresolvedReferences
I900.set_relation(R1["has label"], "test item with english label" @ en)


I000 = create_builtin_item(
key_str="I000",
R1__has_label="dummy item",
R2__has_description="used during development as placeholder for items which will be defined later",
R4__is_instance_of=I2["Metaclass"], # this means: this Item is an ordinary class
)

R000 = create_builtin_relation(
key_str="R000",
R1__has_label="dummy relation",
R2__has_description="used during development as placeholder for relations which will be defined later",
)

# this allows to use I000("with any label") witout triggering an exception in I000.idoc
I000._ignore_mismatching_adhoc_label = True
# ... same for R000
R000._ignore_mismatching_adhoc_label = True


# TODO: evaluate the necessity of this class (especially in face of IntegerRangeElement (ocse.ma))
class Sequence:
r"""
Models a sequence like y, `\dot y, ..., y^(k)`
"""

def __init__(self, base, prop, link_op, start, stop):
# Sequence item with the respective relations and conveniently create all necessary auxiliary items
# runnig index of `prop` and running index of the sequence object have to be connected
self.base = base
self.prop = prop
self.link_op = link_op
self.start = start
self.stop = stop


# this is the inverse operation to `core.start_mod(__URI__)` (see above)
core.end_mod()
28 changes: 3 additions & 25 deletions src/pyerk/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,13 +465,6 @@ def set_relation(
if isinstance(relation, Relation):
if isinstance(obj, (Entity, *allowed_literal_types)) or obj in allowed_literal_types:
return self._set_relation(relation.uri, obj, scope=scope, qualifiers=qualifiers, proxyitem=proxyitem)
# Todo: remove obsolete code:
# elif isinstance(obj, Iterable):
# msg = f"Unsupported iterable type ({type(obj)}) of {obj}, while setting relation {relation.short_key}"
# raise TypeError(msg)
# elif isinstance(obj, (Entity, str, bool, float, int, complex)):
# # obj is eithter an entity or a literal
# return self._set_relation(relation.short_key, obj, scope=scope, proxyitem=proxyitem)
else:
msg = f"Unsupported type ({type(obj)}) of {obj}, while setting relation {relation.short_key} of {self}"
raise TypeError(msg)
Expand Down Expand Up @@ -759,10 +752,6 @@ def __init__(self):
# dict to store important QualifierFactory instances which are created in builtin_entities but needed in core
self.qff_dict = {}

# TODO: obsolete!
# list of keys which are released, when unloading a module
self.released_keys = []

# mapping like {uri_1: keymanager_1, ...}
self.uri_keymanager_dict = {}

Expand Down Expand Up @@ -1803,9 +1792,6 @@ def unlink(self, *args) -> None:

ds.statement_uri_map.pop(self.uri)

# TODO: remove obsolete key-recycling
ds.released_keys.append(self.short_key)


class QualifierStatement(Statement):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -2092,9 +2078,6 @@ def unload_mod(mod_uri: str, strict=True) -> None:
:return: list of released keys
"""

# prepare the list to store the released keys
ds.released_keys.clear()

# TODO: This might to check dependencies in the future

entity_uris: List[str] = ds.entities_created_in_mod.pop(mod_uri, [])
Expand Down Expand Up @@ -2129,9 +2112,6 @@ def unload_mod(mod_uri: str, strict=True) -> None:
aux.clean_dict(ds.statements)
aux.clean_dict(ds.inv_statements)

# TODO: obsolete
res = list(ds.released_keys)

try:
ds.uri_keymanager_dict.pop(mod_uri)
except KeyError:
Expand All @@ -2149,9 +2129,6 @@ def unload_mod(mod_uri: str, strict=True) -> None:
if modname := ds.modnames.get(mod_uri):
sys.modules.pop(modname)

# empty the list again to avoid confusion in future uses
ds.released_keys.clear()


def _unlink_entity(uri: str, remove_from_mod=False) -> None:
"""
Expand Down Expand Up @@ -2213,8 +2190,6 @@ def _unlink_entity(uri: str, remove_from_mod=False) -> None:
ds.statements.pop(entity.uri, None)
ds.inv_statements.pop(entity.uri, None)

ds.released_keys.append(uri)


def replace_and_unlink_entity(old_entity: Entity, new_entity: Entity):
"""
Expand Down Expand Up @@ -2365,6 +2340,9 @@ def __rmatmul__(self, arg: str) -> Literal:

en = LangaguageCode("en")
de = LangaguageCode("de")
fr = LangaguageCode("fr")
it = LangaguageCode("it")
es = LangaguageCode("es")


class RuleResult:
Expand Down
3 changes: 3 additions & 0 deletions src/pyerk/erkloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ def _load_mod_from_path(

# remove all added entities, but tolerate errors (due to incomplete loading)
pyerk.unload_mod(failed_mod_uri, strict=False)

# ensure that the current module is not lurking in sys.modules
sys.modules.pop(modname, None)
raise

if len(pyerk.core._uri_stack) > old_len:
Expand Down
58 changes: 46 additions & 12 deletions src/pyerk/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import argparse
from pathlib import Path
import re

try:
# this will be part of standard library for python >= 3.11
Expand Down Expand Up @@ -319,29 +320,29 @@ def insert_keys_for_placeholders(modpath):
shutil.copy(modpath, backup_path)
print(f"Backup: {backup_path}")

start_tag = r"#\s*?<new_entities>"
end_tag = r"#\s*?</new_entities>"

placeholder = "_newitemkey_ = "
key_count = old_txt.count(f"\n{placeholder}")

#
# write the module without the placeholder lines
old_lines = old_txt.split("\n")
tmp_lines = []
for line in old_lines:
if line.startswith(placeholder):
continue
else:
tmp_lines.append(line)
pattern = f"{start_tag}.*?{end_tag}"
tmp_txt = re.sub(pattern=pattern, repl="", string=old_txt, flags=re.DOTALL)
assert start_tag not in tmp_txt
assert end_tag not in tmp_txt
assert placeholder not in tmp_txt

tmp_modpath = os.path.join(tempfile.mkdtemp(prefix="pyerk_tmp_"), fname)
tmp_modpath = tempfile.mktemp(prefix=f"{fname[:-3]}_tmp_", suffix=".py", dir=".")

with open(tmp_modpath, "w") as fp:
fp.write("\n".join(tmp_lines))
fp.write(tmp_txt)

#
# load this temporary module
loaded_mod = process_mod(path=tmp_modpath, prefix="mod", relative_to_workdir=True)
item_keys = [core.generate_new_key("I", mod_uri=loaded_mod.__URI__) for i in range(key_count)]

old_lines = old_txt.split("\n")

# replace the respective lines in the original module
new_lines = []
for line in old_lines:
Expand All @@ -362,6 +363,39 @@ def insert_keys_for_placeholders(modpath):
with open(modpath, "w") as fp:
fp.write(txt)

core.unload_mod(loaded_mod.__URI__)
os.unlink(tmp_modpath)

replace_dummy_enties_by_label(modpath)


def replace_dummy_enties_by_label(modpath):
"""
load the module, additionally load its source, replace entities like I000["some label"] with
real entities like I7654["some label"].
"""

loaded_mod = process_mod(path=modpath, prefix="mod", relative_to_workdir=True)
pattern = re.compile("""(p.I000\[['"](.*?)['"]\])""")

with open(modpath) as fp:
txt = fp.read()

matches = list(pattern.finditer(txt))
for match in matches:
full_expr = match.group(1) # the whole string like `p.I000["foo bar"]`
label = match.group(2) # only the label string "foo bar"
entity = core.ds.get_item_by_label(label)
if entity is None:
print(f"could not find entity for label: {label}")
continue
new_expr = f'{entity.short_key}["{label}"]'
txt = txt.replace(full_expr, new_expr)


with open(modpath, "w") as fp:
fp.write(txt)


def interactive_session(loaded_mod, prefix):
"""
Expand Down
2 changes: 1 addition & 1 deletion src/pyerk/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# for now we only support a subset of languages with wich the authors are familiar
# if you miss a language, please consider contributing
SUPPORTED_LANGUAGES = ["en", "de"]
SUPPORTED_LANGUAGES = ["en", "de", "fr", "it", "es"]
# https://en.wikipedia.org/wiki/IETF_language_tag


Expand Down
5 changes: 4 additions & 1 deletion tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

# useful to get the currently latest sha strings:
# git log --pretty=oneline | head
TEST_DATA_REPO_COMMIT_SHA = "0df1785dedab592909a9deed8c8fed7ddebb7748" # (2023-11-03 21:50:41)
TEST_DATA_REPO_COMMIT_SHA = "8898efd919cfefa959eca59e19dd3888cb1ea9de" # (2023-11-05 15:29:28)

# TODO: make this more robust (e.g. search for config file or environment variable)
# TODO: put link to docs here (directory layout)
Expand Down Expand Up @@ -74,6 +74,7 @@ def setUp(self):
os.environ["UNITTEST_METHOD_NAME"] = method_repr
self.register_this_module()
p.ds.initialize_hooks()
self.files_to_delete = []

def tearDown(self) -> None:
# possibility to keep the mods loaded on error for easier interactive debugging
Expand All @@ -82,6 +83,8 @@ def tearDown(self) -> None:
self.unload_all_mods()
self.print_methodnames()
os.environ.pop("UNITTEST_METHOD_NAME", None)
for path in self.files_to_delete:
os.unlink(path)

@staticmethod
def unload_all_mods():
Expand Down
Loading

0 comments on commit 2046662

Please sign in to comment.