infixowl.py 79 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407
  1. """RDFLib Python binding for OWL Abstract Syntax
  2. | OWL Constructor | DL Syntax | Manchester OWL Syntax | Example |
  3. |------------------|---------------|------------------------|----------------------------------|
  4. | `intersectionOf` | C ∩ D | C AND D | Human AND Male |
  5. | `unionOf` | C ∪ D | C OR D | Man OR Woman |
  6. | `complementOf` | ¬C | NOT C | NOT Male |
  7. | `oneOf` | {a} ∪ {b}... | {a b ...} | {England Italy Spain} |
  8. | `someValuesFrom` | ∃ R C | R SOME C | hasColleague SOME Professor |
  9. | `allValuesFrom` | ∀ R C | R ONLY C | hasColleague ONLY Professor |
  10. | `minCardinality` | ≥ N R | R MIN 3 | hasColleague MIN 3 |
  11. | `maxCardinality` | ≤ N R | R MAX 3 | hasColleague MAX 3 |
  12. | `cardinality` | = N R | R EXACTLY 3 | hasColleague EXACTLY 3 |
  13. | `hasValue` | ∃ R.{a} | R VALUE a | hasColleague VALUE Matthew |
  14. See:
  15. - http://www.w3.org/TR/owl-semantics/syntax.html
  16. - http://owl-workshop.man.ac.uk/acceptedLong/submission_9.pdf
  17. 3.2.3 Axioms for complete classes without using owl:equivalentClass
  18. Named class description of type 2 (with owl:oneOf) or type 4-6
  19. (with owl:intersectionOf, owl:unionOf or owl:complementOf
  20. Uses Manchester Syntax for `__repr__`
  21. ```python
  22. >>> exNs = Namespace("http://example.com/")
  23. >>> g = Graph()
  24. >>> g.bind("ex", exNs, override=False)
  25. ```
  26. Now we have an empty graph, we can construct OWL classes in it
  27. using the Python classes defined in this module
  28. ```python
  29. >>> a = Class(exNs.Opera, graph=g)
  30. ```
  31. Now we can assert rdfs:subClassOf and owl:equivalentClass relationships
  32. (in the underlying graph) with other classes using the 'subClassOf'
  33. and 'equivalentClass' descriptors which can be set to a list
  34. of objects for the corresponding predicates.
  35. ```python
  36. >>> a.subClassOf = [exNs.MusicalWork]
  37. ```
  38. We can then access the rdfs:subClassOf relationships
  39. ```python
  40. >>> print(list(a.subClassOf))
  41. [Class: ex:MusicalWork ]
  42. ```
  43. This can also be used against already populated graphs:
  44. ```python
  45. >>> owlGraph = Graph().parse(str(OWL))
  46. >>> list(Class(OWL.Class, graph=owlGraph).subClassOf)
  47. [Class: rdfs:Class ]
  48. ```
  49. Operators are also available. For instance we can add ex:Opera to the extension
  50. of the ex:CreativeWork class via the '+=' operator
  51. ```python
  52. >>> a
  53. Class: ex:Opera SubClassOf: ex:MusicalWork
  54. >>> b = Class(exNs.CreativeWork, graph=g)
  55. >>> b += a
  56. >>> print(sorted(a.subClassOf, key=lambda c:c.identifier))
  57. [Class: ex:CreativeWork , Class: ex:MusicalWork ]
  58. ```
  59. And we can then remove it from the extension as well
  60. ```python
  61. >>> b -= a
  62. >>> a
  63. Class: ex:Opera SubClassOf: ex:MusicalWork
  64. ```
  65. Boolean class constructions can also be created with Python operators.
  66. For example, The | operator can be used to construct a class consisting of a
  67. owl:unionOf the operands:
  68. ```python
  69. >>> c = a | b | Class(exNs.Work, graph=g)
  70. >>> c
  71. ( ex:Opera OR ex:CreativeWork OR ex:Work )
  72. ```
  73. Boolean class expressions can also be operated as lists (using python list
  74. operators)
  75. ```python
  76. >>> del c[c.index(Class(exNs.Work, graph=g))]
  77. >>> c
  78. ( ex:Opera OR ex:CreativeWork )
  79. ```
  80. The '&' operator can be used to construct class intersection:
  81. ```python
  82. >>> woman = Class(exNs.Female, graph=g) & Class(exNs.Human, graph=g)
  83. >>> woman.identifier = exNs.Woman
  84. >>> woman
  85. ( ex:Female AND ex:Human )
  86. >>> len(woman)
  87. 2
  88. ```
  89. Enumerated classes can also be manipulated
  90. ```python
  91. >>> contList = [Class(exNs.Africa, graph=g), Class(exNs.NorthAmerica, graph=g)]
  92. >>> EnumeratedClass(members=contList, graph=g)
  93. { ex:Africa ex:NorthAmerica }
  94. ```
  95. owl:Restrictions can also be instantiated:
  96. ```python
  97. >>> Restriction(exNs.hasParent, graph=g, allValuesFrom=exNs.Human)
  98. ( ex:hasParent ONLY ex:Human )
  99. ```
  100. Restrictions can also be created using Manchester OWL syntax in 'colloquial' Python
  101. ```python
  102. >>> exNs.hasParent @ some @ Class(exNs.Physician, graph=g)
  103. ( ex:hasParent SOME ex:Physician )
  104. >>> Property(exNs.hasParent, graph=g) @ max @ Literal(1)
  105. ( ex:hasParent MAX 1 )
  106. >>> print(g.serialize(format='pretty-xml')) # doctest: +SKIP
  107. ```
  108. """
  109. from __future__ import annotations
  110. import itertools
  111. import logging
  112. from typing import Iterable, Union
  113. from rdflib.collection import Collection
  114. from rdflib.graph import Graph, _ObjectType
  115. from rdflib.namespace import OWL, RDF, RDFS, XSD, Namespace, NamespaceManager
  116. from rdflib.term import BNode, Identifier, Literal, URIRef, Variable
  117. from rdflib.util import first
  118. logger = logging.getLogger(__name__)
  119. """
  120. From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/384122
  121. Python has the wonderful "in" operator and it would be nice to have additional
  122. infix operator like this. This recipe shows how (almost) arbitrary infix
  123. operators can be defined.
  124. """
  125. __all__ = [
  126. "ACE_NS",
  127. "AllClasses",
  128. "AllDifferent",
  129. "AllProperties",
  130. "AnnotatableTerms",
  131. "BooleanClass",
  132. "CLASS_RELATIONS",
  133. "Callable",
  134. "CastClass",
  135. "Class",
  136. "ClassNamespaceFactory",
  137. "CommonNSBindings",
  138. "ComponentTerms",
  139. "DeepClassClear",
  140. "EnumeratedClass",
  141. "GetIdentifiedClasses",
  142. "Individual",
  143. "Infix",
  144. "MalformedClass",
  145. "MalformedClassError",
  146. "OWLRDFListProxy",
  147. "Ontology",
  148. "Property",
  149. "PropertyAbstractSyntax",
  150. "Restriction",
  151. "classOrIdentifier",
  152. "classOrTerm",
  153. "exactly",
  154. "generateQName",
  155. "manchesterSyntax",
  156. "max",
  157. "min",
  158. "nsBinds",
  159. "only",
  160. "propertyOrIdentifier",
  161. "some",
  162. "value",
  163. ]
  164. # definition of an Infix operator class
  165. # this recipe also works in jython
  166. # calling sequence for the infix is:
  167. # x @ op @ y
  168. class Infix:
  169. def __init__(self, function):
  170. self.function = function
  171. def __rlshift__(self, other):
  172. return Infix(lambda x, self=self, other=other: self.function(other, x))
  173. def __rshift__(self, other):
  174. return self.function(other)
  175. def __rmatmul__(self, other):
  176. return Infix(lambda x, self=self, other=other: self.function(other, x))
  177. def __matmul__(self, other):
  178. return self.function(other)
  179. def __call__(self, value1, value2):
  180. return self.function(value1, value2) # pragma: no cover
  181. nsBinds = { # noqa: N816
  182. "skos": "http://www.w3.org/2004/02/skos/core#",
  183. "rdf": RDF,
  184. "rdfs": RDFS,
  185. "owl": OWL,
  186. "list": URIRef("http://www.w3.org/2000/10/swap/list#"),
  187. "dc": "http://purl.org/dc/elements/1.1/",
  188. }
  189. def generateQName(graph, uri): # noqa: N802
  190. prefix, uri, localname = graph.compute_qname(classOrIdentifier(uri))
  191. return ":".join([prefix, localname])
  192. def classOrTerm(thing): # noqa: N802
  193. if isinstance(thing, Class):
  194. return thing.identifier
  195. else:
  196. assert isinstance(thing, (URIRef, BNode, Literal))
  197. return thing
  198. def classOrIdentifier(thing): # noqa: N802
  199. if isinstance(thing, (Property, Class)):
  200. return thing.identifier
  201. else:
  202. assert isinstance(thing, (URIRef, BNode)), (
  203. "Expecting a Class, Property, URIRef, or BNode.. not a %s" % thing
  204. )
  205. return thing
  206. def propertyOrIdentifier(thing): # noqa: N802
  207. if isinstance(thing, Property):
  208. return thing.identifier
  209. else:
  210. assert isinstance(thing, URIRef)
  211. return thing
  212. def manchesterSyntax( # noqa: N802
  213. thing, store, boolean=None, transientList=False # noqa: N803
  214. ):
  215. """
  216. Core serialization
  217. thing is a Class and is processed as a subject
  218. store is an RDFLib Graph to be queried about thing
  219. """
  220. assert thing is not None
  221. if boolean:
  222. if transientList:
  223. livechildren = iter(thing)
  224. children = [manchesterSyntax(child, store) for child in thing]
  225. else:
  226. livechildren = iter(Collection(store, thing))
  227. children = [
  228. manchesterSyntax(child, store) for child in Collection(store, thing)
  229. ]
  230. if boolean == OWL.intersectionOf:
  231. childlist = []
  232. named = []
  233. for child in livechildren:
  234. if isinstance(child, URIRef):
  235. named.append(child)
  236. else:
  237. childlist.append(child)
  238. if named:
  239. def castToQName(x): # noqa: N802
  240. prefix, uri, localname = store.compute_qname(x)
  241. return ":".join([prefix, localname])
  242. if len(named) > 1:
  243. prefix = "( " + " AND ".join(map(castToQName, named)) + " )"
  244. else:
  245. prefix = manchesterSyntax(named[0], store)
  246. if childlist:
  247. return (
  248. str(prefix)
  249. + " THAT "
  250. + " AND ".join(
  251. [str(manchesterSyntax(x, store)) for x in childlist]
  252. )
  253. )
  254. else:
  255. return prefix
  256. else:
  257. return "( " + " AND ".join([str(c) for c in children]) + " )"
  258. elif boolean == OWL.unionOf:
  259. return "( " + " OR ".join([str(c) for c in children]) + " )"
  260. elif boolean == OWL.oneOf:
  261. return "{ " + " ".join([str(c) for c in children]) + " }"
  262. else:
  263. assert boolean == OWL.complementOf
  264. elif OWL.Restriction in store.objects(subject=thing, predicate=RDF.type):
  265. prop = list(store.objects(subject=thing, predicate=OWL.onProperty))[0]
  266. prefix, uri, localname = store.compute_qname(prop)
  267. propstring = ":".join([prefix, localname])
  268. label = first(store.objects(subject=prop, predicate=RDFS.label))
  269. if label:
  270. propstring = "'%s'" % label
  271. for onlyclass in store.objects(subject=thing, predicate=OWL.allValuesFrom):
  272. return "( %s ONLY %s )" % (propstring, manchesterSyntax(onlyclass, store))
  273. for val in store.objects(subject=thing, predicate=OWL.hasValue):
  274. return "( %s VALUE %s )" % (propstring, manchesterSyntax(val, store))
  275. for someclass in store.objects(subject=thing, predicate=OWL.someValuesFrom):
  276. return "( %s SOME %s )" % (propstring, manchesterSyntax(someclass, store))
  277. cardlookup = {
  278. OWL.maxCardinality: "MAX",
  279. OWL.minCardinality: "MIN",
  280. OWL.cardinality: "EQUALS",
  281. }
  282. for _s, p, o in store.triples_choices((thing, list(cardlookup.keys()), None)):
  283. return "( %s %s %s )" % (propstring, cardlookup[p], o)
  284. # is thing a complement of anything
  285. compl = list(store.objects(subject=thing, predicate=OWL.complementOf))
  286. if compl:
  287. return "( NOT %s )" % (manchesterSyntax(compl[0], store))
  288. else:
  289. prolog = "\n".join(["PREFIX %s: <%s>" % (k, nsBinds[k]) for k in nsBinds])
  290. qstr = (
  291. prolog
  292. + "\nSELECT ?p ?bool WHERE {?class a owl:Class; ?p ?bool ."
  293. + "?bool rdf:first ?foo }"
  294. )
  295. initb = {Variable("?class"): thing}
  296. for boolprop, col in store.query(qstr, processor="sparql", initBindings=initb):
  297. if not isinstance(thing, URIRef):
  298. return manchesterSyntax(col, store, boolean=boolprop)
  299. try:
  300. prefix, uri, localname = store.compute_qname(thing)
  301. qname = ":".join([prefix, localname])
  302. except Exception:
  303. if isinstance(thing, BNode):
  304. return thing.n3()
  305. # Expect the unexpected
  306. return thing.identifier if not isinstance(thing, str) else thing
  307. label = first(Class(thing, graph=store).label)
  308. if label:
  309. return label
  310. else:
  311. return qname
  312. def GetIdentifiedClasses(graph): # noqa: N802
  313. for c in graph.subjects(predicate=RDF.type, object=OWL.Class):
  314. if isinstance(c, URIRef):
  315. yield Class(c)
  316. class TermDeletionHelper:
  317. def __init__(self, prop):
  318. self.prop = prop
  319. def __call__(self, f):
  320. def _remover(inst):
  321. inst.graph.remove((inst.identifier, self.prop, None))
  322. return _remover
  323. class Individual:
  324. """
  325. A typed individual, the base class of the InfixOWL classes.
  326. """
  327. factoryGraph = Graph() # noqa: N815
  328. def serialize(self, graph):
  329. for fact in self.factoryGraph.triples((self.identifier, None, None)):
  330. graph.add(fact)
  331. def __init__(self, identifier=None, graph=None):
  332. self.__identifier = identifier is not None and identifier or BNode()
  333. if graph is None:
  334. self.graph = self.factoryGraph
  335. else:
  336. self.graph = graph
  337. self.qname = None
  338. if not isinstance(self.identifier, BNode):
  339. try:
  340. prefix, uri, localname = self.graph.compute_qname(self.identifier)
  341. self.qname = ":".join([prefix, localname])
  342. except Exception: # pragma: no cover
  343. pass # pragma: no cover
  344. def clearInDegree(self): # noqa: N802
  345. """
  346. Remove references to this individual as an object in the
  347. backing store.
  348. """
  349. self.graph.remove((None, None, self.identifier))
  350. def clearOutDegree(self): # noqa: N802
  351. """
  352. Remove all statements to this individual as a subject in the
  353. backing store. Note that this only removes the statements
  354. themselves, not the blank node closure so there is a chance
  355. that this will cause orphaned blank nodes to remain in the
  356. graph.
  357. """
  358. self.graph.remove((self.identifier, None, None))
  359. def delete(self):
  360. """
  361. Delete the individual from the graph, clearing the in and
  362. out degrees.
  363. """
  364. self.clearInDegree()
  365. self.clearOutDegree()
  366. def replace(self, other):
  367. """
  368. Replace the individual in the graph with the given other,
  369. causing all triples that refer to it to be changed and then
  370. delete the individual.
  371. ```python
  372. >>> g = Graph()
  373. >>> b = Individual(OWL.Restriction, g)
  374. >>> b.type = RDFS.Resource
  375. >>> len(list(b.type))
  376. 1
  377. >>> del b.type
  378. >>> len(list(b.type))
  379. 0
  380. ```
  381. """
  382. for s, p, _o in self.graph.triples((None, None, self.identifier)):
  383. self.graph.add((s, p, classOrIdentifier(other)))
  384. self.delete()
  385. def _get_type(self) -> Iterable[_ObjectType]:
  386. for _t in self.graph.objects(subject=self.identifier, predicate=RDF.type):
  387. yield _t
  388. def _set_type(self, kind: Union[Individual, Identifier, Iterable[_ObjectType]]):
  389. if not kind:
  390. return
  391. if isinstance(kind, (Individual, Identifier)):
  392. self.graph.add((self.identifier, RDF.type, classOrIdentifier(kind)))
  393. else:
  394. for c in kind:
  395. assert isinstance(c, (Individual, Identifier))
  396. self.graph.add((self.identifier, RDF.type, classOrIdentifier(c)))
  397. @TermDeletionHelper(RDF.type)
  398. def _delete_type(self):
  399. """
  400. ```python
  401. >>> g = Graph()
  402. >>> b = Individual(OWL.Restriction, g)
  403. >>> b.type = RDFS.Resource
  404. >>> len(list(b.type))
  405. 1
  406. >>> del b.type
  407. >>> len(list(b.type))
  408. 0
  409. ```
  410. """
  411. pass # pragma: no cover
  412. type = property(_get_type, _set_type, _delete_type)
  413. def _get_identifier(self) -> Identifier:
  414. return self.__identifier
  415. def _set_identifier(self, i: Identifier):
  416. assert i
  417. if i != self.__identifier:
  418. oldstatements_out = [
  419. (p, o)
  420. for s, p, o in self.graph.triples((self.__identifier, None, None))
  421. ]
  422. oldstatements_in = [
  423. (s, p)
  424. for s, p, o in self.graph.triples((None, None, self.__identifier))
  425. ]
  426. for p1, o1 in oldstatements_out:
  427. self.graph.remove((self.__identifier, p1, o1))
  428. for s1, p1 in oldstatements_in:
  429. self.graph.remove((s1, p1, self.__identifier))
  430. self.__identifier = i
  431. self.graph.addN([(i, p1, o1, self.graph) for p1, o1 in oldstatements_out])
  432. self.graph.addN([(s1, p1, i, self.graph) for s1, p1 in oldstatements_in])
  433. if not isinstance(i, BNode):
  434. try:
  435. prefix, uri, localname = self.graph.compute_qname(i)
  436. self.qname = ":".join([prefix, localname])
  437. except Exception: # pragma: no cover
  438. pass # pragma: no cover
  439. identifier = property(_get_identifier, _set_identifier)
  440. def _get_sameAs(self) -> Iterable[_ObjectType]: # noqa: N802
  441. for _t in self.graph.objects(subject=self.identifier, predicate=OWL.sameAs):
  442. yield _t
  443. def _set_sameAs( # noqa: N802
  444. self, term: Union[Individual, Identifier, Iterable[_ObjectType]]
  445. ):
  446. # if not kind:
  447. # return
  448. if isinstance(term, (Individual, Identifier)):
  449. self.graph.add((self.identifier, OWL.sameAs, classOrIdentifier(term)))
  450. else:
  451. for c in term:
  452. assert isinstance(c, (Individual, Identifier))
  453. self.graph.add((self.identifier, OWL.sameAs, classOrIdentifier(c)))
  454. @TermDeletionHelper(OWL.sameAs)
  455. def _delete_sameAs(self): # noqa: N802
  456. pass # pragma: no cover
  457. sameAs = property(_get_sameAs, _set_sameAs, _delete_sameAs) # noqa: N815
  458. ACE_NS = Namespace("http://attempto.ifi.uzh.ch/ace_lexicon#")
  459. class AnnotatableTerms(Individual):
  460. """Terms in an OWL ontology with rdfs:label and rdfs:comment
  461. Interface with ATTEMPTO (http://attempto.ifi.uzh.ch/site)
  462. ## Verbalisation of OWL entity IRIS
  463. ### How are OWL entity IRIs verbalized?
  464. The OWL verbalizer maps OWL entity IRIs to ACE content words such
  465. that
  466. - OWL individuals map to ACE proper names (PN)
  467. - OWL classes map to ACE common nouns (CN)
  468. - OWL properties map to ACE transitive verbs (TV)
  469. There are 6 morphological categories that determine the surface form
  470. of an IRI:
  471. - singular form of a proper name (e.g. John)
  472. - singular form of a common noun (e.g. man)
  473. - plural form of a common noun (e.g. men)
  474. - singular form of a transitive verb (e.g. mans)
  475. - plural form of a transitive verb (e.g. man)
  476. - past participle form a transitive verb (e.g. manned)
  477. The user has full control over the eventual surface forms of the IRIs
  478. but has to choose them in terms of the above categories.
  479. Furthermore,
  480. - the surface forms must be legal ACE content words (e.g. they
  481. should not contain punctuation symbols);
  482. - the mapping of IRIs to surface forms must be bidirectional
  483. within the same word class, in order to be able to (if needed)
  484. parse the verbalization back into OWL in a semantics preserving
  485. way.
  486. ### Using the lexicon
  487. It is possible to specify the mapping of IRIs to surface forms using
  488. the following annotation properties:
  489. ```
  490. http://attempto.ifi.uzh.ch/ace_lexicon#PN_sg
  491. http://attempto.ifi.uzh.ch/ace_lexicon#CN_sg
  492. http://attempto.ifi.uzh.ch/ace_lexicon#CN_pl
  493. http://attempto.ifi.uzh.ch/ace_lexicon#TV_sg
  494. http://attempto.ifi.uzh.ch/ace_lexicon#TV_pl
  495. http://attempto.ifi.uzh.ch/ace_lexicon#TV_vbg
  496. ```
  497. For example, the following axioms state that if the IRI "#man" is used
  498. as a plural common noun, then the wordform men must be used by the
  499. verbalizer. If, however, it is used as a singular transitive verb,
  500. then mans must be used.
  501. ```xml
  502. <AnnotationAssertion>
  503. <AnnotationProperty IRI="http://attempto.ifi.uzh.ch/ace_lexicon#CN_pl"/>
  504. <IRI>#man</IRI>
  505. <Literal datatypeIRI="&xsd;string">men</Literal>
  506. </AnnotationAssertion>
  507. <AnnotationAssertion>
  508. <AnnotationProperty IRI="http://attempto.ifi.uzh.ch/ace_lexicon#TV_sg"/>
  509. <IRI>#man</IRI>
  510. <Literal datatypeIRI="&xsd;string">mans</Literal>
  511. </AnnotationAssertion>
  512. ```
  513. """
  514. def __init__(
  515. self,
  516. identifier,
  517. graph=None,
  518. nameAnnotation=None, # noqa: N803
  519. nameIsLabel=False, # noqa: N803
  520. ):
  521. super(AnnotatableTerms, self).__init__(identifier, graph)
  522. if nameAnnotation:
  523. self.setupACEAnnotations()
  524. self.PN_sgprop.extent = [
  525. (self.identifier, self.handleAnnotation(nameAnnotation))
  526. ]
  527. if nameIsLabel:
  528. self.label = [nameAnnotation]
  529. def handleAnnotation(self, val): # noqa: N802
  530. return val if isinstance(val, Literal) else Literal(val)
  531. def setupACEAnnotations(self): # noqa: N802
  532. self.graph.bind("ace", ACE_NS, override=False)
  533. # PN_sg singular form of a proper name ()
  534. self.PN_sgprop = Property(
  535. ACE_NS.PN_sg, baseType=OWL.AnnotationProperty, graph=self.graph
  536. )
  537. # CN_sg singular form of a common noun
  538. self.CN_sgprop = Property(
  539. ACE_NS.CN_sg, baseType=OWL.AnnotationProperty, graph=self.graph
  540. )
  541. # CN_pl plural form of a common noun
  542. self.CN_plprop = Property(
  543. ACE_NS.CN_pl, baseType=OWL.AnnotationProperty, graph=self.graph
  544. )
  545. # singular form of a transitive verb
  546. self.tv_sgprop = Property(
  547. ACE_NS.TV_sg, baseType=OWL.AnnotationProperty, graph=self.graph
  548. )
  549. # plural form of a transitive verb
  550. self.tv_plprop = Property(
  551. ACE_NS.TV_pl, baseType=OWL.AnnotationProperty, graph=self.graph
  552. )
  553. # past participle form a transitive verb
  554. self.tv_vbgprop = Property(
  555. ACE_NS.TV_vbg, baseType=OWL.AnnotationProperty, graph=self.graph
  556. )
  557. def _get_comment(self):
  558. for comment in self.graph.objects(
  559. subject=self.identifier, predicate=RDFS.comment
  560. ):
  561. yield comment
  562. def _set_comment(self, comment):
  563. if not comment:
  564. return
  565. if isinstance(comment, Identifier):
  566. self.graph.add((self.identifier, RDFS.comment, comment))
  567. else:
  568. for c in comment:
  569. self.graph.add((self.identifier, RDFS.comment, c))
  570. @TermDeletionHelper(RDFS.comment)
  571. def _del_comment(self):
  572. pass # pragma: no cover
  573. comment = property(_get_comment, _set_comment, _del_comment)
  574. def _get_seealso(self):
  575. for seealso in self.graph.objects(
  576. subject=self.identifier, predicate=RDFS.seeAlso
  577. ):
  578. yield seealso
  579. def _set_seealso(self, seealsos):
  580. if not seealsos:
  581. return
  582. for s in seealsos:
  583. self.graph.add((self.identifier, RDFS.seeAlso, s))
  584. @TermDeletionHelper(RDFS.seeAlso)
  585. def _del_seealso(self):
  586. pass # pragma: no cover
  587. seeAlso = property(_get_seealso, _set_seealso, _del_seealso) # noqa: N815
  588. def _get_label(self):
  589. for label in self.graph.objects(subject=self.identifier, predicate=RDFS.label):
  590. yield label
  591. def _set_label(self, label):
  592. if not label:
  593. return
  594. if isinstance(label, Identifier):
  595. self.graph.add((self.identifier, RDFS.label, label))
  596. else:
  597. for l_ in label:
  598. self.graph.add((self.identifier, RDFS.label, l_))
  599. @TermDeletionHelper(RDFS.label)
  600. def _delete_label(self):
  601. """
  602. ```python
  603. >>> g = Graph()
  604. >>> b = Individual(OWL.Restriction,g)
  605. >>> b.label = Literal('boo')
  606. >>> len(list(b.label))
  607. 1
  608. >>> del b.label
  609. >>> len(list(b.label))
  610. 0
  611. ```
  612. """
  613. pass # pragma: no cover
  614. label = property(_get_label, _set_label, _delete_label)
  615. class Ontology(AnnotatableTerms):
  616. """The owl ontology metadata"""
  617. def __init__(self, identifier=None, imports=None, comment=None, graph=None):
  618. super(Ontology, self).__init__(identifier, graph)
  619. self.imports = [] if imports is None else imports
  620. self.comment = [] if comment is None else comment
  621. if (self.identifier, RDF.type, OWL.Ontology) not in self.graph:
  622. self.graph.add((self.identifier, RDF.type, OWL.Ontology))
  623. def setVersion(self, version): # noqa: N802
  624. self.graph.set((self.identifier, OWL.versionInfo, version))
  625. def _get_imports(self):
  626. for owl in self.graph.objects(
  627. subject=self.identifier, predicate=OWL["imports"]
  628. ):
  629. yield owl
  630. def _set_imports(self, other):
  631. if not other:
  632. return
  633. for o in other:
  634. self.graph.add((self.identifier, OWL["imports"], o))
  635. @TermDeletionHelper(OWL["imports"])
  636. def _del_imports(self):
  637. pass # pragma: no cover
  638. imports = property(_get_imports, _set_imports, _del_imports)
  639. def AllClasses(graph): # noqa: N802
  640. for c in set(graph.subjects(predicate=RDF.type, object=OWL.Class)):
  641. yield Class(c)
  642. def AllProperties(graph): # noqa: N802
  643. prevprops = set()
  644. for s, _p, o in graph.triples_choices(
  645. (
  646. None,
  647. RDF.type,
  648. [
  649. OWL.SymmetricProperty,
  650. OWL.FunctionalProperty,
  651. OWL.InverseFunctionalProperty,
  652. OWL.TransitiveProperty,
  653. OWL.DatatypeProperty,
  654. OWL.ObjectProperty,
  655. OWL.AnnotationProperty,
  656. ],
  657. )
  658. ):
  659. if o in [
  660. OWL.SymmetricProperty,
  661. OWL.InverseFunctionalProperty,
  662. OWL.TransitiveProperty,
  663. OWL.ObjectProperty,
  664. ]:
  665. bType = OWL.ObjectProperty # noqa: N806
  666. else:
  667. bType = OWL.DatatypeProperty # noqa: N806
  668. if s not in prevprops:
  669. prevprops.add(s)
  670. yield Property(s, graph=graph, baseType=bType)
  671. class ClassNamespaceFactory(Namespace):
  672. def term(self, name):
  673. return Class(URIRef(self + name))
  674. def __getitem__(self, key, default=None):
  675. return self.term(key)
  676. def __getattr__(self, name):
  677. if name.startswith("__"): # ignore any special Python names!
  678. raise AttributeError
  679. else:
  680. return self.term(name)
  681. CLASS_RELATIONS = set(
  682. Namespace("http://www.w3.org/2002/07/owl#resourceProperties")
  683. ).difference(
  684. [
  685. OWL.onProperty,
  686. OWL.allValuesFrom,
  687. OWL.hasValue,
  688. OWL.someValuesFrom,
  689. OWL.inverseOf,
  690. OWL.imports,
  691. OWL.versionInfo,
  692. OWL.backwardCompatibleWith,
  693. OWL.incompatibleWith,
  694. OWL.unionOf,
  695. OWL.intersectionOf,
  696. OWL.oneOf,
  697. ]
  698. )
  699. def ComponentTerms(cls): # noqa: N802
  700. """
  701. Takes a Class instance and returns a generator over the classes that
  702. are involved in its definition, ignoring unnamed classes
  703. """
  704. if OWL.Restriction in cls.type:
  705. try:
  706. cls = CastClass(cls, Individual.factoryGraph)
  707. for _s, _p, inner_class_id in cls.factoryGraph.triples_choices(
  708. (cls.identifier, [OWL.allValuesFrom, OWL.someValuesFrom], None)
  709. ):
  710. inner_class = Class(inner_class_id, skipOWLClassMembership=True)
  711. if isinstance(inner_class_id, BNode):
  712. for _c in ComponentTerms(inner_class):
  713. yield _c
  714. else:
  715. yield inner_class
  716. except Exception: # pragma: no cover
  717. pass # pragma: no cover
  718. else:
  719. cls = CastClass(cls, Individual.factoryGraph)
  720. if isinstance(cls, BooleanClass):
  721. for _cls in cls:
  722. _cls = Class(_cls, skipOWLClassMembership=True)
  723. if isinstance(_cls.identifier, BNode):
  724. for _c in ComponentTerms(_cls):
  725. yield _c
  726. else:
  727. yield _cls
  728. else:
  729. for inner_class in cls.subClassOf:
  730. if isinstance(inner_class.identifier, BNode):
  731. for _c in ComponentTerms(inner_class):
  732. yield _c
  733. else:
  734. yield inner_class
  735. for _s, _p, o in cls.factoryGraph.triples_choices(
  736. (classOrIdentifier(cls), CLASS_RELATIONS, None)
  737. ):
  738. if isinstance(o, BNode):
  739. for _c in ComponentTerms(CastClass(o, Individual.factoryGraph)):
  740. yield _c
  741. else:
  742. yield inner_class
  743. def DeepClassClear(class_to_prune): # noqa: N802
  744. """
  745. Recursively clear the given class, continuing
  746. where any related class is an anonymous class
  747. ```python
  748. >>> EX = Namespace("http://example.com/")
  749. >>> g = Graph()
  750. >>> g.bind("ex", EX, override=False)
  751. >>> Individual.factoryGraph = g
  752. >>> classB = Class(EX.B)
  753. >>> classC = Class(EX.C)
  754. >>> classD = Class(EX.D)
  755. >>> classE = Class(EX.E)
  756. >>> classF = Class(EX.F)
  757. >>> anonClass = EX.someProp @ some @ classD
  758. >>> classF += anonClass
  759. >>> list(anonClass.subClassOf)
  760. [Class: ex:F ]
  761. >>> classA = classE | classF | anonClass
  762. >>> classB += classA
  763. >>> classA.equivalentClass = [Class()]
  764. >>> classB.subClassOf = [EX.someProp @ some @ classC]
  765. >>> classA
  766. ( ex:E OR ex:F OR ( ex:someProp SOME ex:D ) )
  767. >>> DeepClassClear(classA)
  768. >>> classA
  769. ( )
  770. >>> list(anonClass.subClassOf)
  771. []
  772. >>> classB
  773. Class: ex:B SubClassOf: ( ex:someProp SOME ex:C )
  774. >>> otherClass = classD | anonClass
  775. >>> otherClass
  776. ( ex:D OR ( ex:someProp SOME ex:D ) )
  777. >>> DeepClassClear(otherClass)
  778. >>> otherClass
  779. ( )
  780. >>> otherClass.delete()
  781. >>> list(g.triples((otherClass.identifier, None, None)))
  782. []
  783. ```
  784. """
  785. def deepClearIfBNode(_class): # noqa: N802
  786. if isinstance(classOrIdentifier(_class), BNode):
  787. DeepClassClear(_class)
  788. class_to_prune = CastClass(class_to_prune, Individual.factoryGraph)
  789. for c in class_to_prune.subClassOf:
  790. deepClearIfBNode(c)
  791. class_to_prune.graph.remove((class_to_prune.identifier, RDFS.subClassOf, None))
  792. for c in class_to_prune.equivalentClass:
  793. deepClearIfBNode(c)
  794. class_to_prune.graph.remove((class_to_prune.identifier, OWL.equivalentClass, None))
  795. inverse_class = class_to_prune.complementOf
  796. if inverse_class:
  797. class_to_prune.graph.remove((class_to_prune.identifier, OWL.complementOf, None))
  798. deepClearIfBNode(inverse_class)
  799. if isinstance(class_to_prune, BooleanClass):
  800. for c in class_to_prune:
  801. deepClearIfBNode(c)
  802. class_to_prune.clear()
  803. class_to_prune.graph.remove(
  804. (class_to_prune.identifier, class_to_prune._operator, None)
  805. )
  806. class MalformedClass(ValueError): # noqa: N818
  807. """
  808. !!! warning "Deprecated"
  809. This class will be removed in version `7.0.0`.
  810. """
  811. pass
  812. class MalformedClassError(MalformedClass):
  813. def __init__(self, msg):
  814. self.msg = msg
  815. def __repr__(self):
  816. return self.msg
  817. def CastClass(c, graph=None): # noqa: N802
  818. graph = graph is None and c.factoryGraph or graph
  819. for kind in graph.objects(subject=classOrIdentifier(c), predicate=RDF.type):
  820. if kind == OWL.Restriction:
  821. kwargs = {"identifier": classOrIdentifier(c), "graph": graph}
  822. for _s, p, o in graph.triples((classOrIdentifier(c), None, None)):
  823. if p != RDF.type:
  824. if p == OWL.onProperty:
  825. kwargs["onProperty"] = o
  826. else:
  827. if p not in Restriction.restrictionKinds:
  828. continue
  829. kwargs[str(p.split(str(OWL))[-1])] = o
  830. if not set(
  831. [str(i.split(str(OWL))[-1]) for i in Restriction.restrictionKinds]
  832. ).intersection(kwargs):
  833. raise MalformedClassError("Malformed owl:Restriction")
  834. return Restriction(**kwargs)
  835. else:
  836. for _s, p, _o in graph.triples_choices(
  837. (
  838. classOrIdentifier(c),
  839. [OWL.intersectionOf, OWL.unionOf, OWL.oneOf],
  840. None,
  841. )
  842. ):
  843. if p == OWL.oneOf:
  844. return EnumeratedClass(classOrIdentifier(c), graph=graph)
  845. else:
  846. return BooleanClass(classOrIdentifier(c), operator=p, graph=graph)
  847. # assert (classOrIdentifier(c),RDF.type,OWL.Class) in graph
  848. return Class(classOrIdentifier(c), graph=graph, skipOWLClassMembership=True)
  849. class Class(AnnotatableTerms):
  850. """'General form' for classes:
  851. The Manchester Syntax (supported in Protege) is used as the basis for the
  852. form of this class
  853. See: http://owl-workshop.man.ac.uk/acceptedLong/submission_9.pdf:
  854. ```
  855. [Annotation]
  856. ‘Class:’ classID {Annotation
  857. ( (‘SubClassOf:’ ClassExpression)
  858. | (‘EquivalentTo’ ClassExpression)
  859. | (’DisjointWith’ ClassExpression)) }
  860. ```
  861. Appropriate excerpts from OWL Reference:
  862. ".. Subclass axioms provide us with partial definitions: they represent
  863. necessary but not sufficient conditions for establishing class
  864. membership of an individual."
  865. ".. A class axiom may contain (multiple) owl:equivalentClass statements"
  866. "..A class axiom may also contain (multiple) owl:disjointWith statements.."
  867. "..An owl:complementOf property links a class to precisely one class
  868. description."
  869. """
  870. def _serialize(self, graph):
  871. for cl in self.subClassOf:
  872. CastClass(cl, self.graph).serialize(graph)
  873. for cl in self.equivalentClass:
  874. CastClass(cl, self.graph).serialize(graph)
  875. for cl in self.disjointWith:
  876. CastClass(cl, self.graph).serialize(graph)
  877. if self.complementOf:
  878. CastClass(self.complementOf, self.graph).serialize(graph)
  879. def serialize(self, graph):
  880. for fact in self.graph.triples((self.identifier, None, None)):
  881. graph.add(fact)
  882. self._serialize(graph)
  883. def setupNounAnnotations(self, noun_annotations): # noqa: N802
  884. if isinstance(noun_annotations, tuple):
  885. cn_sgprop, cn_plprop = noun_annotations
  886. else:
  887. cn_sgprop = noun_annotations
  888. cn_plprop = noun_annotations
  889. if cn_sgprop:
  890. self.CN_sgprop.extent = [
  891. (self.identifier, self.handleAnnotation(cn_sgprop))
  892. ]
  893. if cn_plprop:
  894. self.CN_plprop.extent = [
  895. (self.identifier, self.handleAnnotation(cn_plprop))
  896. ]
  897. def __init__(
  898. self,
  899. identifier=None,
  900. subClassOf=None, # noqa: N803
  901. equivalentClass=None, # noqa: N803
  902. disjointWith=None, # noqa: N803
  903. complementOf=None, # noqa: N803
  904. graph=None,
  905. skipOWLClassMembership=False, # noqa: N803
  906. comment=None,
  907. nounAnnotations=None, # noqa: N803
  908. nameAnnotation=None, # noqa: N803
  909. nameIsLabel=False, # noqa: N803
  910. ):
  911. super(Class, self).__init__(identifier, graph, nameAnnotation, nameIsLabel)
  912. if nounAnnotations:
  913. self.setupNounAnnotations(nounAnnotations)
  914. if (
  915. not skipOWLClassMembership
  916. and (self.identifier, RDF.type, OWL.Class) not in self.graph
  917. and (self.identifier, RDF.type, OWL.Restriction) not in self.graph
  918. ):
  919. self.graph.add((self.identifier, RDF.type, OWL.Class))
  920. self.subClassOf = [] if subClassOf is None else subClassOf
  921. self.equivalentClass = [] if equivalentClass is None else equivalentClass
  922. self.disjointWith = [] if disjointWith is None else disjointWith
  923. if complementOf:
  924. self.complementOf = complementOf
  925. self.comment = [] if comment is None else comment
  926. def _get_extent(self, graph=None):
  927. for member in (graph is None and self.graph or graph).subjects(
  928. predicate=RDF.type, object=self.identifier
  929. ):
  930. yield member
  931. def _set_extent(self, other):
  932. if not other:
  933. return
  934. for m in other:
  935. self.graph.add((classOrIdentifier(m), RDF.type, self.identifier))
  936. @TermDeletionHelper(RDF.type)
  937. def _del_type(self):
  938. pass # pragma: no cover
  939. extent = property(_get_extent, _set_extent, _del_type)
  940. def _get_annotation(self, term=RDFS.label):
  941. for annotation in self.graph.objects(subject=self.identifier, predicate=term):
  942. yield annotation
  943. annotation = property(_get_annotation, lambda x: x) # type: ignore[arg-type,misc]
  944. def _get_extentquery(self):
  945. return (Variable("CLASS"), RDF.type, self.identifier)
  946. def _set_extentquery(self, other):
  947. pass # pragma: no cover
  948. extentQuery = property(_get_extentquery, _set_extentquery) # noqa: N815
  949. def __hash__(self):
  950. """
  951. >>> b = Class(OWL.Restriction)
  952. >>> c = Class(OWL.Restriction)
  953. >>> len(set([b,c]))
  954. 1
  955. """
  956. return hash(self.identifier)
  957. def __eq__(self, other):
  958. assert isinstance(other, Class), repr(other)
  959. return self.identifier == other.identifier
  960. def __iadd__(self, other):
  961. assert isinstance(other, Class)
  962. other.subClassOf = [self]
  963. return self
  964. def __isub__(self, other):
  965. assert isinstance(other, Class)
  966. self.graph.remove((classOrIdentifier(other), RDFS.subClassOf, self.identifier))
  967. return self
  968. def __invert__(self):
  969. """
  970. Shorthand for Manchester syntax's not operator
  971. """
  972. return Class(complementOf=self)
  973. def __or__(self, other):
  974. """
  975. Construct an anonymous class description consisting of the union of
  976. this class and 'other' and return it
  977. """
  978. return BooleanClass(
  979. operator=OWL.unionOf, members=[self, other], graph=self.graph
  980. )
  981. def __and__(self, other):
  982. """
  983. Construct an anonymous class description consisting of the
  984. intersection of this class and 'other' and return it
  985. Chaining 3 intersections
  986. ```python
  987. >>> exNs = Namespace("http://example.com/")
  988. >>> g = Graph()
  989. >>> g.bind("ex", exNs, override=False)
  990. >>> female = Class(exNs.Female, graph=g)
  991. >>> human = Class(exNs.Human, graph=g)
  992. >>> youngPerson = Class(exNs.YoungPerson, graph=g)
  993. >>> youngWoman = female & human & youngPerson
  994. >>> youngWoman # doctest: +SKIP
  995. ex:YoungPerson THAT ( ex:Female AND ex:Human )
  996. >>> isinstance(youngWoman, BooleanClass)
  997. True
  998. >>> isinstance(youngWoman.identifier, BNode)
  999. True
  1000. ```
  1001. """
  1002. return BooleanClass(
  1003. operator=OWL.intersectionOf, members=[self, other], graph=self.graph
  1004. )
  1005. def _get_subclassof(self):
  1006. for anc in self.graph.objects(
  1007. subject=self.identifier, predicate=RDFS.subClassOf
  1008. ):
  1009. yield Class(anc, graph=self.graph, skipOWLClassMembership=True)
  1010. def _set_subclassof(self, other):
  1011. if not other:
  1012. return
  1013. for sc in other:
  1014. self.graph.add((self.identifier, RDFS.subClassOf, classOrIdentifier(sc)))
  1015. @TermDeletionHelper(RDFS.subClassOf)
  1016. def _del_subclassof(self):
  1017. pass # pragma: no cover
  1018. subClassOf = property( # noqa: N815
  1019. _get_subclassof, _set_subclassof, _del_subclassof
  1020. )
  1021. def _get_equivalentclass(self):
  1022. for ec in self.graph.objects(
  1023. subject=self.identifier, predicate=OWL.equivalentClass
  1024. ):
  1025. yield Class(ec, graph=self.graph)
  1026. def _set_equivalentclass(self, other):
  1027. if not other:
  1028. return
  1029. for sc in other:
  1030. self.graph.add(
  1031. (self.identifier, OWL.equivalentClass, classOrIdentifier(sc))
  1032. )
  1033. @TermDeletionHelper(OWL.equivalentClass)
  1034. def _del_equivalentclass(self):
  1035. pass # pragma: no cover
  1036. equivalentClass = property( # noqa: N815
  1037. _get_equivalentclass, _set_equivalentclass, _del_equivalentclass
  1038. )
  1039. def _get_disjointwith(self):
  1040. for dc in self.graph.objects(
  1041. subject=self.identifier, predicate=OWL.disjointWith
  1042. ):
  1043. yield Class(dc, graph=self.graph)
  1044. def _set_disjointwith(self, other):
  1045. if not other:
  1046. return
  1047. for c in other:
  1048. self.graph.add((self.identifier, OWL.disjointWith, classOrIdentifier(c)))
  1049. @TermDeletionHelper(OWL.disjointWith)
  1050. def _del_disjointwith(self):
  1051. pass # pragma: no cover
  1052. disjointWith = property( # noqa: N815
  1053. _get_disjointwith, _set_disjointwith, _del_disjointwith
  1054. )
  1055. def _get_complementof(self):
  1056. comp = list(
  1057. self.graph.objects(subject=self.identifier, predicate=OWL.complementOf)
  1058. )
  1059. if not comp:
  1060. return None
  1061. elif len(comp) == 1:
  1062. return Class(comp[0], graph=self.graph)
  1063. else:
  1064. raise Exception(len(comp))
  1065. def _set_complementof(self, other):
  1066. if not other:
  1067. return
  1068. self.graph.add((self.identifier, OWL.complementOf, classOrIdentifier(other)))
  1069. @TermDeletionHelper(OWL.complementOf)
  1070. def _del_complementof(self):
  1071. pass # pragma: no cover
  1072. complementOf = property( # noqa: N815
  1073. _get_complementof, _set_complementof, _del_complementof
  1074. )
  1075. def _get_parents(self):
  1076. """
  1077. computed attributes that returns a generator over taxonomic 'parents'
  1078. by disjunction, conjunction, and subsumption
  1079. ```python
  1080. >>> from rdflib.util import first
  1081. >>> exNs = Namespace('http://example.com/')
  1082. >>> g = Graph()
  1083. >>> g.bind("ex", exNs, override=False)
  1084. >>> Individual.factoryGraph = g
  1085. >>> brother = Class(exNs.Brother)
  1086. >>> sister = Class(exNs.Sister)
  1087. >>> sibling = brother | sister
  1088. >>> sibling.identifier = exNs.Sibling
  1089. >>> sibling
  1090. ( ex:Brother OR ex:Sister )
  1091. >>> first(brother.parents)
  1092. Class: ex:Sibling EquivalentTo: ( ex:Brother OR ex:Sister )
  1093. >>> parent = Class(exNs.Parent)
  1094. >>> male = Class(exNs.Male)
  1095. >>> father = parent & male
  1096. >>> father.identifier = exNs.Father
  1097. >>> list(father.parents)
  1098. [Class: ex:Parent , Class: ex:Male ]
  1099. ```
  1100. """
  1101. for parent in itertools.chain(self.subClassOf, self.equivalentClass):
  1102. yield parent
  1103. link = first(self.factoryGraph.subjects(RDF.first, self.identifier))
  1104. if link:
  1105. siblingslist = list(self.factoryGraph.transitive_subjects(RDF.rest, link))
  1106. if siblingslist:
  1107. collectionhead = siblingslist[-1]
  1108. else:
  1109. collectionhead = link
  1110. for disjointclass in self.factoryGraph.subjects(
  1111. OWL.unionOf, collectionhead
  1112. ):
  1113. if isinstance(disjointclass, URIRef):
  1114. yield Class(disjointclass, skipOWLClassMembership=True)
  1115. for rdf_list in self.factoryGraph.objects(self.identifier, OWL.intersectionOf):
  1116. for member in OWLRDFListProxy([rdf_list], graph=self.factoryGraph):
  1117. if isinstance(member, URIRef):
  1118. yield Class(member, skipOWLClassMembership=True)
  1119. parents = property(_get_parents)
  1120. def isPrimitive(self): # noqa: N802
  1121. if (self.identifier, RDF.type, OWL.Restriction) in self.graph:
  1122. return False
  1123. # sc = list(self.subClassOf)
  1124. ec = list(self.equivalentClass)
  1125. for _boolclass, p, rdf_list in self.graph.triples_choices(
  1126. # type error: Argument 1 to "triples_choices" of "Graph" has incompatible type "Tuple[Any, List[URIRef], None]"; expected "Union[Tuple[List[Node], Node, Node], Tuple[Node, List[Node], Node], Tuple[Node, Node, List[Node]]]"
  1127. (self.identifier, [OWL.intersectionOf, OWL.unionOf], None) # type: ignore[arg-type]
  1128. ):
  1129. ec.append(manchesterSyntax(rdf_list, self.graph, boolean=p))
  1130. for _e in ec:
  1131. return False
  1132. if self.complementOf:
  1133. return False
  1134. return True
  1135. def subSumpteeIds(self): # noqa: N802
  1136. for s in self.graph.subjects(predicate=RDFS.subClassOf, object=self.identifier):
  1137. yield s
  1138. # def __iter__(self):
  1139. # for s in self.graph.subjects(
  1140. # predicate=RDFS.subClassOf,object=self.identifier):
  1141. # yield Class(s,skipOWLClassMembership=True)
  1142. def __repr__(self):
  1143. return self.manchesterClass(full=False, normalization=True)
  1144. def manchesterClass(self, full=False, normalization=True): # noqa: N802
  1145. """
  1146. Returns the Manchester Syntax equivalent for this class
  1147. """
  1148. exprs = []
  1149. sc = list(self.subClassOf)
  1150. ec = list(self.equivalentClass)
  1151. for _boolclass, p, rdf_list in self.graph.triples_choices(
  1152. # type error: Argument 1 to "triples_choices" of "Graph" has incompatible type "Tuple[Any, List[URIRef], None]"; expected "Union[Tuple[List[Node], Node, Node], Tuple[Node, List[Node], Node], Tuple[Node, Node, List[Node]]]"
  1153. (self.identifier, [OWL.intersectionOf, OWL.unionOf], None) # type: ignore[arg-type]
  1154. ):
  1155. ec.append(manchesterSyntax(rdf_list, self.graph, boolean=p))
  1156. dc = list(self.disjointWith)
  1157. c = self.complementOf
  1158. if c:
  1159. dc.append(c)
  1160. klasskind = ""
  1161. label = list(self.graph.objects(self.identifier, RDFS.label))
  1162. # type error: Incompatible types in assignment (expression has type "str", variable has type "List[Node]")
  1163. # type error: Unsupported operand types for + ("str" and "Node")
  1164. label = label and "(" + label[0] + ")" or "" # type: ignore[assignment, operator]
  1165. if sc:
  1166. if full:
  1167. scjoin = "\n "
  1168. else:
  1169. scjoin = ", "
  1170. nec_statements = [
  1171. isinstance(s, Class)
  1172. and isinstance(self.identifier, BNode)
  1173. and repr(CastClass(s, self.graph))
  1174. or
  1175. # repr(BooleanClass(classOrIdentifier(s),
  1176. # operator=None,
  1177. # graph=self.graph)) or
  1178. manchesterSyntax(classOrIdentifier(s), self.graph)
  1179. for s in sc
  1180. ]
  1181. if nec_statements:
  1182. klasskind = "Primitive Type %s" % label
  1183. exprs.append(
  1184. "SubClassOf: %s" % scjoin.join([str(n) for n in nec_statements])
  1185. )
  1186. if full:
  1187. exprs[-1] = "\n " + exprs[-1]
  1188. if ec:
  1189. nec_suff_statements = [
  1190. isinstance(s, str)
  1191. and s
  1192. or manchesterSyntax(classOrIdentifier(s), self.graph)
  1193. for s in ec
  1194. ]
  1195. if nec_suff_statements:
  1196. klasskind = "A Defined Class %s" % label
  1197. exprs.append("EquivalentTo: %s" % ", ".join(nec_suff_statements))
  1198. if full:
  1199. exprs[-1] = "\n " + exprs[-1]
  1200. if dc:
  1201. exprs.append(
  1202. "DisjointWith %s\n"
  1203. % "\n ".join(
  1204. [manchesterSyntax(classOrIdentifier(s), self.graph) for s in dc]
  1205. )
  1206. )
  1207. if full:
  1208. exprs[-1] = "\n " + exprs[-1]
  1209. descr = list(self.graph.objects(self.identifier, RDFS.comment))
  1210. if full and normalization:
  1211. klassdescr = (
  1212. klasskind
  1213. and "\n ## %s ##" % klasskind
  1214. + (descr and "\n %s" % descr[0] or "")
  1215. + " . ".join(exprs)
  1216. or " . ".join(exprs)
  1217. )
  1218. else:
  1219. klassdescr = (
  1220. full
  1221. and (descr and "\n %s" % descr[0] or "")
  1222. or "" + " . ".join(exprs)
  1223. )
  1224. return (
  1225. isinstance(self.identifier, BNode)
  1226. and "Some Class "
  1227. or "Class: %s " % self.qname
  1228. ) + klassdescr
  1229. class OWLRDFListProxy:
  1230. def __init__(self, rdf_list, members=None, graph=None):
  1231. if graph:
  1232. self.graph = graph
  1233. members = [] if members is None else members
  1234. if rdf_list:
  1235. self._rdfList = Collection(self.graph, rdf_list[0])
  1236. for member in members:
  1237. if member not in self._rdfList:
  1238. self._rdfList.append(classOrIdentifier(member))
  1239. else:
  1240. self._rdfList = Collection(
  1241. self.graph, BNode(), [classOrIdentifier(m) for m in members]
  1242. )
  1243. # type error: "OWLRDFListProxy" has no attribute "identifier"
  1244. # type error: "OWLRDFListProxy" has no attribute "_operator"
  1245. self.graph.add((self.identifier, self._operator, self._rdfList.uri)) # type: ignore[attr-defined]
  1246. def __eq__(self, other):
  1247. """
  1248. Equivalence of boolean class constructors is determined by
  1249. equivalence of its members
  1250. """
  1251. assert isinstance(other, Class), repr(other) + repr(type(other))
  1252. if isinstance(other, BooleanClass):
  1253. length = len(self)
  1254. if length != len(other):
  1255. return False
  1256. else:
  1257. for idx in range(length):
  1258. if self[idx] != other[idx]:
  1259. return False
  1260. return True
  1261. else:
  1262. # type error: "OWLRDFListProxy" has no attribute "identifier"
  1263. return self.identifier == other.identifier # type: ignore[attr-defined]
  1264. # Redirect python list accessors to the underlying Collection instance
  1265. def __len__(self):
  1266. return len(self._rdfList)
  1267. def index(self, item):
  1268. return self._rdfList.index(classOrIdentifier(item))
  1269. def __getitem__(self, key):
  1270. return self._rdfList[key]
  1271. def __setitem__(self, key, value):
  1272. self._rdfList[key] = classOrIdentifier(value)
  1273. def __delitem__(self, key):
  1274. del self._rdfList[key]
  1275. def clear(self):
  1276. self._rdfList.clear()
  1277. def __iter__(self):
  1278. for item in self._rdfList:
  1279. yield item
  1280. def __contains__(self, item):
  1281. for i in self._rdfList:
  1282. if i == classOrIdentifier(item):
  1283. return 1
  1284. return 0
  1285. def append(self, item):
  1286. self._rdfList.append(item)
  1287. def __iadd__(self, other):
  1288. self._rdfList.append(classOrIdentifier(other))
  1289. return self
  1290. class EnumeratedClass(OWLRDFListProxy, Class):
  1291. """Class for owl:oneOf forms:
  1292. OWL Abstract Syntax is used
  1293. axiom ::= 'EnumeratedClass('
  1294. classID ['Deprecated'] { annotation } { individualID } ')'
  1295. ```python
  1296. >>> exNs = Namespace("http://example.com/")
  1297. >>> g = Graph()
  1298. >>> g.bind("ex", exNs, override=False)
  1299. >>> Individual.factoryGraph = g
  1300. >>> ogbujiBros = EnumeratedClass(exNs.ogbujicBros,
  1301. ... members=[exNs.chime,
  1302. ... exNs.uche,
  1303. ... exNs.ejike])
  1304. >>> ogbujiBros # doctest: +SKIP
  1305. { ex:chime ex:uche ex:ejike }
  1306. >>> col = Collection(g, first(
  1307. ... g.objects(predicate=OWL.oneOf, subject=ogbujiBros.identifier)))
  1308. >>> sorted([g.qname(item) for item in col])
  1309. ['ex:chime', 'ex:ejike', 'ex:uche']
  1310. >>> print(g.serialize(format='n3')) # doctest: +SKIP
  1311. @prefix ex: <http://example.com/> .
  1312. @prefix owl: <http://www.w3.org/2002/07/owl#> .
  1313. @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
  1314. <BLANKLINE>
  1315. ex:ogbujicBros a owl:Class;
  1316. owl:oneOf ( ex:chime ex:uche ex:ejike ) .
  1317. <BLANKLINE>
  1318. <BLANKLINE>
  1319. ```
  1320. """
  1321. _operator = OWL.oneOf
  1322. def isPrimitive(self): # noqa: N802
  1323. return False
  1324. def __init__(self, identifier=None, members=None, graph=None):
  1325. Class.__init__(self, identifier, graph=graph)
  1326. members = [] if members is None else members
  1327. rdfList = list( # noqa: N806
  1328. self.graph.objects(predicate=OWL.oneOf, subject=self.identifier)
  1329. )
  1330. OWLRDFListProxy.__init__(self, rdfList, members)
  1331. def __repr__(self):
  1332. """
  1333. Returns the Manchester Syntax equivalent for this class
  1334. """
  1335. return manchesterSyntax(self._rdfList.uri, self.graph, boolean=self._operator)
  1336. def serialize(self, graph):
  1337. clonedlist = Collection(graph, BNode())
  1338. for cl in self._rdfList:
  1339. clonedlist.append(cl)
  1340. CastClass(cl, self.graph).serialize(graph)
  1341. graph.add((self.identifier, self._operator, clonedlist.uri))
  1342. for s, p, o in self.graph.triples((self.identifier, None, None)):
  1343. if p != self._operator:
  1344. graph.add((s, p, o))
  1345. self._serialize(graph)
  1346. BooleanPredicates = [OWL.intersectionOf, OWL.unionOf]
  1347. class BooleanClassExtentHelper:
  1348. """
  1349. ```python
  1350. >>> testGraph = Graph()
  1351. >>> Individual.factoryGraph = testGraph
  1352. >>> EX = Namespace("http://example.com/")
  1353. >>> testGraph.bind("ex", EX, override=False)
  1354. >>> fire = Class(EX.Fire)
  1355. >>> water = Class(EX.Water)
  1356. >>> testClass = BooleanClass(members=[fire, water])
  1357. >>> testClass2 = BooleanClass(
  1358. ... operator=OWL.unionOf, members=[fire, water])
  1359. >>> for c in BooleanClass.getIntersections():
  1360. ... print(c) # doctest: +SKIP
  1361. ( ex:Fire AND ex:Water )
  1362. >>> for c in BooleanClass.getUnions():
  1363. ... print(c) #doctest: +SKIP
  1364. ( ex:Fire OR ex:Water )
  1365. ```
  1366. """
  1367. def __init__(self, operator):
  1368. self.operator = operator
  1369. def __call__(self, f):
  1370. def _getExtent(): # noqa: N802
  1371. for c in Individual.factoryGraph.subjects(self.operator):
  1372. yield BooleanClass(c, operator=self.operator)
  1373. return _getExtent
  1374. class Callable:
  1375. def __init__(self, anycallable):
  1376. self._callfn = anycallable
  1377. def __call__(self, *args, **kwargs):
  1378. return self._callfn(*args, **kwargs)
  1379. class BooleanClass(OWLRDFListProxy, Class):
  1380. """
  1381. See: http://www.w3.org/TR/owl-ref/#Boolean
  1382. owl:complementOf is an attribute of Class, however
  1383. """
  1384. @BooleanClassExtentHelper(OWL.intersectionOf)
  1385. @Callable
  1386. def getIntersections(): # type: ignore[misc] # noqa: N802
  1387. pass # pragma: no cover
  1388. getIntersections = Callable(getIntersections) # noqa: N815
  1389. @BooleanClassExtentHelper(OWL.unionOf)
  1390. @Callable
  1391. def getUnions(): # type: ignore[misc] # noqa: N802
  1392. pass # pragma: no cover
  1393. getUnions = Callable(getUnions) # noqa: N815
  1394. def __init__(
  1395. self, identifier=None, operator=OWL.intersectionOf, members=None, graph=None
  1396. ):
  1397. if operator is None:
  1398. props = []
  1399. for _s, p, _o in graph.triples_choices(
  1400. (identifier, [OWL.intersectionOf, OWL.unionOf], None)
  1401. ):
  1402. props.append(p)
  1403. operator = p
  1404. assert len(props) == 1, repr(props)
  1405. Class.__init__(self, identifier, graph=graph)
  1406. assert operator in [OWL.intersectionOf, OWL.unionOf], str(operator)
  1407. self._operator = operator
  1408. rdf_list = list(self.graph.objects(predicate=operator, subject=self.identifier))
  1409. assert (
  1410. not members or not rdf_list
  1411. ), "This is a previous boolean class description."
  1412. OWLRDFListProxy.__init__(self, rdf_list, members)
  1413. def copy(self):
  1414. """
  1415. Create a copy of this class
  1416. """
  1417. copy_of_class = BooleanClass(
  1418. operator=self._operator, members=list(self), graph=self.graph
  1419. )
  1420. return copy_of_class
  1421. def serialize(self, graph):
  1422. clonedlist = Collection(graph, BNode())
  1423. for cl in self._rdfList:
  1424. clonedlist.append(cl)
  1425. CastClass(cl, self.graph).serialize(graph)
  1426. graph.add((self.identifier, self._operator, clonedlist.uri))
  1427. for s, p, o in self.graph.triples((self.identifier, None, None)):
  1428. if p != self._operator:
  1429. graph.add((s, p, o))
  1430. self._serialize(graph)
  1431. def isPrimitive(self): # noqa: N802
  1432. return False
  1433. def changeOperator(self, newOperator): # noqa: N802, N803
  1434. """
  1435. Converts a unionOf / intersectionOf class expression into one
  1436. that instead uses the given operator
  1437. ```python
  1438. >>> testGraph = Graph()
  1439. >>> Individual.factoryGraph = testGraph
  1440. >>> EX = Namespace("http://example.com/")
  1441. >>> testGraph.bind("ex", EX, override=False)
  1442. >>> fire = Class(EX.Fire)
  1443. >>> water = Class(EX.Water)
  1444. >>> testClass = BooleanClass(members=[fire,water])
  1445. >>> testClass
  1446. ( ex:Fire AND ex:Water )
  1447. >>> testClass.changeOperator(OWL.unionOf)
  1448. >>> testClass
  1449. ( ex:Fire OR ex:Water )
  1450. >>> try:
  1451. ... testClass.changeOperator(OWL.unionOf)
  1452. ... except Exception as e:
  1453. ... print(e) # doctest: +SKIP
  1454. The new operator is already being used!
  1455. ```
  1456. """
  1457. assert newOperator != self._operator, "The new operator is already being used!"
  1458. self.graph.remove((self.identifier, self._operator, self._rdfList.uri))
  1459. self.graph.add((self.identifier, newOperator, self._rdfList.uri))
  1460. self._operator = newOperator
  1461. def __repr__(self):
  1462. """
  1463. Returns the Manchester Syntax equivalent for this class
  1464. """
  1465. return manchesterSyntax(
  1466. self._rdfList.uri if isinstance(self._rdfList, Collection) else BNode(),
  1467. self.graph,
  1468. boolean=self._operator,
  1469. )
  1470. def __or__(self, other):
  1471. """
  1472. Adds other to the list and returns self
  1473. """
  1474. assert self._operator == OWL.unionOf
  1475. self._rdfList.append(classOrIdentifier(other))
  1476. return self
  1477. def AllDifferent(members): # noqa: N802
  1478. """
  1479. TODO: implement this function
  1480. DisjointClasses(' description description { description } ')'
  1481. """
  1482. pass # pragma: no cover
  1483. class Restriction(Class):
  1484. """
  1485. ```
  1486. restriction ::= 'restriction('
  1487. datavaluedPropertyID dataRestrictionComponent
  1488. { dataRestrictionComponent } ')'
  1489. | 'restriction(' individualvaluedPropertyID
  1490. individualRestrictionComponent
  1491. { individualRestrictionComponent } ')'
  1492. ```
  1493. """
  1494. restrictionKinds = [ # noqa: N815
  1495. OWL.allValuesFrom,
  1496. OWL.someValuesFrom,
  1497. OWL.hasValue,
  1498. OWL.cardinality,
  1499. OWL.maxCardinality,
  1500. OWL.minCardinality,
  1501. ]
  1502. def __init__(
  1503. self,
  1504. onProperty, # noqa: N803
  1505. graph=None,
  1506. allValuesFrom=None, # noqa: N803
  1507. someValuesFrom=None, # noqa: N803
  1508. value=None,
  1509. cardinality=None,
  1510. maxCardinality=None, # noqa: N803
  1511. minCardinality=None, # noqa: N803
  1512. identifier=None,
  1513. ):
  1514. graph = Graph() if graph is None else graph
  1515. super(Restriction, self).__init__(
  1516. identifier, graph=graph, skipOWLClassMembership=True
  1517. )
  1518. if (
  1519. self.identifier,
  1520. OWL.onProperty,
  1521. propertyOrIdentifier(onProperty),
  1522. ) not in graph:
  1523. graph.add(
  1524. (self.identifier, OWL.onProperty, propertyOrIdentifier(onProperty))
  1525. )
  1526. self.onProperty = onProperty
  1527. restr_types = [
  1528. (allValuesFrom, OWL.allValuesFrom),
  1529. (someValuesFrom, OWL.someValuesFrom),
  1530. (value, OWL.hasValue),
  1531. (cardinality, OWL.cardinality),
  1532. (maxCardinality, OWL.maxCardinality),
  1533. (minCardinality, OWL.minCardinality),
  1534. ]
  1535. valid_restr_props = [(i, oterm) for (i, oterm) in restr_types if i is not None]
  1536. if not len(valid_restr_props):
  1537. raise ValueError(
  1538. "Missing value. One of: allValuesFrom, someValuesFrom,"
  1539. "value, cardinality, maxCardinality or minCardinality"
  1540. "must have a value."
  1541. )
  1542. restriction_range, restriction_type = valid_restr_props.pop()
  1543. self.restrictionType = restriction_type
  1544. if isinstance(restriction_range, Identifier):
  1545. self.restrictionRange = restriction_range
  1546. elif isinstance(restriction_range, Class):
  1547. self.restrictionRange = classOrIdentifier(restriction_range)
  1548. else:
  1549. # error: Incompatible types in assignment (expression has type "Optional[Identifier]", variable has type "Identifier")
  1550. self.restrictionRange = first( # type: ignore[assignment]
  1551. # type error: Argument 1 to "first" has incompatible type "Generator[Node, None, None]"; expected "Iterable[Identifier]"
  1552. self.graph.objects(self.identifier, restriction_type) # type: ignore[arg-type]
  1553. )
  1554. if (
  1555. self.identifier,
  1556. restriction_type,
  1557. self.restrictionRange,
  1558. ) not in self.graph:
  1559. self.graph.add((self.identifier, restriction_type, self.restrictionRange))
  1560. assert self.restrictionRange is not None, Class(self.identifier)
  1561. if (self.identifier, RDF.type, OWL.Restriction) not in self.graph:
  1562. self.graph.add((self.identifier, RDF.type, OWL.Restriction))
  1563. self.graph.remove((self.identifier, RDF.type, OWL.Class))
  1564. def serialize(self, graph):
  1565. """
  1566. ```python
  1567. >>> g1 = Graph()
  1568. >>> g2 = Graph()
  1569. >>> EX = Namespace("http://example.com/")
  1570. >>> g1.bind("ex", EX, override=False)
  1571. >>> g2.bind("ex", EX, override=False)
  1572. >>> Individual.factoryGraph = g1
  1573. >>> prop = Property(EX.someProp, baseType=OWL.DatatypeProperty)
  1574. >>> restr1 = (Property(
  1575. ... EX.someProp,
  1576. ... baseType=OWL.DatatypeProperty)) @ some @ (Class(EX.Foo))
  1577. >>> restr1 # doctest: +SKIP
  1578. ( ex:someProp SOME ex:Foo )
  1579. >>> restr1.serialize(g2)
  1580. >>> Individual.factoryGraph = g2
  1581. >>> list(Property(
  1582. ... EX.someProp,baseType=None).type
  1583. ... ) #doctest: +NORMALIZE_WHITESPACE +SKIP
  1584. [rdflib.term.URIRef(
  1585. 'http://www.w3.org/2002/07/owl#DatatypeProperty')]
  1586. ```
  1587. """
  1588. Property(self.onProperty, graph=self.graph, baseType=None).serialize(graph)
  1589. for s, p, o in self.graph.triples((self.identifier, None, None)):
  1590. graph.add((s, p, o))
  1591. if p in [OWL.allValuesFrom, OWL.someValuesFrom]:
  1592. CastClass(o, self.graph).serialize(graph)
  1593. def isPrimitive(self): # noqa: N802
  1594. return False
  1595. def __hash__(self):
  1596. return hash((self.onProperty, self.restrictionRange))
  1597. def __eq__(self, other):
  1598. """
  1599. Equivalence of restrictions is determined by equivalence of the
  1600. property in question and the restriction 'range'
  1601. """
  1602. assert isinstance(other, Class), repr(other) + repr(type(other))
  1603. if isinstance(other, Restriction):
  1604. return (
  1605. other.onProperty == self.onProperty
  1606. # type error: "Restriction" has no attribute "restriction_range"; maybe "restrictionRange"?
  1607. and other.restriction_range == self.restrictionRange # type: ignore[attr-defined]
  1608. )
  1609. else:
  1610. return False
  1611. def _get_onproperty(self):
  1612. return list(
  1613. self.graph.objects(subject=self.identifier, predicate=OWL.onProperty)
  1614. )[0]
  1615. def _set_onproperty(self, prop):
  1616. if not prop:
  1617. return
  1618. triple = (self.identifier, OWL.onProperty, propertyOrIdentifier(prop))
  1619. if triple in self.graph:
  1620. return
  1621. else:
  1622. self.graph.set(triple)
  1623. @TermDeletionHelper(OWL.onProperty)
  1624. def _del_onproperty(self):
  1625. pass # pragma: no cover
  1626. onProperty = property( # noqa: N815
  1627. _get_onproperty, _set_onproperty, _del_onproperty
  1628. )
  1629. def _get_allvaluesfrom(self):
  1630. for i in self.graph.objects(
  1631. subject=self.identifier, predicate=OWL.allValuesFrom
  1632. ):
  1633. return Class(i, graph=self.graph)
  1634. return None
  1635. def _set_allvaluesfrom(self, other):
  1636. if not other:
  1637. return
  1638. triple = (self.identifier, OWL.allValuesFrom, classOrIdentifier(other))
  1639. if triple in self.graph:
  1640. return
  1641. else:
  1642. self.graph.set(triple)
  1643. @TermDeletionHelper(OWL.allValuesFrom)
  1644. def _del_allvaluesfrom(self):
  1645. pass # pragma: no cover
  1646. allValuesFrom = property( # noqa: N815
  1647. _get_allvaluesfrom, _set_allvaluesfrom, _del_allvaluesfrom
  1648. )
  1649. def _get_somevaluesfrom(self):
  1650. for i in self.graph.objects(
  1651. subject=self.identifier, predicate=OWL.someValuesFrom
  1652. ):
  1653. return Class(i, graph=self.graph)
  1654. return None
  1655. def _set_somevaluesfrom(self, other):
  1656. if not other:
  1657. return
  1658. triple = (self.identifier, OWL.someValuesFrom, classOrIdentifier(other))
  1659. if triple in self.graph:
  1660. return
  1661. else:
  1662. self.graph.set(triple)
  1663. @TermDeletionHelper(OWL.someValuesFrom)
  1664. def _del_somevaluesfrom(self):
  1665. pass # pragma: no cover
  1666. someValuesFrom = property( # noqa: N815
  1667. _get_somevaluesfrom, _set_somevaluesfrom, _del_somevaluesfrom
  1668. )
  1669. def _get_hasvalue(self):
  1670. for i in self.graph.objects(subject=self.identifier, predicate=OWL.hasValue):
  1671. return Class(i, graph=self.graph)
  1672. return None
  1673. def _set_hasvalue(self, other):
  1674. if not other:
  1675. return
  1676. triple = (self.identifier, OWL.hasValue, classOrIdentifier(other))
  1677. if triple in self.graph:
  1678. return
  1679. else:
  1680. self.graph.set(triple)
  1681. @TermDeletionHelper(OWL.hasValue)
  1682. def _del_hasvalue(self):
  1683. pass # pragma: no cover
  1684. hasValue = property(_get_hasvalue, _set_hasvalue, _del_hasvalue) # noqa: N815
  1685. def _get_cardinality(self):
  1686. for i in self.graph.objects(subject=self.identifier, predicate=OWL.cardinality):
  1687. return Class(i, graph=self.graph)
  1688. return None
  1689. def _set_cardinality(self, other):
  1690. if not other:
  1691. return
  1692. triple = (self.identifier, OWL.cardinality, classOrTerm(other))
  1693. if triple in self.graph:
  1694. return
  1695. else:
  1696. self.graph.set(triple)
  1697. @TermDeletionHelper(OWL.cardinality)
  1698. def _del_cardinality(self):
  1699. pass # pragma: no cover
  1700. cardinality = property(_get_cardinality, _set_cardinality, _del_cardinality)
  1701. def _get_maxcardinality(self):
  1702. for i in self.graph.objects(
  1703. subject=self.identifier, predicate=OWL.maxCardinality
  1704. ):
  1705. return Class(i, graph=self.graph)
  1706. return None
  1707. def _set_maxcardinality(self, other):
  1708. if not other:
  1709. return
  1710. triple = (self.identifier, OWL.maxCardinality, classOrTerm(other))
  1711. if triple in self.graph:
  1712. return
  1713. else:
  1714. self.graph.set(triple)
  1715. @TermDeletionHelper(OWL.maxCardinality)
  1716. def _del_maxcardinality(self):
  1717. pass # pragma: no cover
  1718. maxCardinality = property( # noqa: N815
  1719. _get_maxcardinality, _set_maxcardinality, _del_maxcardinality
  1720. )
  1721. def _get_mincardinality(self):
  1722. for i in self.graph.objects(
  1723. subject=self.identifier, predicate=OWL.minCardinality
  1724. ):
  1725. return Class(i, graph=self.graph)
  1726. return None
  1727. def _set_mincardinality(self, other):
  1728. if not other:
  1729. return
  1730. triple = (self.identifier, OWL.minCardinality, classOrIdentifier(other))
  1731. if triple in self.graph:
  1732. return
  1733. else:
  1734. self.graph.set(triple)
  1735. @TermDeletionHelper(OWL.minCardinality)
  1736. def _del_mincardinality(self):
  1737. pass # pragma: no cover
  1738. minCardinality = property( # noqa: N815
  1739. _get_mincardinality, _set_mincardinality, _del_mincardinality
  1740. )
  1741. def restrictionKind(self): # noqa: N802
  1742. for s, p, o in self.graph.triples_choices(
  1743. # type error: Argument 1 to "triples_choices" of "Graph" has incompatible type "Tuple[Any, List[URIRef], None]"; expected "Union[Tuple[List[Node], Node, Node], Tuple[Node, List[Node], Node], Tuple[Node, Node, List[Node]]]"
  1744. (self.identifier, self.restrictionKinds, None) # type: ignore[arg-type]
  1745. ):
  1746. # type error: "Node" has no attribute "split"
  1747. return p.split(str(OWL))[-1] # type: ignore[attr-defined]
  1748. return None
  1749. def __repr__(self):
  1750. """
  1751. Returns the Manchester Syntax equivalent for this restriction
  1752. """
  1753. return manchesterSyntax(self.identifier, self.graph)
  1754. # Infix Operators #
  1755. some = Infix(
  1756. lambda prop, _class: Restriction(prop, graph=_class.graph, someValuesFrom=_class)
  1757. )
  1758. only = Infix(
  1759. lambda prop, _class: Restriction(prop, graph=_class.graph, allValuesFrom=_class)
  1760. )
  1761. max = Infix(
  1762. lambda prop, _class: Restriction(prop, graph=prop.graph, maxCardinality=_class)
  1763. )
  1764. min = Infix(
  1765. lambda prop, _class: Restriction(prop, graph=prop.graph, minCardinality=_class)
  1766. )
  1767. exactly = Infix(
  1768. lambda prop, _class: Restriction(prop, graph=prop.graph, cardinality=_class)
  1769. )
  1770. value = Infix(lambda prop, _class: Restriction(prop, graph=prop.graph, value=_class))
  1771. # Unused
  1772. PropertyAbstractSyntax = """
  1773. %s( %s { %s }
  1774. %s
  1775. { 'super(' datavaluedPropertyID ')'} ['Functional']
  1776. { domain( %s ) } { range( %s ) } )"""
  1777. class Property(AnnotatableTerms):
  1778. """
  1779. ```
  1780. axiom ::= 'DatatypeProperty(' datavaluedPropertyID ['Deprecated']
  1781. { annotation }
  1782. { 'super(' datavaluedPropertyID ')'} ['Functional']
  1783. { 'domain(' description ')' } { 'range(' dataRange ')' } ')'
  1784. | 'ObjectProperty(' individualvaluedPropertyID ['Deprecated']
  1785. { annotation }
  1786. { 'super(' individualvaluedPropertyID ')' }
  1787. [ 'inverseOf(' individualvaluedPropertyID ')' ] [ 'Symmetric' ]
  1788. [ 'Functional' | 'InverseFunctional' |
  1789. 'Functional' 'InverseFunctional' |
  1790. 'Transitive' ]
  1791. { 'domain(' description ')' } { 'range(' description ')' } ')
  1792. ```
  1793. """
  1794. def setupVerbAnnotations(self, verb_annotations): # noqa: N802
  1795. """OWL properties map to ACE transitive verbs (TV)
  1796. There are 6 morphological categories that determine the surface form
  1797. of an IRI:
  1798. - singular form of a transitive verb (e.g. mans)
  1799. - plural form of a transitive verb (e.g. man)
  1800. - past participle form a transitive verb (e.g. manned)
  1801. - http://attempto.ifi.uzh.ch/ace_lexicon#TV_sg
  1802. - http://attempto.ifi.uzh.ch/ace_lexicon#TV_pl
  1803. - http://attempto.ifi.uzh.ch/ace_lexicon#TV_vbg
  1804. """
  1805. if isinstance(verb_annotations, tuple):
  1806. tv_sgprop, tv_plprop, tv_vbg = verb_annotations
  1807. else:
  1808. tv_sgprop = verb_annotations
  1809. tv_plprop = verb_annotations
  1810. tv_vbg = verb_annotations
  1811. if tv_sgprop:
  1812. self.tv_sgprop.extent = [
  1813. (self.identifier, self.handleAnnotation(tv_sgprop))
  1814. ]
  1815. if tv_plprop:
  1816. self.tv_plprop.extent = [
  1817. (self.identifier, self.handleAnnotation(tv_plprop))
  1818. ]
  1819. if tv_vbg:
  1820. self.tv_vbgprop.extent = [(self.identifier, self.handleAnnotation(tv_vbg))]
  1821. def __init__(
  1822. self,
  1823. identifier=None,
  1824. graph=None,
  1825. baseType=OWL.ObjectProperty, # noqa: N803
  1826. subPropertyOf=None, # noqa: N803
  1827. domain=None,
  1828. range=None,
  1829. inverseOf=None, # noqa: N803
  1830. otherType=None, # noqa: N803
  1831. equivalentProperty=None, # noqa: N803
  1832. comment=None,
  1833. verbAnnotations=None, # noqa: N803
  1834. nameAnnotation=None, # noqa: N803
  1835. nameIsLabel=False, # noqa: N803
  1836. ):
  1837. super(Property, self).__init__(identifier, graph, nameAnnotation, nameIsLabel)
  1838. if verbAnnotations:
  1839. self.setupVerbAnnotations(verbAnnotations)
  1840. assert not isinstance(self.identifier, BNode)
  1841. if baseType is None:
  1842. # None give, determine via introspection
  1843. self._baseType = first(Individual(self.identifier, graph=self.graph).type)
  1844. else:
  1845. if (self.identifier, RDF.type, baseType) not in self.graph:
  1846. self.graph.add((self.identifier, RDF.type, baseType))
  1847. self._baseType = baseType
  1848. self.subPropertyOf = subPropertyOf
  1849. self.inverseOf = inverseOf
  1850. self.domain = domain
  1851. self.range = range
  1852. self.comment = [] if comment is None else comment
  1853. def serialize(self, graph):
  1854. for fact in self.graph.triples((self.identifier, None, None)):
  1855. graph.add(fact)
  1856. for p in itertools.chain(self.subPropertyOf, self.inverseOf):
  1857. p.serialize(graph)
  1858. for c in itertools.chain(self.domain, self.range):
  1859. CastClass(c, self.graph).serialize(graph)
  1860. def _get_extent(self, graph=None):
  1861. for triple in (graph is None and self.graph or graph).triples(
  1862. (None, self.identifier, None)
  1863. ):
  1864. yield triple
  1865. def _set_extent(self, other):
  1866. if not other:
  1867. return
  1868. for subj, obj in other:
  1869. self.graph.add((subj, self.identifier, obj))
  1870. extent = property(_get_extent, _set_extent)
  1871. def __repr__(self):
  1872. rt = []
  1873. if OWL.ObjectProperty in self.type:
  1874. rt.append(
  1875. "ObjectProperty( %s annotation(%s)"
  1876. % (self.qname, first(self.comment) and first(self.comment) or "")
  1877. )
  1878. if first(self.inverseOf):
  1879. # type error: Item "None" of "Optional[Any]" has no attribute "inverseOf"
  1880. two_link_inverse = first(first(self.inverseOf).inverseOf) # type: ignore[union-attr]
  1881. if two_link_inverse and two_link_inverse.identifier == self.identifier:
  1882. # type error: Item "None" of "Optional[Any]" has no attribute "qname"
  1883. inverserepr = first(self.inverseOf).qname # type: ignore[union-attr]
  1884. else:
  1885. inverserepr = repr(first(self.inverseOf))
  1886. rt.append(
  1887. " inverseOf( %s )%s"
  1888. % (
  1889. inverserepr,
  1890. OWL.SymmetricProperty in self.type and " Symmetric" or "",
  1891. )
  1892. )
  1893. for _s, _p, roletype in self.graph.triples_choices(
  1894. # type error: Argument 1 to "triples_choices" of "Graph" has incompatible type "Tuple[Any, URIRef, List[URIRef]]"; expected "Union[Tuple[List[Node], Node, Node], Tuple[Node, List[Node], Node], Tuple[Node, Node, List[Node]]]"
  1895. ( # type: ignore[arg-type]
  1896. self.identifier,
  1897. RDF.type,
  1898. [
  1899. OWL.FunctionalProperty,
  1900. OWL.InverseFunctionalProperty,
  1901. OWL.TransitiveProperty,
  1902. ],
  1903. )
  1904. ):
  1905. # type error: "Node" has no attribute "split"
  1906. rt.append(str(roletype.split(str(OWL))[-1])) # type: ignore[attr-defined]
  1907. else:
  1908. rt.append(
  1909. "DatatypeProperty( %s %s"
  1910. % (self.qname, first(self.comment) and first(self.comment) or "")
  1911. )
  1912. for _s, _p, roletype in self.graph.triples(
  1913. (self.identifier, RDF.type, OWL.FunctionalProperty)
  1914. ):
  1915. rt.append(" Functional")
  1916. def canonicalName(term, g): # noqa: N802
  1917. normalized_name = classOrIdentifier(term)
  1918. if isinstance(normalized_name, BNode):
  1919. return term
  1920. elif normalized_name.startswith(XSD):
  1921. return str(term)
  1922. elif first(
  1923. g.triples_choices(
  1924. (normalized_name, [OWL.unionOf, OWL.intersectionOf], None)
  1925. )
  1926. ):
  1927. return repr(term)
  1928. else:
  1929. return str(term.qname)
  1930. rt.append(
  1931. " ".join(
  1932. [
  1933. " super( %s )" % canonicalName(super_property, self.graph)
  1934. for super_property in self.subPropertyOf
  1935. ]
  1936. )
  1937. )
  1938. rt.append(
  1939. " ".join(
  1940. [
  1941. " domain( %s )" % canonicalName(domain, self.graph)
  1942. for domain in self.domain
  1943. ]
  1944. )
  1945. )
  1946. rt.append(
  1947. " ".join(
  1948. [
  1949. " range( %s )" % canonicalName(range, self.graph)
  1950. for range in self.range
  1951. ]
  1952. )
  1953. )
  1954. # type error: Incompatible types in assignment (expression has type "str", variable has type "List[str]")
  1955. rt = "\n".join([expr for expr in rt if expr]) # type: ignore[assignment]
  1956. rt += "\n)"
  1957. return rt
  1958. def _get_subpropertyof(self):
  1959. for anc in self.graph.objects(
  1960. subject=self.identifier, predicate=RDFS.subPropertyOf
  1961. ):
  1962. yield Property(anc, graph=self.graph, baseType=None)
  1963. def _set_subpropertyof(self, other):
  1964. if not other:
  1965. return
  1966. for subproperty in other:
  1967. self.graph.add(
  1968. (self.identifier, RDFS.subPropertyOf, classOrIdentifier(subproperty))
  1969. )
  1970. @TermDeletionHelper(RDFS.subPropertyOf)
  1971. def _del_subpropertyof(self):
  1972. pass # pragma: no cover
  1973. subPropertyOf = property( # noqa: N815
  1974. _get_subpropertyof, _set_subpropertyof, _del_subpropertyof
  1975. )
  1976. def _get_inverseof(self):
  1977. for anc in self.graph.objects(subject=self.identifier, predicate=OWL.inverseOf):
  1978. yield Property(anc, graph=self.graph, baseType=None)
  1979. def _set_inverseof(self, other):
  1980. if not other:
  1981. return
  1982. self.graph.add((self.identifier, OWL.inverseOf, classOrIdentifier(other)))
  1983. @TermDeletionHelper(OWL.inverseOf)
  1984. def _del_inverseof(self):
  1985. pass # pragma: no cover
  1986. inverseOf = property(_get_inverseof, _set_inverseof, _del_inverseof) # noqa: N815
  1987. def _get_domain(self):
  1988. for dom in self.graph.objects(subject=self.identifier, predicate=RDFS.domain):
  1989. yield Class(dom, graph=self.graph)
  1990. def _set_domain(self, other):
  1991. if not other:
  1992. return
  1993. if isinstance(other, (Individual, Identifier)):
  1994. self.graph.add((self.identifier, RDFS.domain, classOrIdentifier(other)))
  1995. else:
  1996. for dom in other:
  1997. self.graph.add((self.identifier, RDFS.domain, classOrIdentifier(dom)))
  1998. @TermDeletionHelper(RDFS.domain)
  1999. def _del_domain(self):
  2000. pass # pragma: no cover
  2001. domain = property(_get_domain, _set_domain, _del_domain)
  2002. def _get_range(self):
  2003. for ran in self.graph.objects(subject=self.identifier, predicate=RDFS.range):
  2004. yield Class(ran, graph=self.graph)
  2005. def _set_range(self, ranges):
  2006. if not ranges:
  2007. return
  2008. if isinstance(ranges, (Individual, Identifier)):
  2009. self.graph.add((self.identifier, RDFS.range, classOrIdentifier(ranges)))
  2010. else:
  2011. for range in ranges:
  2012. self.graph.add((self.identifier, RDFS.range, classOrIdentifier(range)))
  2013. @TermDeletionHelper(RDFS.range)
  2014. def _del_range(self):
  2015. pass # pragma: no cover
  2016. range = property(_get_range, _set_range, _del_range)
  2017. def replace(self, other):
  2018. # extension = []
  2019. for s, _p, o in self.extent:
  2020. self.graph.add((s, propertyOrIdentifier(other), o))
  2021. self.graph.remove((None, self.identifier, None))
  2022. def CommonNSBindings(graph, additionalNS=None): # noqa: N802, N803
  2023. """
  2024. Takes a graph and binds the common namespaces (rdf,rdfs, & owl)
  2025. """
  2026. additional_ns = {} if additionalNS is None else additionalNS
  2027. namespace_manager = NamespaceManager(graph)
  2028. namespace_manager.bind("rdfs", RDFS)
  2029. namespace_manager.bind("rdf", RDF)
  2030. namespace_manager.bind("owl", OWL)
  2031. for prefix, uri in list(additional_ns.items()):
  2032. namespace_manager.bind(prefix, uri, override=False)
  2033. graph.namespace_manager = namespace_manager