pylode 3.3.4__tar.gz → 3.4.1__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 (49) hide show
  1. {pylode-3.3.4 → pylode-3.4.1}/PKG-INFO +4 -4
  2. {pylode-3.3.4 → pylode-3.4.1}/README.md +3 -3
  3. {pylode-3.3.4 → pylode-3.4.1}/pylode/cli.py +2 -2
  4. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/ontpub.py +1 -1
  5. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/vocpub.py +96 -198
  6. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf_elements.py +16 -1
  7. pylode-3.4.1/pylode/version.py +8 -0
  8. {pylode-3.3.4 → pylode-3.4.1}/pyproject.toml +1 -1
  9. pylode-3.3.4/pylode/version.py +0 -3
  10. {pylode-3.3.4 → pylode-3.4.1}/pylode/__init__.py +0 -0
  11. {pylode-3.3.4 → pylode-3.4.1}/pylode/__main__.py +0 -0
  12. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/__init__.py +0 -0
  13. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/__init__.py +0 -0
  14. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/component/__init__.py +0 -0
  15. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/component/example.py +0 -0
  16. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/component/heading.py +0 -0
  17. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/component/link.py +0 -0
  18. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/component/preamble.py +0 -0
  19. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/component/properties_table.py +0 -0
  20. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/component/tooltip.py +0 -0
  21. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/dataset.py +0 -0
  22. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/fragment.py +0 -0
  23. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/html.py +0 -0
  24. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/loader.py +0 -0
  25. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/model.py +0 -0
  26. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/namespace.py +0 -0
  27. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/query/__init__.py +0 -0
  28. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/query/common.py +0 -0
  29. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/query/property_shape.py +0 -0
  30. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/random.py +0 -0
  31. {pylode-3.3.4 → pylode-3.4.1}/pylode/profiles/supermodel/state.py +0 -0
  32. {pylode-3.3.4 → pylode-3.4.1}/pylode/pylode.css +0 -0
  33. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/dcterms-mod.ttl +0 -0
  34. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/licenses.ttl +0 -0
  35. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/ontdoc.ttl +0 -0
  36. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/owl.ttl +0 -0
  37. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/prov.ttl +0 -0
  38. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/rdf.ttl +0 -0
  39. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/rdfs-mod.ttl +0 -0
  40. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/sdo-mod.ttl +0 -0
  41. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/skos.ttl +0 -0
  42. {pylode-3.3.4 → pylode-3.4.1}/pylode/rdf/void.ttl +0 -0
  43. {pylode-3.3.4 → pylode-3.4.1}/pylode/server.py +0 -0
  44. {pylode-3.3.4 → pylode-3.4.1}/pylode/static/asciidoc.css +0 -0
  45. {pylode-3.3.4 → pylode-3.4.1}/pylode/static/hierarchy.css +0 -0
  46. {pylode-3.3.4 → pylode-3.4.1}/pylode/static/hierarchy.js +0 -0
  47. {pylode-3.3.4 → pylode-3.4.1}/pylode/static/property-table-row.js +0 -0
  48. {pylode-3.3.4 → pylode-3.4.1}/pylode/static/pylode.css +0 -0
  49. {pylode-3.3.4 → pylode-3.4.1}/pylode/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pylode
3
- Version: 3.3.4
3
+ Version: 3.4.1
4
4
  Summary: An OWL ontology documentation tool using Python, based on LODE.
5
5
  Author: Nicholas Car
6
6
  Author-email: Nicholas Car <nick@kurrawong.ai>
@@ -29,7 +29,7 @@ Project-URL: Changelog, https://github.com/RDFLib/pyLODE/releases
29
29
  Provides-Extra: server
30
30
  Description-Content-Type: text/markdown
31
31
 
32
- ![pyLODE logo](https://rawcdn.githack.com/RDFLib/pyLODE/master/img/pyLODE-250.png)
32
+ ![pyLODE logo](https://raw.githubusercontent.com/RDFLib/pyLODE/master/img/pyLODE-250.png)
33
33
  [![PyPI version](https://badge.fury.io/py/pyLODE.svg)](https://badge.fury.io/py/pyLODE)
34
34
 
35
35
  # pyLODE
@@ -152,8 +152,8 @@ The active endpoint accepts the following querystring parameters:
152
152
  * `url` for the absolute URL of the ontology document that you wish to render. The server hosting that ontology document must be capable of responding to Content Negotiation,
153
153
  i.e. it must supply RDF according to an HTTP `Accept` request for `text/turtle`, `application/rdf+xml` etc.
154
154
  * `profile` for the profile to use to generate HTML. Must be one of:
155
- * `ontpub` (https://w3id.org/profile/ontpub) for ontologies. This is the default if no ``profile`` is provided.
156
- * `vocpub` (https://w3id.org/profile/vocpub) for SKOS vocabularies
155
+ * `ontpub` (https://linked.data.gov.au/def/ontpub) for ontologies. This is the default if no ``profile`` is provided.
156
+ * `vocpub` (https://linked.data.gov.au/def/vocpub) for SKOS vocabularies
157
157
  * `supermodel` for profiles of profiles
158
158
  * `sort` to indicate whether subjects should be sorted in the rendered output. Must be one of:
159
159
  * `true` to sort the subjects (this is the default)
@@ -1,4 +1,4 @@
1
- ![pyLODE logo](https://rawcdn.githack.com/RDFLib/pyLODE/master/img/pyLODE-250.png)
1
+ ![pyLODE logo](https://raw.githubusercontent.com/RDFLib/pyLODE/master/img/pyLODE-250.png)
2
2
  [![PyPI version](https://badge.fury.io/py/pyLODE.svg)](https://badge.fury.io/py/pyLODE)
3
3
 
4
4
  # pyLODE
@@ -121,8 +121,8 @@ The active endpoint accepts the following querystring parameters:
121
121
  * `url` for the absolute URL of the ontology document that you wish to render. The server hosting that ontology document must be capable of responding to Content Negotiation,
122
122
  i.e. it must supply RDF according to an HTTP `Accept` request for `text/turtle`, `application/rdf+xml` etc.
123
123
  * `profile` for the profile to use to generate HTML. Must be one of:
124
- * `ontpub` (https://w3id.org/profile/ontpub) for ontologies. This is the default if no ``profile`` is provided.
125
- * `vocpub` (https://w3id.org/profile/vocpub) for SKOS vocabularies
124
+ * `ontpub` (https://linked.data.gov.au/def/ontpub) for ontologies. This is the default if no ``profile`` is provided.
125
+ * `vocpub` (https://linked.data.gov.au/def/vocpub) for SKOS vocabularies
126
126
  * `supermodel` for profiles of profiles
127
127
  * `sort` to indicate whether subjects should be sorted in the rendered output. Must be one of:
128
128
  * `true` to sort the subjects (this is the default)
@@ -44,8 +44,8 @@ parser.add_argument(
44
44
  "-p",
45
45
  "--profile",
46
46
  help="Which profile to use to generate HTML. Must be one of "
47
- "'ontpub' (https://w3id.org/profile/ontpub) - for ontologies, "
48
- "'vocpub' (https://w3id.org/profile/vocpub) - for SKOS vocabularies"
47
+ "'ontpub' (https://linked.data.gov.au/def/ontpub) - for ontologies, "
48
+ "'vocpub' (https://linked.data.gov.au/def/vocpub) - for SKOS vocabularies"
49
49
  "'supermodel' - for profiles of profiles",
50
50
  choices=["ontpub", "vocpub", "supermodel"],
51
51
  default="ontpub",
@@ -316,7 +316,7 @@ class OntPub:
316
316
 
317
317
  Just calls other helper functions in order"""
318
318
  make_pylode_logo(
319
- self.doc, __version__, "OntPub", "https://w3id.org/profile/ontpub"
319
+ self.doc, __version__, "OntPub", "https://linked.data.gov.au/def/ontpub"
320
320
  )
321
321
  self._make_metadata()
322
322
  self._make_main_sections()
@@ -3,6 +3,7 @@ from collections import defaultdict
3
3
  from itertools import chain
4
4
  from pathlib import Path
5
5
  from typing import Dict, Union
6
+ from rdflib import URIRef, Literal
6
7
 
7
8
  import dominate
8
9
  from dominate.tags import (
@@ -44,6 +45,8 @@ from rdflib.namespace import (
44
45
  SKOS,
45
46
  )
46
47
 
48
+ from ..rdf_elements import CONCEPT_PROPS
49
+
47
50
  try:
48
51
  from .rdf_elements import AGENT_PROPS, CLASS_PROPS, ONT_PROPS, ONTDOC, PROP_PROPS
49
52
  from .utils import (
@@ -274,6 +277,9 @@ class VocPub:
274
277
  for s_, o in g.subject_objects(ORG.memberOf):
275
278
  g.add((s_, SDO.affiliation, o))
276
279
 
280
+ for s, o in g.subject_objects(SKOS.broader):
281
+ g.add((o, SKOS.narrower, s))
282
+
277
283
  def _make_head(
278
284
  self, schema_org: Graph, include_css: bool = True, destination: Path = None
279
285
  ):
@@ -318,12 +324,12 @@ class VocPub:
318
324
 
319
325
  Just calls other helper functions in order"""
320
326
  make_pylode_logo(
321
- self.doc, __version__, "VocPub", "https://w3id.org/profile/vocpub"
327
+ self.doc, __version__, "VocPub", "https://linked.data.gov.au/def/vocpub"
322
328
  )
323
329
  self._make_metadata()
330
+ self._make_concept_hierarchy()
324
331
  self._make_main_sections()
325
332
  self._make_namespaces()
326
- self._make_legend()
327
333
  self._make_toc()
328
334
 
329
335
  def _make_metadata(self):
@@ -406,149 +412,97 @@ class VocPub:
406
412
 
407
413
  return sdo
408
414
 
409
- def _make_main_sections(self):
415
+ def _make_concept_hierarchy(self):
410
416
  with self.content:
411
- if (None, RDF.type, OWL.Class) in self.ont:
412
- d = section_html(
413
- "Classes",
414
- self.ont,
415
- self.back_onts,
416
- self.ns,
417
- OWL.Class,
418
- CLASS_PROPS,
419
- self.toc,
420
- "classes",
421
- self.fids,
422
- self.props_labeled,
423
- )
424
- d.render()
425
-
426
- if (None, RDF.type, RDF.Property) in self.ont:
427
- d = section_html(
428
- "Properties",
429
- self.ont,
430
- self.back_onts,
431
- self.ns,
432
- RDF.Property,
433
- PROP_PROPS,
434
- self.toc,
435
- "properties",
436
- self.fids,
437
- self.props_labeled,
438
- )
439
- d.render()
440
-
441
- if (None, RDF.type, OWL.ObjectProperty) in self.ont:
442
- d = section_html(
443
- "Object Properties",
444
- self.ont,
445
- self.back_onts,
446
- self.ns,
447
- OWL.ObjectProperty,
448
- PROP_PROPS,
449
- self.toc,
450
- "objectproperties",
451
- self.fids,
452
- self.props_labeled,
453
- )
454
- d.render()
455
-
456
- if (None, RDF.type, OWL.DatatypeProperty) in self.ont:
457
- d = section_html(
458
- "Datatype Properties",
459
- self.ont,
460
- self.back_onts,
461
- self.ns,
462
- OWL.DatatypeProperty,
463
- PROP_PROPS,
464
- self.toc,
465
- "datatypeproperties",
466
- self.fids,
467
- self.props_labeled,
468
- )
469
- d.render()
470
-
471
- if (None, RDF.type, OWL.AnnotationProperty) in self.ont:
472
- d = section_html(
473
- "Annotation Properties",
474
- self.ont,
475
- self.back_onts,
476
- self.ns,
477
- OWL.AnnotationProperty,
478
- PROP_PROPS,
479
- self.toc,
480
- "annotationproperties",
481
- self.fids,
482
- self.props_labeled,
417
+ if (None, RDF.type, SKOS.Concept) in self.ont:
418
+ concepts = []
419
+ for s in self.ont.subjects(RDF.type, SKOS.Concept):
420
+ for o in self.ont.objects(s, SKOS.prefLabel):
421
+ c = {
422
+ "iri": str(s),
423
+ "prefLabel": str(o)
424
+ }
425
+ for o2 in self.ont.objects(s, SKOS.broader):
426
+ c["broader"] = str(o2)
427
+ concepts.append(c)
428
+
429
+ def build_html_tree(items):
430
+ # Index items by id
431
+ by_id = {item["iri"]: dict(item, children=[]) for item in items}
432
+
433
+ roots = []
434
+
435
+ # Build tree structure
436
+ for item in by_id.values():
437
+ broader = item.get("broader")
438
+ if broader and broader in by_id:
439
+ by_id[broader]["children"].append(item)
440
+ else:
441
+ roots.append(item)
442
+
443
+ # Recursive renderer
444
+ def render_nodes(nodes):
445
+ container = ul()
446
+ for node in nodes:
447
+ node_li = li(a(node["prefLabel"], href="#" + str(self.ont.namespace_manager.qname(node["iri"]).replace(":", "_")),))
448
+ if node["children"]:
449
+ node_li.add(render_nodes(node["children"]))
450
+ container.add(node_li)
451
+ return container
452
+
453
+ return render_nodes(roots)
454
+
455
+ d = div(
456
+ h2("Concept Hierarchy"),
457
+ build_html_tree(concepts),
458
+ id="concept-hierarchy"
483
459
  )
484
- d.render()
485
460
 
486
- if (None, RDF.type, OWL.FunctionalProperty) in self.ont:
487
- d = section_html(
488
- "Functional Properties",
489
- self.ont,
490
- self.back_onts,
491
- self.ns,
492
- OWL.FunctionalProperty,
493
- PROP_PROPS,
494
- self.toc,
495
- "functionalproperties",
496
- self.fids,
497
- self.props_labeled,
498
- )
499
461
  d.render()
500
462
 
501
- def _make_legend(self):
463
+ def _make_main_sections(self):
464
+ cs_iri = self.ont.value(predicate=RDF.type, object=SKOS.ConceptScheme)
502
465
  with self.content:
503
- with div(id="legend"):
504
- h2("Legend")
505
- with table(_class="entity"):
506
- if self.toc.get("classes") is not None:
507
- with tr():
508
- td(sup("c", _class="sup-c", title="OWL/RDFS Class"))
509
- td("Classes")
510
- if self.toc.get("properties") is not None:
511
- with tr():
512
- td(sup("p", _class="sup-p", title="RDF Property"))
513
- td("Properties")
514
- if self.toc.get("objectproperties") is not None:
515
- with tr():
516
- td(sup("op", _class="sup-op", title="OWL Object Property"))
517
- td("Object Properties")
518
- if self.toc.get("datatypeproperties") is not None:
519
- with tr():
520
- td(
521
- sup(
522
- "dp",
523
- _class="sup-dp",
524
- title="OWL Datatype Property",
525
- )
526
- )
527
- td("Datatype Properties")
528
- if self.toc.get("annotationproperties") is not None:
529
- with tr():
530
- td(
531
- sup(
532
- "ap",
533
- _class="sup-ap",
534
- title="OWL Annotation Property",
535
- )
536
- )
537
- td("Annotation Properties")
538
- if self.toc.get("functionalproperties") is not None:
539
- with tr():
540
- td(
541
- sup(
542
- "fp",
543
- _class="sup-fp",
544
- title="OWL Functional Property",
545
- )
546
- )
547
- td("Functional Properties")
548
- if self.toc.get("named_individuals") is not None:
549
- with tr():
550
- td(sup("ni", _class="sup-ni", title="OWL Named Individual"))
551
- td("Named Individuals")
466
+ if (None, RDF.type, SKOS.Concept) in self.ont:
467
+ concepts = {}
468
+ for s in self.ont.subjects(RDF.type, SKOS.Concept):
469
+ concepts[s] = {}
470
+ for p, o in self.ont.predicate_objects(s):
471
+ concepts[s][p] = o
472
+
473
+ def order_dict_by_key_list_keep_rest(d, key_order):
474
+ ordered = {k: d[k] for k in key_order if k in d}
475
+ remaining = {k: v for k, v in d.items() if k not in ordered}
476
+ return ordered | remaining
477
+
478
+ d = div(
479
+ h2("Concepts"),
480
+ id="concepts"
481
+ )
482
+ for iri, concept in order_dict_by_key_list_keep_rest(concepts, CONCEPT_PROPS).items(): # order by CONCEPT_PROPS
483
+ props_table = table()
484
+ props_table.add(tr(td(strong("IRI"), style="width:250px;"), td(code(iri))))
485
+ for k, v in concept.items():
486
+ if k not in [RDF.type, SKOS.prefLabel, "iri", SKOS.inScheme]:
487
+ if k in CONCEPT_PROPS:
488
+ if isinstance(v, URIRef):
489
+ if v == cs_iri:
490
+ v = "This vocabulary"
491
+ else:
492
+ curie = self.ont.namespace_manager.qname(v).replace(":", "_")
493
+ v = a(self.ont.value(subject=v, predicate=DCTERMS.title), href="#" + curie)
494
+ p_label = self.props_labeled.get(k).get("title").title()
495
+ p_desc = self.props_labeled.get(k).get("description")
496
+ props_table.add(tr(td(a(p_label, href=k, title=p_desc)), td(v)))
497
+ #props.add(tr(td(k), td(v)))
498
+ d.add(
499
+ div(
500
+ h3(concept[DCTERMS.title], id=self.ont.namespace_manager.qname(iri).replace(":", "_")),
501
+ props_table,
502
+ _class = "entity"
503
+ )
504
+ )
505
+ d.render()
552
506
 
553
507
  def _make_namespaces(self):
554
508
  # only get namespaces used in ont
@@ -591,65 +545,9 @@ class VocPub:
591
545
  with ul(_class="first"):
592
546
  li(h4(a("Metadata", href="#metadata")))
593
547
 
594
- if (
595
- self.toc.get("classes") is not None
596
- and len(self.toc["classes"]) > 0
597
- ):
598
- with li():
599
- h4(a("Classes", href="#classes"))
600
- with ul(_class="second"):
601
- for c in self.toc["classes"]:
602
- li(a(c[1], href=c[0]))
603
-
604
- if (
605
- self.toc.get("properties") is not None
606
- and len(self.toc["properties"]) > 0
607
- ):
608
- with li():
609
- h4(a("Properties", href="#properties"))
610
- with ul(_class="second"):
611
- for c in self.toc["properties"]:
612
- li(a(c[1], href=c[0]))
613
-
614
- if (
615
- self.toc.get("objectproperties") is not None
616
- and len(self.toc["objectproperties"]) > 0
617
- ):
618
- with li():
619
- h4(a("Object Properties", href="#objectproperties"))
620
- with ul(_class="second"):
621
- for c in self.toc["objectproperties"]:
622
- li(a(c[1], href=c[0]))
623
-
624
- if (
625
- self.toc.get("datatypeproperties") is not None
626
- and len(self.toc["datatypeproperties"]) > 0
627
- ):
628
- with li():
629
- h4(a("Datatype Properties", href="#datatypeproperties"))
630
- with ul(_class="second"):
631
- for c in self.toc["datatypeproperties"]:
632
- li(a(c[1], href=c[0]))
633
-
634
- if (
635
- self.toc.get("annotationproperties") is not None
636
- and len(self.toc["annotationproperties"]) > 0
637
- ):
638
- with li():
639
- h4(a("Annotation Properties", href="#annotationproperties"))
640
- with ul(_class="second"):
641
- for c in self.toc["annotationproperties"]:
642
- li(a(c[1], href=c[0]))
643
-
644
- if (
645
- self.toc.get("functionalproperties") is not None
646
- and len(self.toc["functionalproperties"]) > 0
647
- ):
648
- with li():
649
- h4(a("Functional Properties", href="#functionalproperties"))
650
- with ul(_class="second"):
651
- for c in self.toc["functionalproperties"]:
652
- li(a(c[1], href=c[0]))
548
+ li(h4(a("Concepts Hierarchy", href="#concept-hierarchy")))
549
+
550
+ li(h4(a("Concepts", href="#concepts")))
653
551
 
654
552
  with li():
655
553
  h4(a("Namespaces", href="#namespaces"))
@@ -62,6 +62,21 @@ CLASS_PROPS = [
62
62
  ONTDOC.superClassOf,
63
63
  ]
64
64
 
65
+ # properties for SKOS Concept instances
66
+ CONCEPT_PROPS = [
67
+ SKOS.prefLabel,
68
+ SKOS.definition,
69
+ RDFS.isDefinedBy,
70
+ SKOS.inScheme,
71
+ SKOS.altLabel,
72
+ SKOS.example,
73
+ DCTERMS.source,
74
+ DCTERMS.provenance,
75
+ SKOS.note,
76
+ SKOS.broader,
77
+ SKOS.narrower,
78
+ ]
79
+
65
80
  # properties for instances of RDF Property and OWL specialised
66
81
  # forms, such as ObjectProperty etc.
67
82
  PROP_PROPS = [
@@ -107,7 +122,7 @@ RESTRICTION_PROPS = [
107
122
  ]
108
123
 
109
124
  # all known properties
110
- PROPS = set(ONT_PROPS + CLASS_PROPS + PROP_PROPS + AGENT_PROPS + RESTRICTION_PROPS)
125
+ PROPS = set(ONT_PROPS + CLASS_PROPS + PROP_PROPS + AGENT_PROPS + RESTRICTION_PROPS + CONCEPT_PROPS)
111
126
 
112
127
  ONT_TYPES = {
113
128
  OWL.Class: ("c", "OWL/RDFS Class"),
@@ -0,0 +1,8 @@
1
+ import tomllib
2
+ from pathlib import Path
3
+
4
+ _pyproject_file = Path(__file__).parent.parent / "pyproject.toml"
5
+ with _pyproject_file.open("rb") as f:
6
+ _data = tomllib.load(f)
7
+
8
+ __version__ = _data["project"]["version"]
@@ -7,7 +7,7 @@ Changelog = "https://github.com/RDFLib/pyLODE/releases"
7
7
 
8
8
  [project]
9
9
  name = "pylode"
10
- version = "3.3.4"
10
+ version = "3.4.1"
11
11
  description = "An OWL ontology documentation tool using Python, based on LODE."
12
12
  authors = [{ name = "Nicholas Car", email = "nick@kurrawong.ai" }]
13
13
  requires-python = ">=3.9,<4"
@@ -1,3 +0,0 @@
1
- import importlib.metadata
2
-
3
- __version__ = importlib.metadata.version(__package__)
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