pyMetaModel 0.6.7__tar.gz → 0.6.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/PKG-INFO +1 -1
  2. pymetamodel-0.6.9/meta/__init__.py +1 -0
  3. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/metamodel.py +63 -77
  4. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/version.py +1 -1
  5. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/test_metamodel.py +23 -5
  6. pymetamodel-0.6.7/meta/__init__.py +0 -1
  7. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/.github/workflows/build.yml +0 -0
  8. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/.github/workflows/upload-to-pypi.yml +0 -0
  9. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/.gitignore +0 -0
  10. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/.project +0 -0
  11. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/.pydevproject +0 -0
  12. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/LICENSE +0 -0
  13. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/README.md +0 -0
  14. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/docs/index.md +0 -0
  15. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.mermaid +0 -0
  16. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.puml +0 -0
  17. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.py +0 -0
  18. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.sidif +0 -0
  19. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.xlsx +0 -0
  20. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws.yaml +0 -0
  21. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/ceur-ws/ceur-ws_puml.txt +0 -0
  22. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/city/city.mermaid +0 -0
  23. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/city/city.puml +0 -0
  24. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/city/city.py +0 -0
  25. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/city/city.sidif +0 -0
  26. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/city/city.xlsx +0 -0
  27. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/city/city.yaml +0 -0
  28. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/city/city_puml.txt +0 -0
  29. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/family/FamilyContext.mermaid +0 -0
  30. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/family/FamilyContext.puml +0 -0
  31. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/family/FamilyContext.py +0 -0
  32. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/family/FamilyContext.sidif +0 -0
  33. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/family/FamilyContext.xlsx +0 -0
  34. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/family/FamilyContext.yaml +0 -0
  35. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/family/FamilyContext_puml.txt +0 -0
  36. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.mermaid +0 -0
  37. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.puml +0 -0
  38. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.py +0 -0
  39. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.sidif +0 -0
  40. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.xlsx +0 -0
  41. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure.yaml +0 -0
  42. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/infrastructure/infrastructure_puml.txt +0 -0
  43. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/metamodel/metamodel.mermaid +0 -0
  44. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/metamodel/metamodel.puml +0 -0
  45. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/metamodel/metamodel.py +0 -0
  46. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/metamodel/metamodel.sidif +0 -0
  47. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/metamodel/metamodel.xlsx +0 -0
  48. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/metamodel/metamodel.yaml +0 -0
  49. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/metamodel/metamodel_puml.txt +0 -0
  50. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.mermaid +0 -0
  51. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.puml +0 -0
  52. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.py +0 -0
  53. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.sidif +0 -0
  54. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.xlsx +0 -0
  55. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events.yaml +0 -0
  56. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/scientific-events/scientific-events_puml.txt +0 -0
  57. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.mermaid +0 -0
  58. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.puml +0 -0
  59. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.py +0 -0
  60. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.sidif +0 -0
  61. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM.yaml +0 -0
  62. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smartRQM/smartRQM_puml.txt +0 -0
  63. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smwcon/smwcon.mermaid +0 -0
  64. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smwcon/smwcon.puml +0 -0
  65. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smwcon/smwcon.py +0 -0
  66. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smwcon/smwcon.sidif +0 -0
  67. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smwcon/smwcon.xlsx +0 -0
  68. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smwcon/smwcon.yaml +0 -0
  69. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/smwcon/smwcon_puml.txt +0 -0
  70. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.mermaid +0 -0
  71. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.puml +0 -0
  72. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.py +0 -0
  73. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.sidif +0 -0
  74. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.xlsx +0 -0
  75. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema.yaml +0 -0
  76. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/teaching/TeachingSchema_puml.txt +0 -0
  77. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/wiki/wiki.mermaid +0 -0
  78. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/wiki/wiki.puml +0 -0
  79. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/wiki/wiki.py +0 -0
  80. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/wiki/wiki.sidif +0 -0
  81. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/wiki/wiki.xlsx +0 -0
  82. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/wiki/wiki.yaml +0 -0
  83. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/examples/wiki/wiki_puml.txt +0 -0
  84. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/metamodel_cmd.py +0 -0
  85. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/mw.py +0 -0
  86. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/profiler.py +0 -0
  87. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/sidif2linkml.py +0 -0
  88. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/smw_type.py +0 -0
  89. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/meta/uml.py +0 -0
  90. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/metamodel.puml +0 -0
  91. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/mkdocs.yml +0 -0
  92. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/pyproject.toml +0 -0
  93. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/scripts/blackisort +0 -0
  94. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/scripts/doc +0 -0
  95. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/scripts/genexamples +0 -0
  96. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/scripts/install +0 -0
  97. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/scripts/release +0 -0
  98. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/scripts/test +0 -0
  99. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/__init__.py +0 -0
  100. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/basemwtest.py +0 -0
  101. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/basesmwtest.py +0 -0
  102. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/basetest.py +0 -0
  103. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/test_generators.py +0 -0
  104. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/test_linkml.py +0 -0
  105. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/test_mw.py +0 -0
  106. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/test_plantuml.py +0 -0
  107. {pymetamodel-0.6.7 → pymetamodel-0.6.9}/tests/test_sidif.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyMetaModel
3
- Version: 0.6.7
3
+ Version: 0.6.9
4
4
  Dynamic: Summary
5
5
  Project-URL: Home, https://github.com/WolfgangFahl/pyMetaModel
6
6
  Project-URL: Documentation, https://wiki.bitplan.com/index.php/PyMetaModel
@@ -0,0 +1 @@
1
+ __version__ = "0.6.9"
@@ -26,18 +26,19 @@ class Context:
26
26
  A Context groups some topics like a Namespace/Package.
27
27
  This class provides helper functions and constants to render a Context to corresponding wiki pages
28
28
  """
29
+
29
30
  name: Optional[str] = None
30
- since: Optional[str] = None # isodate string
31
- updated: Optional[str] = None # isodate string
31
+ since: Optional[str] = None # isodate string
32
+ updated: Optional[str] = None # isodate string
32
33
  copyright: Optional[str] = None
33
34
  master: Optional[str] = None
34
35
  demo: Optional[str] = None
35
- topics: Dict[str, 'Topic'] = field(default_factory=dict)
36
- types: Dict[str, 'Topic'] = field(default_factory=dict)
36
+ topics: Dict[str, "Topic"] = field(default_factory=dict)
37
+ types: Dict[str, "Topic"] = field(default_factory=dict)
37
38
  errors: List[str] = field(default_factory=list)
38
39
 
39
40
  def __post_init__(self):
40
- self.log=Log()
41
+ self.log = Log()
41
42
  pass
42
43
 
43
44
  @classmethod
@@ -49,17 +50,17 @@ class Context:
49
50
  "updated": datetime.strptime("2024-08-07", "%Y-%m-%d"),
50
51
  "copyright": "2015-2025 BITPlan GmbH",
51
52
  "master": "https://contexts.bitplan.com",
52
- "demo": "https://wiki.bitplan.com/index.php/List_of_Contexts"
53
+ "demo": "https://wiki.bitplan.com/index.php/List_of_Contexts",
53
54
  }
54
55
  ]
55
56
  return samples
56
57
 
57
- def sanitize(self,key:str):
58
+ def sanitize(self, key: str):
58
59
  """
59
60
  make sure my needed properties exist
60
61
  """
61
- if not hasattr(self,"name"):
62
- self.name=key
62
+ if not hasattr(self, "name"):
63
+ self.name = key
63
64
  pass
64
65
  pass
65
66
 
@@ -82,7 +83,7 @@ class Context:
82
83
  return self.topics[topic_name]
83
84
  else:
84
85
  self.error(
85
- f"""topic {topic_name} not found in context {getattr(self,"name","?")} for {purpose}"""
86
+ f"""topic {topic_name} not found in context {getattr(self, "name", "?")} for {purpose}"""
86
87
  )
87
88
  return None
88
89
 
@@ -186,6 +187,7 @@ class Context:
186
187
  prop.topicLink = tl
187
188
  prop.isLink = True
188
189
  prop.showInGrid = False
190
+ prop.separator = tl.separator
189
191
  return prop
190
192
 
191
193
  @classmethod
@@ -225,8 +227,8 @@ class Context:
225
227
  prop.sanitize()
226
228
  context.addProperty(prop)
227
229
  elif isA == "SMW_Type":
228
- smw_type=SMW_Type.from_dict2(record) # @UndefinedVariable
229
- context.types[smw_type.type]=smw_type
230
+ smw_type = SMW_Type.from_dict2(record) # @UndefinedVariable
231
+ context.types[smw_type.type] = smw_type
230
232
  else: # isA == Topic or in declared topics
231
233
  topic = Topic.from_dict2(record)
232
234
  topic.sanitize()
@@ -238,14 +240,16 @@ class Context:
238
240
  context.error(f"topic {topic.name} has no defined context")
239
241
  if hasattr(topic, "name") and topic.name:
240
242
  context.topics[topic.name] = topic
241
- topic.context_obj=context
243
+ topic.context_obj = context
242
244
  else:
243
245
  # potential foreign or extends declaration
244
- context.error(f"missing name for topic {topic} {key} - foreign declaration?")
246
+ context.error(
247
+ f"missing name for topic {topic} {key} - foreign declaration?"
248
+ )
245
249
  except Exception as ex:
246
250
  if context:
247
- msg=f"invalid dict {record}: {str(ex)}"
248
- context.log.log("❌","dict",msg)
251
+ msg = f"invalid dict {record}: {str(ex)}"
252
+ context.log.log("❌", "dict", msg)
249
253
  else:
250
254
  raise ex
251
255
  # link topic to concepts and add topicLinks
@@ -258,7 +262,9 @@ class Context:
258
262
  return context
259
263
 
260
264
  @classmethod
261
- def fromSiDIF_input(cls, sidif_input: str, debug: bool = False)->Tuple['Context',str,str]:
265
+ def fromSiDIF_input(
266
+ cls, sidif_input: str, debug: bool = False
267
+ ) -> Tuple["Context", str, str]:
262
268
  """
263
269
  initialize me from the given SiDIF input which might be a file path
264
270
  or url
@@ -333,7 +339,7 @@ class Context:
333
339
  error = None
334
340
  errMsg = None
335
341
  sidif = None
336
- msg=f"reading sidif for {mw_context.context} from {mw_context.wikiId}"
342
+ msg = f"reading sidif for {mw_context.context} from {mw_context.wikiId}"
337
343
  if debug:
338
344
  print(msg)
339
345
  try:
@@ -346,17 +352,19 @@ class Context:
346
352
  sidif=sidif, title=mw_context.wikiId, depth=depth, debug=debug
347
353
  )
348
354
  else:
349
- errMsg=f"{msg} failed"
350
- error=ValueError(msg)
355
+ errMsg = f"{msg} failed"
356
+ error = ValueError(msg)
351
357
  return context, error, errMsg
352
358
 
359
+
353
360
  @lod_storable
354
361
  class Topic:
355
362
  """
356
363
  A Topic is a Concept/Class/Thing/Entity
357
364
  """
365
+
358
366
  name: Optional[str] = None
359
- _pluralName: Optional[str] = None
367
+ pluralName: Optional[str] = None
360
368
  icon: Optional[str] = None
361
369
  iconUrl: Optional[str] = None
362
370
  documentation: Optional[str] = None
@@ -364,25 +372,24 @@ class Topic:
364
372
  defaultstoremode: str = "property"
365
373
  extends: Optional[str] = None
366
374
  listLimit: int = 200
367
- cargo:bool=False
368
- headerTabs:bool=False
375
+ cargo: bool = False
376
+ headerTabs: bool = False
369
377
  context: Optional[str] = None
370
- properties: Dict[str, 'Property'] = field(default_factory=dict)
371
- sourceTopicLinks: Dict[str, 'TopicLink'] = field(default_factory=dict)
372
- targetTopicLinks: Dict[str, 'TopicLink'] = field(default_factory=dict)
378
+ properties: Dict[str, "Property"] = field(default_factory=dict)
379
+ sourceTopicLinks: Dict[str, "TopicLink"] = field(default_factory=dict)
380
+ targetTopicLinks: Dict[str, "TopicLink"] = field(default_factory=dict)
373
381
  # object references
374
382
  context_obj: Optional[Context] = None
375
- conceptProperty: Optional['Property'] = None
383
+ conceptProperty: Optional["Property"] = None
376
384
 
377
-
378
- def get_primary_key_property(self) -> Optional['Property']:
385
+ def get_primary_key_property(self) -> Optional["Property"]:
379
386
  """Return the first property marked as primaryKey, or None."""
380
387
  return next(
381
388
  (p for p in self.properties.values() if getattr(p, "primaryKey", False)),
382
389
  None,
383
390
  )
384
391
 
385
- def get_extends_topics(self, l: List['Topic'] = None) -> List['Topic']:
392
+ def get_extends_topics(self, l: List["Topic"] = None) -> List["Topic"]:
386
393
  """
387
394
  Get the topics this topic is extending.
388
395
 
@@ -406,7 +413,7 @@ class Topic:
406
413
 
407
414
  return l
408
415
 
409
- def get_all_properties(self) -> List['Property']:
416
+ def get_all_properties(self) -> List["Property"]:
410
417
  """
411
418
  Get all properties of the topic, including those inherited from extended topics.
412
419
 
@@ -426,18 +433,18 @@ class Topic:
426
433
 
427
434
  return all_properties
428
435
 
429
-
430
436
  def sanitize(self):
431
437
  """
432
438
  make sure my properties exist note that name is not handled here
433
439
  on purpose since it needs special treatment
434
440
  """
435
- doc=self.documentation if hasattr(self, "documentation") else "?"
441
+ doc = self.documentation if hasattr(self, "documentation") else "?"
436
442
  if not hasattr(self, "wikiDocumentation") or not self.wikiDocumentation:
437
- self.wikiDocumentation=doc
438
- if not hasattr(self,"defaultstoremode"):
439
- self.defaultstoremode="property"
440
- pass
443
+ self.wikiDocumentation = doc
444
+ if not hasattr(self, "defaultstoremode"):
445
+ self.defaultstoremode = "property"
446
+ if not self.pluralName:
447
+ self.pluralName = f"{self.name}s"
441
448
 
442
449
  @classmethod
443
450
  def getSamples(cls):
@@ -462,7 +469,7 @@ class Topic:
462
469
  set my concept property to any primary key or mandatory property
463
470
  """
464
471
  self.conceptProperty = None
465
- all_properties=self.propertiesByIndex(to_root=True)
472
+ all_properties = self.propertiesByIndex(to_root=True)
466
473
  for prop in all_properties:
467
474
  mandatory = False
468
475
  primaryKey = False
@@ -474,27 +481,6 @@ class Topic:
474
481
  self.conceptProperty = prop
475
482
  break
476
483
 
477
- @property
478
- def pluralName(self)->str:
479
- """
480
- Getter for pluralName.
481
-
482
- Returns:
483
- str: The plural name of the topic.
484
- """
485
- # Default behavior if _pluralName is not explicitly set
486
- return self._pluralName if self._pluralName is not None else f"{self.name}s"
487
-
488
- @pluralName.setter
489
- def pluralName(self, value):
490
- """
491
- Setter for pluralName.
492
-
493
- Args:
494
- value (str): The plural name to be set for the topic.
495
- """
496
- self._pluralName = value
497
-
498
484
  def getPluralName(self) -> str:
499
485
  """
500
486
  get the plural name for this topic
@@ -520,7 +506,7 @@ class Topic:
520
506
  list: a list of properties in sort order
521
507
  """
522
508
  prop_list = []
523
- all_properties=self.get_all_properties()
509
+ all_properties = self.get_all_properties()
524
510
  for prop in all_properties:
525
511
  if hasattr(prop, "sortPos"):
526
512
  sortPos = prop.sortPos
@@ -541,13 +527,12 @@ class Topic:
541
527
  inheritance_chain = self.get_extends_topics()
542
528
  inheritance_chain.append(self)
543
529
  for topic in inheritance_chain:
544
- sorted_props=topic.propertiesByIndex(to_root=False)
530
+ sorted_props = topic.propertiesByIndex(to_root=False)
545
531
  properties_dict[topic.name] = sorted_props
546
532
 
547
533
  return properties_dict
548
534
 
549
-
550
- def propertiesByIndex(self,to_root:bool=False) -> list:
535
+ def propertiesByIndex(self, to_root: bool = False) -> list:
551
536
  """
552
537
  Return the properties by index, considering inheritance chain.
553
538
 
@@ -571,7 +556,6 @@ class Topic:
571
556
 
572
557
  return sorted_props
573
558
 
574
-
575
559
  def askSort(self) -> str:
576
560
  """
577
561
  generate the sort clause for my SMW ask query
@@ -601,7 +585,7 @@ class Topic:
601
585
  mainlabel: str = None,
602
586
  filterShowInGrid: bool = True,
603
587
  listLimit: int = None,
604
- result_format: str="table"
588
+ result_format: str = "table",
605
589
  ) -> str:
606
590
  """
607
591
  Get the askQuery for the topic, considering inheritance.
@@ -624,7 +608,7 @@ class Topic:
624
608
  for topic_name, properties in properties_dict.items():
625
609
  for prop in properties:
626
610
  if filterShowInGrid:
627
- show=getattr(prop, "showInGrid",False)
611
+ show = getattr(prop, "showInGrid", False)
628
612
  else:
629
613
  show = True
630
614
  if show:
@@ -635,11 +619,13 @@ class Topic:
635
619
  markup += f"""{self.askSort()}}}}}"""
636
620
  return markup
637
621
 
622
+
638
623
  @lod_storable
639
624
  class Property:
640
625
  """
641
626
  Provides helper functions and constants for properties
642
627
  """
628
+
643
629
  name: Optional[str] = None
644
630
  label: Optional[str] = None
645
631
  type: Optional[str] = None
@@ -652,18 +638,18 @@ class Property:
652
638
  uploadable: bool = False
653
639
  defaultValue: Optional[str] = None
654
640
  inputType: Optional[str] = None
655
- allowedValues: Optional[str]=None
641
+ allowedValues: Optional[str] = None
656
642
  documentation: Optional[str] = None
657
- values_from: Optional[str]=None
658
- formatterURI: Optional[str] = None # externalFormatterURI
643
+ values_from: Optional[str] = None
644
+ formatterURI: Optional[str] = None # externalFormatterURI
659
645
  showInGrid: bool = True
660
646
  isLink: bool = False
661
647
  nullable: bool = False
662
648
  sortAscending: bool = True
649
+ separator: Optional[str] = None
663
650
  # Links
664
651
  topic: Optional[str] = None
665
- topicLink: Optional['TopicLink'] = None
666
-
652
+ topicLink: Optional["TopicLink"] = None
667
653
 
668
654
  @classmethod
669
655
  def getSamples(cls):
@@ -705,23 +691,23 @@ class Property:
705
691
  ]
706
692
  return samples
707
693
 
708
-
709
694
  def sanitize(self):
710
695
  """
711
696
  make sure attributes such as namespace are set properly
712
697
  """
713
- self.namespace="Property"
714
- self.topicLink=None
698
+ self.namespace = "Property"
699
+ self.topicLink = None
715
700
  if hasattr(self, "scope"):
716
701
  pass
717
702
  if not hasattr(self, "showInGrid"):
718
- self.showInGrid=True
703
+ self.showInGrid = True
719
704
  if not hasattr(self, "label"):
720
- self.label=getattr(self,"name")
705
+ self.label = getattr(self, "name")
721
706
  if not hasattr(self, "type") or not self.type:
722
- self.type="Text"
707
+ self.type = "Text"
723
708
  pass
724
709
 
710
+
725
711
  @lod_storable
726
712
  class TopicLink:
727
713
  """
@@ -16,7 +16,7 @@ class Version(object):
16
16
  description = "pyMetaModel: MetaModel for Knowledge Graphs"
17
17
  version = meta.__version__
18
18
  date = "2022-11-30"
19
- updated = "2025-09-19"
19
+ updated = "2026-07-13"
20
20
  authors = "Wolfgang Fahl"
21
21
  doc_url = "https://wiki.bitplan.com/index.php/pyMetaModel"
22
22
  chat_url = "https://github.com/WolfgangFahl/pyMetaModel/discussions"
@@ -6,6 +6,8 @@ Created on 2023-02-25
6
6
 
7
7
  from meta.smw_type import SMW_Type
8
8
  from tests.basemwtest import BaseMediawikiTest
9
+ from meta.metamodel import Property, Topic
10
+ from tests.basetest import Basetest
9
11
 
10
12
 
11
13
  class TestMetaModel(BaseMediawikiTest):
@@ -30,16 +32,12 @@ class TestMetaModel(BaseMediawikiTest):
30
32
  ask_query = SMW_Type.askQuery()
31
33
  did = smwAccess.smw.query(ask_query)
32
34
  for record in did.values():
33
- smw_type = SMW_Type.from_dict(record) # @UndefinedVariable
35
+ smw_type = SMW_Type.from_dict(record) # @UndefinedVariable
34
36
  if debug:
35
37
  print(smw_type)
36
38
  self.assertIsInstance(smw_type, SMW_Type)
37
39
 
38
40
 
39
- from meta.metamodel import Property, Topic
40
- from tests.basetest import Basetest
41
-
42
-
43
41
  class TestTopic(Basetest):
44
42
  """
45
43
  test Topic methods - see https://github.com/WolfgangFahl/pyMetaModel/issues/36
@@ -61,3 +59,23 @@ class TestTopic(Basetest):
61
59
  topic = Topic()
62
60
  topic.properties = {"at": Property(name="at")}
63
61
  self.assertIsNone(topic.get_primary_key_property())
62
+
63
+ def test_pluralName_from_wiki(self):
64
+ """test that pluralName loaded from SiDIF is used, not the default fallback
65
+ see https://github.com/WolfgangFahl/pyMetaModel/issues/37
66
+ """
67
+ topic = Topic()
68
+ topic.name = "Property"
69
+ topic.pluralName = "Properties"
70
+ pluralName = topic.getPluralName()
71
+ self.assertEqual(pluralName, "Properties")
72
+
73
+ def test_pluralName_default_fallback(self):
74
+ """test that getPluralName falls back to name+s when pluralName not set
75
+ see https://github.com/WolfgangFahl/pyMetaModel/issues/37
76
+ """
77
+ topic = Topic()
78
+ topic.name = "Topic"
79
+ topic.sanitize()
80
+ pluralName = topic.getPluralName()
81
+ self.assertEqual(pluralName, "Topics")
@@ -1 +0,0 @@
1
- __version__ = "0.6.7"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes