Skip to content

ontodoc

A module for documenting ontologies.

AttributeDict

Bases: dict

A dict with attribute access.

Note that methods like key() and update() may be overridden.

Source code in ontopy/ontodoc.py
424
425
426
427
428
429
430
431
class AttributeDict(dict):
    """A dict with attribute access.

    Note that methods like key() and update() may be overridden."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

DocPP

Documentation pre-processor.

It supports the following features
  • Comment lines

    %% Comment line...

  • Insert header with given level

    %HEADER label [level=1]

  • Insert figure with optional caption and width. filepath should be relative to basedir. If width is 0, no width will be specified.

    %FIGURE filepath [caption='' width=0px]

  • Include other markdown files. Header levels may be up or down with shift

    %INCLUDE filepath [shift=0]

  • Insert generated documentation for ontology entity. The header level may be set with header_level.

    %ENTITY name [header_level=3]

  • Insert generated documentation for ontology branch name. Options:

    • header_level: Header level.
    • terminated: Whether to branch should be terminated at all branch names in the final document.
    • include_leafs: Whether to include leaf.

    %BRANCH name [header_level=3 terminated=1 include_leafs=0 namespaces='' ontologies='']

  • Insert generated figure of ontology branch name. The figure is written to path. The default path is figdir/name, where figdir is given at class initiation. It is recommended to exclude the file extension from path. In this case, the default figformat will be used (and easily adjusted to the correct format required by the backend). leafs may be a comma- separated list of leaf node names.

    %BRANCHFIG name [path='' caption='' terminated=1 include_leafs=1 strict_leafs=1, width=0px leafs='' relations=all edgelabels=0 namespaces='' ontologies='']

  • This is a combination of the %HEADER and %BRANCHFIG directives.

    %BRANCHHEAD name [level=2 path='' caption='' terminated=1 include_leafs=1 width=0px leafs='']

  • This is a combination of the %HEADER, %BRANCHFIG and %BRANCH directives. It inserts documentation of branch name, with a header followed by a figure and then documentation of each element.

    %BRANCHDOC name [level=2 path='' title='' caption='' terminated=1 strict_leafs=1 width=0px leafs='' relations='all' rankdir='BT' legend=1 namespaces='' ontologies='']

  • Insert generated documentation for all entities of the given type. Valid values of type are: "classes", "individuals", "object_properties", "data_properties", "annotations_properties"

    %ALL type [header_level=3, namespaces='', ontologies='']

  • Insert generated figure of all entities of the given type. Valid values of type are: "classes", "object_properties" and "data_properties".

    %ALLFIG type

Parameters

str

Input template.

OntoDoc instance

Instance of OntoDoc

str

Base directory for including relative file paths.

str

Default directory to store generated figures.

str

Default format for generated figures.

float

Default scaling of generated figures.

float

Maximum figure width. Figures larger than this will be rescaled.

bool

Whether to include imported entities.

Source code in ontopy/ontodoc.py
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
class DocPP:  # pylint: disable=too-many-instance-attributes
    """Documentation pre-processor.

    It supports the following features:

      * Comment lines

            %% Comment line...

      * Insert header with given level

            %HEADER label [level=1]

      * Insert figure with optional caption and width. `filepath`
        should be relative to `basedir`.  If width is 0, no width will
        be specified.

            %FIGURE filepath [caption='' width=0px]

      * Include other markdown files.  Header levels may be up or down with
        `shift`

            %INCLUDE filepath [shift=0]

      * Insert generated documentation for ontology entity.  The header
        level may be set with `header_level`.

            %ENTITY name [header_level=3]

      * Insert generated documentation for ontology branch `name`.  Options:
          - header_level: Header level.
          - terminated: Whether to branch should be terminated at all branch
            names in the final document.
          - include_leafs: Whether to include leaf.

            %BRANCH name [header_level=3 terminated=1 include_leafs=0
                          namespaces='' ontologies='']

      * Insert generated figure of ontology branch `name`.  The figure
        is written to `path`.  The default path is `figdir`/`name`,
        where `figdir` is given at class initiation. It is recommended
        to exclude the file extension from `path`.  In this case, the
        default figformat will be used (and easily adjusted to the
        correct format required by the backend). `leafs` may be a comma-
        separated list of leaf node names.

            %BRANCHFIG name [path='' caption='' terminated=1 include_leafs=1
                             strict_leafs=1, width=0px leafs='' relations=all
                             edgelabels=0 namespaces='' ontologies='']

      * This is a combination of the %HEADER and %BRANCHFIG directives.

            %BRANCHHEAD name [level=2  path='' caption='' terminated=1
                              include_leafs=1 width=0px leafs='']

      * This is a combination of the %HEADER, %BRANCHFIG and %BRANCH
        directives. It inserts documentation of branch `name`, with a
        header followed by a figure and then documentation of each
        element.

            %BRANCHDOC name [level=2  path='' title='' caption='' terminated=1
                             strict_leafs=1 width=0px leafs='' relations='all'
                             rankdir='BT' legend=1 namespaces='' ontologies='']

      * Insert generated documentation for all entities of the given type.
        Valid values of `type` are: "classes", "individuals",
        "object_properties", "data_properties", "annotations_properties"

            %ALL type [header_level=3, namespaces='', ontologies='']

      * Insert generated figure of all entities of the given type.
        Valid values of `type` are: "classes", "object_properties" and
        "data_properties".

            %ALLFIG type

    Parameters
    ----------
    template : str
        Input template.
    ontodoc : OntoDoc instance
        Instance of OntoDoc
    basedir : str
        Base directory for including relative file paths.
    figdir : str
        Default directory to store generated figures.
    figformat : str
        Default format for generated figures.
    figscale : float
        Default scaling of generated figures.
    maxwidth : float
        Maximum figure width.  Figures larger than this will be rescaled.
    imported : bool
        Whether to include imported entities.
    """

    # FIXME - this class should be refractured:
    #   * Instead of rescan the entire document for each pre-processer
    #     directive, we should scan the source like by line and handle
    #     each directive as they occour.
    #   * The current implementation has a lot of dublicated code.
    #   * Instead of modifying the source in-place, we should copy to a
    #     result list. This will make good error reporting much easier.
    #   * Branch leaves are only looked up in the file witht the %BRANCH
    #     directive, not in all included files as expedted.

    def __init__(  # pylint: disable=too-many-arguments
        self,
        template,
        ontodoc,
        basedir=".",
        figdir="genfigs",
        figformat="png",
        figscale=1.0,
        maxwidth=None,
        imported=False,
    ):
        self.lines = template.split("\n")
        self.ontodoc = ontodoc
        self.basedir = basedir
        self.figdir = os.path.join(basedir, figdir)
        self.figformat = figformat
        self.figscale = figscale
        self.maxwidth = maxwidth
        self.imported = imported
        self._branch_cache = None
        self._processed = False  # Whether process() has been called

    def __str__(self):
        return self.get_buffer()

    def get_buffer(self):
        """Returns the current buffer."""
        return "\n".join(self.lines)

    def copy(self):
        """Returns a copy of self."""
        docpp = DocPP(
            "",
            self.ontodoc,
            self.basedir,
            figformat=self.figformat,
            figscale=self.figscale,
            maxwidth=self.maxwidth,
        )
        docpp.lines[:] = self.lines
        docpp.figdir = self.figdir
        return docpp

    def get_branches(self):
        """Returns a list with all branch names as specified with %BRANCH
        (in current and all included documents).  The returned value is
        cached for efficiency purposes and so that it is not lost after
        processing branches."""
        if self._branch_cache is None:
            names = []
            docpp = self.copy()
            docpp.process_includes()
            for line in docpp.lines:
                if line.startswith("%BRANCH"):
                    names.append(shlex.split(line)[1])
            self._branch_cache = names
        return self._branch_cache

    def shift_header_levels(self, shift):
        """Shift header level of all hashtag-headers in buffer.  Underline
        headers are ignored."""
        if not shift:
            return
        pat = re.compile("^#+ ")
        for i, line in enumerate(self.lines):
            match = pat.match(line)
            if match:
                if shift > 0:
                    self.lines[i] = "#" * shift + line
                elif shift < 0:
                    counter = match.end()
                    if shift > counter:
                        self.lines[i] = line.lstrip("# ")
                    else:
                        self.lines[i] = line[counter:]

    def process_comments(self):
        """Strips out comment lines starting with "%%"."""
        self.lines = [line for line in self.lines if not line.startswith("%%")]

    def process_headers(self):
        """Expand all %HEADER specifications."""
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%HEADER "):
                tokens = shlex.split(line)
                name = tokens[1]
                opts = get_options(tokens[2:], level=1)
                del self.lines[i]
                self.lines[i:i] = self.ontodoc.get_header(
                    name, int(opts.level)  # pylint: disable=no-member
                ).split("\n")

    def process_figures(self):
        """Expand all %FIGURE specifications."""
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%FIGURE "):
                tokens = shlex.split(line)
                path = tokens[1]
                opts = get_options(tokens[2:], caption="", width=0)
                del self.lines[i]
                self.lines[i:i] = self.ontodoc.get_figure(
                    os.path.join(self.basedir, path),
                    caption=opts.caption,  # pylint: disable=no-member
                    width=opts.width,  # pylint: disable=no-member
                ).split("\n")

    def process_entities(self):
        """Expand all %ENTITY specifications."""
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%ENTITY "):
                tokens = shlex.split(line)
                name = tokens[1]
                opts = get_options(tokens[2:], header_level=3)
                del self.lines[i]
                self.lines[i:i] = self.ontodoc.itemdoc(
                    name, int(opts.header_level)  # pylint: disable=no-member
                ).split("\n")

    def process_branches(self):
        """Expand all %BRANCH specifications."""
        onto = self.ontodoc.onto

        # Get all branch names in final document
        names = self.get_branches()
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%BRANCH "):
                tokens = shlex.split(line)
                name = tokens[1]
                opts = get_options(
                    tokens[2:],
                    header_level=3,
                    terminated=1,
                    include_leafs=0,
                    namespaces="",
                    ontologies="",
                )
                leafs = (
                    names if opts.terminated else ()
                )  # pylint: disable=no-member

                included_namespaces = (
                    opts.namespaces.split(",")
                    if opts.namespaces
                    else ()  # pylint: disable=no-member
                )
                included_ontologies = (
                    opts.ontologies.split(",")
                    if opts.ontologies
                    else ()  # pylint: disable=no-member
                )

                branch = filter_classes(
                    onto.get_branch(
                        name, leafs, opts.include_leafs
                    ),  # pylint: disable=no-member
                    included_namespaces=included_namespaces,
                    included_ontologies=included_ontologies,
                )

                del self.lines[i]
                self.lines[i:i] = self.ontodoc.itemsdoc(
                    branch, int(opts.header_level)  # pylint: disable=no-member
                ).split("\n")

    def _make_branchfig(  # pylint: disable=too-many-arguments,too-many-locals
        self,
        name: str,
        path: "Union[Path, str]",
        terminated: bool,
        include_leafs: bool,
        strict_leafs: bool,
        width: float,
        leafs: "Union[str, list[str]]",
        relations: str,
        edgelabels: str,
        rankdir: str,
        legend: bool,
        included_namespaces: "Iterable[str]",
        included_ontologies: "Iterable[str]",
    ) -> "tuple[str, list[str], float]":
        """Help method for process_branchfig().

        Args:
            name: name of branch root
            path: optional figure path name
            include_leafs: whether to include leafs
            strict_leafs: whether strictly exclude leafs descendants
            terminated: whether the graph should be terminated at leaf nodes
            width: optional figure width
            leafs: optional leafs node names for graph termination
            relations: comma-separated list of relations to include
            edgelabels: whether to include edgelabels
            rankdir: graph direction (BT, TB, RL, LR)
            legend: whether to add legend
            included_namespaces: sequence of names of namespaces to be included
            included_ontologies: sequence of names of ontologies to be included

        Returns:
            filepath: path to generated figure
            leafs: used list of leaf node names
            width: actual figure width

        """
        onto = self.ontodoc.onto
        if leafs:
            if isinstance(leafs, str):
                leafs = leafs.split(",")
        elif terminated:
            leafs = set(self.get_branches())
            leafs.discard(name)
        else:
            leafs = None
        if path:
            figdir = os.path.dirname(path)
            formatext = os.path.splitext(path)[1]
            if formatext:
                fmt = formatext.lstrip(".")
            else:
                fmt = self.figformat
                path += f".{fmt}"
        else:
            figdir = self.figdir
            fmt = self.figformat
            term = "T" if terminated else ""
            path = os.path.join(figdir, name + term) + f".{fmt}"

        # Create graph
        graph = OntoGraph(onto, graph_attr={"rankdir": rankdir})
        graph.add_branch(
            root=name,
            leafs=leafs,
            include_leafs=include_leafs,
            strict_leafs=strict_leafs,
            relations=relations,
            edgelabels=edgelabels,
            included_namespaces=included_namespaces,
            included_ontologies=included_ontologies,
        )
        if legend:
            graph.add_legend()

        if not width:
            figwidth, _ = graph.get_figsize()
            width = self.figscale * figwidth
            if self.maxwidth and width > self.maxwidth:
                width = self.maxwidth

        filepath = os.path.join(self.basedir, path)
        destdir = os.path.dirname(filepath)
        if not os.path.exists(destdir):
            os.makedirs(destdir)
        graph.save(filepath, fmt=fmt)
        return filepath, leafs, width

    def process_branchfigs(self):
        """Process all %BRANCHFIG directives."""
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%BRANCHFIG "):
                tokens = shlex.split(line)
                name = tokens[1]
                opts = get_options(
                    tokens[2:],
                    path="",
                    caption="",
                    terminated=1,
                    include_leafs=1,
                    strict_leafs=1,
                    width=0,
                    leafs="",
                    relations="all",
                    edgelabels=0,
                    rankdir="BT",
                    legend=1,
                    namespaces="",
                    ontologies="",
                )

                included_namespaces = (
                    opts.namespaces.split(",")
                    if opts.namespaces
                    else ()  # pylint: disable=no-member
                )
                included_ontologies = (
                    opts.ontologies.split(",")
                    if opts.ontologies
                    else ()  # pylint: disable=no-member
                )

                filepath, _, width = self._make_branchfig(
                    name,
                    opts.path,  # pylint: disable=no-member
                    opts.terminated,  # pylint: disable=no-member
                    opts.include_leafs,  # pylint: disable=no-member
                    opts.strict_leafs,  # pylint: disable=no-member
                    opts.width,  # pylint: disable=no-member
                    opts.leafs,  # pylint: disable=no-member
                    opts.relations,  # pylint: disable=no-member
                    opts.edgelabels,  # pylint: disable=no-member
                    opts.rankdir,  # pylint: disable=no-member
                    opts.legend,  # pylint: disable=no-member
                    included_namespaces,
                    included_ontologies,
                )

                del self.lines[i]
                self.lines[i:i] = self.ontodoc.get_figure(
                    filepath,
                    caption=opts.caption,
                    width=width,  # pylint: disable=no-member
                ).split("\n")

    def process_branchdocs(self):  # pylint: disable=too-many-locals
        """Process all %BRANCHDOC and  %BRANCHEAD directives."""
        onto = self.ontodoc.onto
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%BRANCHDOC ") or line.startswith(
                "%BRANCHHEAD "
            ):
                with_branch = bool(line.startswith("%BRANCHDOC "))
                tokens = shlex.split(line)
                name = tokens[1]
                title = camelsplit(name)
                title = title[0].upper() + title[1:] + " branch"
                opts = get_options(
                    tokens[2:],
                    level=2,
                    path="",
                    title=title,
                    caption=title + ".",
                    terminated=1,
                    strict_leafs=1,
                    width=0,
                    leafs="",
                    relations="all",
                    edgelabels=0,
                    rankdir="BT",
                    legend=1,
                    namespaces="",
                    ontologies="",
                )

                included_namespaces = (
                    opts.namespaces.split(",")
                    if opts.namespaces
                    else ()  # pylint: disable=no-member
                )
                included_ontologies = (
                    opts.ontologies.split(",")
                    if opts.ontologies
                    else ()  # pylint: disable=no-member
                )

                include_leafs = 1
                filepath, leafs, width = self._make_branchfig(
                    name,
                    opts.path,  # pylint: disable=no-member
                    opts.terminated,  # pylint: disable=no-member
                    include_leafs,
                    opts.strict_leafs,  # pylint: disable=no-member
                    opts.width,  # pylint: disable=no-member
                    opts.leafs,  # pylint: disable=no-member
                    opts.relations,  # pylint: disable=no-member
                    opts.edgelabels,  # pylint: disable=no-member
                    opts.rankdir,  # pylint: disable=no-member
                    opts.legend,  # pylint: disable=no-member
                    included_namespaces,
                    included_ontologies,
                )

                sec = []
                sec.append(
                    self.ontodoc.get_header(opts.title, int(opts.level))
                )  # pylint: disable=no-member
                sec.append(
                    self.ontodoc.get_figure(
                        filepath,
                        caption=opts.caption,
                        width=width,  # pylint: disable=no-member
                    )
                )
                if with_branch:
                    include_leafs = 0
                    branch = filter_classes(
                        onto.get_branch(name, leafs, include_leafs),
                        included_namespaces=included_namespaces,
                        included_ontologies=included_ontologies,
                    )
                    sec.append(
                        self.ontodoc.itemsdoc(
                            branch, int(opts.level + 1)
                        )  # pylint: disable=no-member
                    )

                del self.lines[i]
                self.lines[i:i] = sec

    def process_alls(self):
        """Expand all %ALL specifications."""
        onto = self.ontodoc.onto
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%ALL "):
                tokens = shlex.split(line)
                token = tokens[1]
                opts = get_options(tokens[2:], header_level=3)
                if token == "classes":  # nosec
                    items = onto.classes(imported=self.imported)
                elif token in ("object_properties", "relations"):
                    items = onto.object_properties(imported=self.imported)
                elif token == "data_properties":  # nosec
                    items = onto.data_properties(imported=self.imported)
                elif token == "annotation_properties":  # nosec
                    items = onto.annotation_properties(imported=self.imported)
                elif token == "individuals":  # nosec
                    items = onto.individuals(imported=self.imported)
                else:
                    raise InvalidTemplateError(
                        f"Invalid argument to %%ALL: {token}"
                    )
                items = sorted(items, key=asstring)
                del self.lines[i]
                self.lines[i:i] = self.ontodoc.itemsdoc(
                    items, int(opts.header_level)  # pylint: disable=no-member
                ).split("\n")

    def process_allfig(self):  # pylint: disable=too-many-locals
        """Process all %ALLFIG directives."""
        onto = self.ontodoc.onto
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%ALLFIG "):
                tokens = shlex.split(line)
                token = tokens[1]
                opts = get_options(
                    tokens[2:],
                    path="",
                    level=3,
                    terminated=0,
                    include_leafs=1,
                    strict_leafs=1,
                    width=0,
                    leafs="",
                    relations="isA",
                    edgelabels=0,
                    rankdir="BT",
                    legend=1,
                    namespaces="",
                    ontologies="",
                )
                if token == "classes":  # nosec
                    roots = onto.get_root_classes(imported=self.imported)
                elif token in ("object_properties", "relations"):
                    roots = onto.get_root_object_properties(
                        imported=self.imported
                    )
                elif token == "data_properties":  # nosec
                    roots = onto.get_root_data_properties(
                        imported=self.imported
                    )
                else:
                    raise InvalidTemplateError(
                        f"Invalid argument to %%ALLFIG: {token}"
                    )

                included_namespaces = (
                    opts.namespaces.split(",")
                    if opts.namespaces
                    else ()  # pylint: disable=no-member
                )
                included_ontologies = (
                    opts.ontologies.split(",")
                    if opts.ontologies
                    else ()  # pylint: disable=no-member
                )

                sec = []
                for root in roots:
                    name = asstring(root)
                    filepath, _, width = self._make_branchfig(
                        name,
                        opts.path,  # pylint: disable=no-member
                        opts.terminated,  # pylint: disable=no-member
                        opts.include_leafs,  # pylint: disable=no-member
                        opts.strict_leafs,  # pylint: disable=no-member
                        opts.width,  # pylint: disable=no-member
                        opts.leafs,  # pylint: disable=no-member
                        opts.relations,  # pylint: disable=no-member
                        opts.edgelabels,  # pylint: disable=no-member
                        opts.rankdir,  # pylint: disable=no-member
                        opts.legend,  # pylint: disable=no-member
                        included_namespaces,
                        included_ontologies,
                    )
                    title = f"Taxonomy of {name}."
                    sec.append(
                        self.ontodoc.get_header(title, int(opts.level))
                    )  # pylint: disable=no-member
                    sec.extend(
                        self.ontodoc.get_figure(
                            filepath, caption=title, width=width
                        ).split("\n")
                    )

                del self.lines[i]
                self.lines[i:i] = sec

    def process_includes(self):
        """Process all %INCLUDE directives."""
        for i, line in reversed(list(enumerate(self.lines))):
            if line.startswith("%INCLUDE "):
                tokens = shlex.split(line)
                filepath = tokens[1]
                opts = get_options(tokens[2:], shift=0)
                with open(
                    os.path.join(self.basedir, filepath), "rt", encoding="utf8"
                ) as handle:
                    docpp = DocPP(
                        handle.read(),
                        self.ontodoc,
                        basedir=os.path.dirname(filepath),
                        figformat=self.figformat,
                        figscale=self.figscale,
                        maxwidth=self.maxwidth,
                    )
                    docpp.figdir = self.figdir
                if opts.shift:  # pylint: disable=no-member
                    docpp.shift_header_levels(
                        int(opts.shift)
                    )  # pylint: disable=no-member
                docpp.process()
                del self.lines[i]
                self.lines[i:i] = docpp.lines

    def process(self):
        """Perform all pre-processing steps."""
        if not self._processed:
            self.process_comments()
            self.process_headers()
            self.process_figures()
            self.process_entities()
            self.process_branches()
            self.process_branchfigs()
            self.process_branchdocs()
            self.process_alls()
            self.process_allfig()
            self.process_includes()
            self._processed = True

    def write(  # pylint: disable=too-many-arguments
        self,
        outfile,
        fmt=None,
        pandoc_option_files=(),
        pandoc_options=(),
        genfile=None,
        verbose=True,
    ):
        """Writes documentation to `outfile`.

        Parameters
        ----------
        outfile : str
            File that the documentation is written to.
        fmt : str
            Output format.  If it is "md" or "simple-html",
            the built-in template generator is used.  Otherwise
            pandoc is used.  If not given, the format is inferred
            from the `outfile` name extension.
        pandoc_option_files : sequence
            Sequence with command line arguments provided to pandoc.
        pandoc_options : sequence
            Additional pandoc options overriding options read from
        `pandoc_option_files`.
        genfile : str
            Store temporary generated markdown input file to pandoc
            to this file (for debugging).
        verbose : bool
            Whether to show some messages when running pandoc.
        """
        self.process()
        content = self.get_buffer()

        substitutions = self.ontodoc.style.get("substitutions", [])
        for reg, sub in substitutions:
            content = re.sub(reg, sub, content)

        fmt = get_format(outfile, fmt)
        if fmt not in ("simple-html", "markdown", "md"):  # Run pandoc
            if not genfile:
                with NamedTemporaryFile(mode="w+t", suffix=".md") as temp_file:
                    temp_file.write(content)
                    temp_file.flush()
                    genfile = temp_file.name

                    run_pandoc(
                        genfile,
                        outfile,
                        fmt,
                        pandoc_option_files=pandoc_option_files,
                        pandoc_options=pandoc_options,
                        verbose=verbose,
                    )
            else:
                with open(genfile, "wt") as handle:
                    handle.write(content)

                run_pandoc(
                    genfile,
                    outfile,
                    fmt,
                    pandoc_option_files=pandoc_option_files,
                    pandoc_options=pandoc_options,
                    verbose=verbose,
                )
        else:
            if verbose:
                print("Writing:", outfile)
            with open(outfile, "wt") as handle:
                handle.write(content)

copy()

Returns a copy of self.

Source code in ontopy/ontodoc.py
591
592
593
594
595
596
597
598
599
600
601
602
603
def copy(self):
    """Returns a copy of self."""
    docpp = DocPP(
        "",
        self.ontodoc,
        self.basedir,
        figformat=self.figformat,
        figscale=self.figscale,
        maxwidth=self.maxwidth,
    )
    docpp.lines[:] = self.lines
    docpp.figdir = self.figdir
    return docpp

get_branches()

Returns a list with all branch names as specified with %BRANCH (in current and all included documents). The returned value is cached for efficiency purposes and so that it is not lost after processing branches.

Source code in ontopy/ontodoc.py
605
606
607
608
609
610
611
612
613
614
615
616
617
618
def get_branches(self):
    """Returns a list with all branch names as specified with %BRANCH
    (in current and all included documents).  The returned value is
    cached for efficiency purposes and so that it is not lost after
    processing branches."""
    if self._branch_cache is None:
        names = []
        docpp = self.copy()
        docpp.process_includes()
        for line in docpp.lines:
            if line.startswith("%BRANCH"):
                names.append(shlex.split(line)[1])
        self._branch_cache = names
    return self._branch_cache

get_buffer()

Returns the current buffer.

Source code in ontopy/ontodoc.py
587
588
589
def get_buffer(self):
    """Returns the current buffer."""
    return "\n".join(self.lines)

process()

Perform all pre-processing steps.

Source code in ontopy/ontodoc.py
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
def process(self):
    """Perform all pre-processing steps."""
    if not self._processed:
        self.process_comments()
        self.process_headers()
        self.process_figures()
        self.process_entities()
        self.process_branches()
        self.process_branchfigs()
        self.process_branchdocs()
        self.process_alls()
        self.process_allfig()
        self.process_includes()
        self._processed = True

process_allfig()

Process all %ALLFIG directives.

Source code in ontopy/ontodoc.py
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
def process_allfig(self):  # pylint: disable=too-many-locals
    """Process all %ALLFIG directives."""
    onto = self.ontodoc.onto
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%ALLFIG "):
            tokens = shlex.split(line)
            token = tokens[1]
            opts = get_options(
                tokens[2:],
                path="",
                level=3,
                terminated=0,
                include_leafs=1,
                strict_leafs=1,
                width=0,
                leafs="",
                relations="isA",
                edgelabels=0,
                rankdir="BT",
                legend=1,
                namespaces="",
                ontologies="",
            )
            if token == "classes":  # nosec
                roots = onto.get_root_classes(imported=self.imported)
            elif token in ("object_properties", "relations"):
                roots = onto.get_root_object_properties(
                    imported=self.imported
                )
            elif token == "data_properties":  # nosec
                roots = onto.get_root_data_properties(
                    imported=self.imported
                )
            else:
                raise InvalidTemplateError(
                    f"Invalid argument to %%ALLFIG: {token}"
                )

            included_namespaces = (
                opts.namespaces.split(",")
                if opts.namespaces
                else ()  # pylint: disable=no-member
            )
            included_ontologies = (
                opts.ontologies.split(",")
                if opts.ontologies
                else ()  # pylint: disable=no-member
            )

            sec = []
            for root in roots:
                name = asstring(root)
                filepath, _, width = self._make_branchfig(
                    name,
                    opts.path,  # pylint: disable=no-member
                    opts.terminated,  # pylint: disable=no-member
                    opts.include_leafs,  # pylint: disable=no-member
                    opts.strict_leafs,  # pylint: disable=no-member
                    opts.width,  # pylint: disable=no-member
                    opts.leafs,  # pylint: disable=no-member
                    opts.relations,  # pylint: disable=no-member
                    opts.edgelabels,  # pylint: disable=no-member
                    opts.rankdir,  # pylint: disable=no-member
                    opts.legend,  # pylint: disable=no-member
                    included_namespaces,
                    included_ontologies,
                )
                title = f"Taxonomy of {name}."
                sec.append(
                    self.ontodoc.get_header(title, int(opts.level))
                )  # pylint: disable=no-member
                sec.extend(
                    self.ontodoc.get_figure(
                        filepath, caption=title, width=width
                    ).split("\n")
                )

            del self.lines[i]
            self.lines[i:i] = sec

process_alls()

Expand all %ALL specifications.

Source code in ontopy/ontodoc.py
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
def process_alls(self):
    """Expand all %ALL specifications."""
    onto = self.ontodoc.onto
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%ALL "):
            tokens = shlex.split(line)
            token = tokens[1]
            opts = get_options(tokens[2:], header_level=3)
            if token == "classes":  # nosec
                items = onto.classes(imported=self.imported)
            elif token in ("object_properties", "relations"):
                items = onto.object_properties(imported=self.imported)
            elif token == "data_properties":  # nosec
                items = onto.data_properties(imported=self.imported)
            elif token == "annotation_properties":  # nosec
                items = onto.annotation_properties(imported=self.imported)
            elif token == "individuals":  # nosec
                items = onto.individuals(imported=self.imported)
            else:
                raise InvalidTemplateError(
                    f"Invalid argument to %%ALL: {token}"
                )
            items = sorted(items, key=asstring)
            del self.lines[i]
            self.lines[i:i] = self.ontodoc.itemsdoc(
                items, int(opts.header_level)  # pylint: disable=no-member
            ).split("\n")

process_branchdocs()

Process all %BRANCHDOC and %BRANCHEAD directives.

Source code in ontopy/ontodoc.py
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
def process_branchdocs(self):  # pylint: disable=too-many-locals
    """Process all %BRANCHDOC and  %BRANCHEAD directives."""
    onto = self.ontodoc.onto
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%BRANCHDOC ") or line.startswith(
            "%BRANCHHEAD "
        ):
            with_branch = bool(line.startswith("%BRANCHDOC "))
            tokens = shlex.split(line)
            name = tokens[1]
            title = camelsplit(name)
            title = title[0].upper() + title[1:] + " branch"
            opts = get_options(
                tokens[2:],
                level=2,
                path="",
                title=title,
                caption=title + ".",
                terminated=1,
                strict_leafs=1,
                width=0,
                leafs="",
                relations="all",
                edgelabels=0,
                rankdir="BT",
                legend=1,
                namespaces="",
                ontologies="",
            )

            included_namespaces = (
                opts.namespaces.split(",")
                if opts.namespaces
                else ()  # pylint: disable=no-member
            )
            included_ontologies = (
                opts.ontologies.split(",")
                if opts.ontologies
                else ()  # pylint: disable=no-member
            )

            include_leafs = 1
            filepath, leafs, width = self._make_branchfig(
                name,
                opts.path,  # pylint: disable=no-member
                opts.terminated,  # pylint: disable=no-member
                include_leafs,
                opts.strict_leafs,  # pylint: disable=no-member
                opts.width,  # pylint: disable=no-member
                opts.leafs,  # pylint: disable=no-member
                opts.relations,  # pylint: disable=no-member
                opts.edgelabels,  # pylint: disable=no-member
                opts.rankdir,  # pylint: disable=no-member
                opts.legend,  # pylint: disable=no-member
                included_namespaces,
                included_ontologies,
            )

            sec = []
            sec.append(
                self.ontodoc.get_header(opts.title, int(opts.level))
            )  # pylint: disable=no-member
            sec.append(
                self.ontodoc.get_figure(
                    filepath,
                    caption=opts.caption,
                    width=width,  # pylint: disable=no-member
                )
            )
            if with_branch:
                include_leafs = 0
                branch = filter_classes(
                    onto.get_branch(name, leafs, include_leafs),
                    included_namespaces=included_namespaces,
                    included_ontologies=included_ontologies,
                )
                sec.append(
                    self.ontodoc.itemsdoc(
                        branch, int(opts.level + 1)
                    )  # pylint: disable=no-member
                )

            del self.lines[i]
            self.lines[i:i] = sec

process_branches()

Expand all %BRANCH specifications.

Source code in ontopy/ontodoc.py
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
def process_branches(self):
    """Expand all %BRANCH specifications."""
    onto = self.ontodoc.onto

    # Get all branch names in final document
    names = self.get_branches()
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%BRANCH "):
            tokens = shlex.split(line)
            name = tokens[1]
            opts = get_options(
                tokens[2:],
                header_level=3,
                terminated=1,
                include_leafs=0,
                namespaces="",
                ontologies="",
            )
            leafs = (
                names if opts.terminated else ()
            )  # pylint: disable=no-member

            included_namespaces = (
                opts.namespaces.split(",")
                if opts.namespaces
                else ()  # pylint: disable=no-member
            )
            included_ontologies = (
                opts.ontologies.split(",")
                if opts.ontologies
                else ()  # pylint: disable=no-member
            )

            branch = filter_classes(
                onto.get_branch(
                    name, leafs, opts.include_leafs
                ),  # pylint: disable=no-member
                included_namespaces=included_namespaces,
                included_ontologies=included_ontologies,
            )

            del self.lines[i]
            self.lines[i:i] = self.ontodoc.itemsdoc(
                branch, int(opts.header_level)  # pylint: disable=no-member
            ).split("\n")

process_branchfigs()

Process all %BRANCHFIG directives.

Source code in ontopy/ontodoc.py
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
def process_branchfigs(self):
    """Process all %BRANCHFIG directives."""
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%BRANCHFIG "):
            tokens = shlex.split(line)
            name = tokens[1]
            opts = get_options(
                tokens[2:],
                path="",
                caption="",
                terminated=1,
                include_leafs=1,
                strict_leafs=1,
                width=0,
                leafs="",
                relations="all",
                edgelabels=0,
                rankdir="BT",
                legend=1,
                namespaces="",
                ontologies="",
            )

            included_namespaces = (
                opts.namespaces.split(",")
                if opts.namespaces
                else ()  # pylint: disable=no-member
            )
            included_ontologies = (
                opts.ontologies.split(",")
                if opts.ontologies
                else ()  # pylint: disable=no-member
            )

            filepath, _, width = self._make_branchfig(
                name,
                opts.path,  # pylint: disable=no-member
                opts.terminated,  # pylint: disable=no-member
                opts.include_leafs,  # pylint: disable=no-member
                opts.strict_leafs,  # pylint: disable=no-member
                opts.width,  # pylint: disable=no-member
                opts.leafs,  # pylint: disable=no-member
                opts.relations,  # pylint: disable=no-member
                opts.edgelabels,  # pylint: disable=no-member
                opts.rankdir,  # pylint: disable=no-member
                opts.legend,  # pylint: disable=no-member
                included_namespaces,
                included_ontologies,
            )

            del self.lines[i]
            self.lines[i:i] = self.ontodoc.get_figure(
                filepath,
                caption=opts.caption,
                width=width,  # pylint: disable=no-member
            ).split("\n")

process_comments()

Strips out comment lines starting with "%%".

Source code in ontopy/ontodoc.py
638
639
640
def process_comments(self):
    """Strips out comment lines starting with "%%"."""
    self.lines = [line for line in self.lines if not line.startswith("%%")]

process_entities()

Expand all %ENTITY specifications.

Source code in ontopy/ontodoc.py
668
669
670
671
672
673
674
675
676
677
678
def process_entities(self):
    """Expand all %ENTITY specifications."""
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%ENTITY "):
            tokens = shlex.split(line)
            name = tokens[1]
            opts = get_options(tokens[2:], header_level=3)
            del self.lines[i]
            self.lines[i:i] = self.ontodoc.itemdoc(
                name, int(opts.header_level)  # pylint: disable=no-member
            ).split("\n")

process_figures()

Expand all %FIGURE specifications.

Source code in ontopy/ontodoc.py
654
655
656
657
658
659
660
661
662
663
664
665
666
def process_figures(self):
    """Expand all %FIGURE specifications."""
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%FIGURE "):
            tokens = shlex.split(line)
            path = tokens[1]
            opts = get_options(tokens[2:], caption="", width=0)
            del self.lines[i]
            self.lines[i:i] = self.ontodoc.get_figure(
                os.path.join(self.basedir, path),
                caption=opts.caption,  # pylint: disable=no-member
                width=opts.width,  # pylint: disable=no-member
            ).split("\n")

process_headers()

Expand all %HEADER specifications.

Source code in ontopy/ontodoc.py
642
643
644
645
646
647
648
649
650
651
652
def process_headers(self):
    """Expand all %HEADER specifications."""
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%HEADER "):
            tokens = shlex.split(line)
            name = tokens[1]
            opts = get_options(tokens[2:], level=1)
            del self.lines[i]
            self.lines[i:i] = self.ontodoc.get_header(
                name, int(opts.level)  # pylint: disable=no-member
            ).split("\n")

process_includes()

Process all %INCLUDE directives.

Source code in ontopy/ontodoc.py
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
def process_includes(self):
    """Process all %INCLUDE directives."""
    for i, line in reversed(list(enumerate(self.lines))):
        if line.startswith("%INCLUDE "):
            tokens = shlex.split(line)
            filepath = tokens[1]
            opts = get_options(tokens[2:], shift=0)
            with open(
                os.path.join(self.basedir, filepath), "rt", encoding="utf8"
            ) as handle:
                docpp = DocPP(
                    handle.read(),
                    self.ontodoc,
                    basedir=os.path.dirname(filepath),
                    figformat=self.figformat,
                    figscale=self.figscale,
                    maxwidth=self.maxwidth,
                )
                docpp.figdir = self.figdir
            if opts.shift:  # pylint: disable=no-member
                docpp.shift_header_levels(
                    int(opts.shift)
                )  # pylint: disable=no-member
            docpp.process()
            del self.lines[i]
            self.lines[i:i] = docpp.lines

shift_header_levels(shift)

Shift header level of all hashtag-headers in buffer. Underline headers are ignored.

Source code in ontopy/ontodoc.py
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
def shift_header_levels(self, shift):
    """Shift header level of all hashtag-headers in buffer.  Underline
    headers are ignored."""
    if not shift:
        return
    pat = re.compile("^#+ ")
    for i, line in enumerate(self.lines):
        match = pat.match(line)
        if match:
            if shift > 0:
                self.lines[i] = "#" * shift + line
            elif shift < 0:
                counter = match.end()
                if shift > counter:
                    self.lines[i] = line.lstrip("# ")
                else:
                    self.lines[i] = line[counter:]

write(outfile, fmt=None, pandoc_option_files=(), pandoc_options=(), genfile=None, verbose=True)

Writes documentation to outfile.

Parameters
str

File that the documentation is written to.

str

Output format. If it is "md" or "simple-html", the built-in template generator is used. Otherwise pandoc is used. If not given, the format is inferred from the outfile name extension.

sequence

Sequence with command line arguments provided to pandoc.

sequence

Additional pandoc options overriding options read from

pandoc_option_files.

str

Store temporary generated markdown input file to pandoc to this file (for debugging).

bool

Whether to show some messages when running pandoc.

Source code in ontopy/ontodoc.py
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
def write(  # pylint: disable=too-many-arguments
    self,
    outfile,
    fmt=None,
    pandoc_option_files=(),
    pandoc_options=(),
    genfile=None,
    verbose=True,
):
    """Writes documentation to `outfile`.

    Parameters
    ----------
    outfile : str
        File that the documentation is written to.
    fmt : str
        Output format.  If it is "md" or "simple-html",
        the built-in template generator is used.  Otherwise
        pandoc is used.  If not given, the format is inferred
        from the `outfile` name extension.
    pandoc_option_files : sequence
        Sequence with command line arguments provided to pandoc.
    pandoc_options : sequence
        Additional pandoc options overriding options read from
    `pandoc_option_files`.
    genfile : str
        Store temporary generated markdown input file to pandoc
        to this file (for debugging).
    verbose : bool
        Whether to show some messages when running pandoc.
    """
    self.process()
    content = self.get_buffer()

    substitutions = self.ontodoc.style.get("substitutions", [])
    for reg, sub in substitutions:
        content = re.sub(reg, sub, content)

    fmt = get_format(outfile, fmt)
    if fmt not in ("simple-html", "markdown", "md"):  # Run pandoc
        if not genfile:
            with NamedTemporaryFile(mode="w+t", suffix=".md") as temp_file:
                temp_file.write(content)
                temp_file.flush()
                genfile = temp_file.name

                run_pandoc(
                    genfile,
                    outfile,
                    fmt,
                    pandoc_option_files=pandoc_option_files,
                    pandoc_options=pandoc_options,
                    verbose=verbose,
                )
        else:
            with open(genfile, "wt") as handle:
                handle.write(content)

            run_pandoc(
                genfile,
                outfile,
                fmt,
                pandoc_option_files=pandoc_option_files,
                pandoc_options=pandoc_options,
                verbose=verbose,
            )
    else:
        if verbose:
            print("Writing:", outfile)
        with open(outfile, "wt") as handle:
            handle.write(content)

InvalidTemplateError

Bases: NameError

Raised on errors in template files.

Source code in ontopy/ontodoc.py
434
435
class InvalidTemplateError(NameError):
    """Raised on errors in template files."""

OntoDoc

A class for helping documentating ontologies.

Parameters

Ontology instance

The ontology that should be documented.

dict | "html" | "markdown" | "markdown_tex"

A dict defining the following template strings (and substitutions):

:header: Formats an header. Substitutions: {level}, {label} :link: Formats a link. Substitutions: {name} :point: Formats a point (list item). Substitutions: {point}, {ontology} :points: Formats a list of points. Used within annotations. Substitutions: {points}, {ontology} :annotation: Formats an annotation. Substitutions: {key}, {value}, {ontology} :substitutions: list of (regex, sub) pairs for substituting annotation values.

Source code in ontopy/ontodoc.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
class OntoDoc:
    """A class for helping documentating ontologies.

    Parameters
    ----------
    onto : Ontology instance
        The ontology that should be documented.
    style : dict | "html" | "markdown" | "markdown_tex"
        A dict defining the following template strings (and substitutions):

        :header: Formats an header.
            Substitutions: {level}, {label}
        :link: Formats a link.
           Substitutions: {name}
        :point: Formats a point (list item).
           Substitutions: {point}, {ontology}
        :points: Formats a list of points.  Used within annotations.
           Substitutions: {points}, {ontology}
        :annotation: Formats an annotation.
            Substitutions: {key}, {value}, {ontology}
        :substitutions: list of ``(regex, sub)`` pairs for substituting
            annotation values.
    """

    _markdown_style = dict(
        sep="\n",
        figwidth="{{ width={width:.0f}px }}",
        figure="![{caption}]({path}){figwidth}\n",
        header="\n{:#<{level}} {label}",
        link="[{name}]({lowerurl})",
        point="  - {point}\n",
        points="\n\n{points}\n",
        annotation="**{key}:** {value}\n",
        substitutions=[],
    )
    # Extra style settings for markdown+tex (e.g. pdf generation with pandoc)
    _markdown_tex_extra_style = dict(
        substitutions=[
            # logic/math symbols
            ("\u2200", r"$\\forall$"),
            ("\u2203", r"$\\exists$"),
            ("\u2206", r"$\\nabla$"),
            ("\u2227", r"$\\land$"),
            ("\u2228", r"$\\lor$"),
            ("\u2207", r"$\\nabla$"),
            ("\u2212", r"-"),
            ("->", r"$\\rightarrow$"),
            # uppercase greek letters
            ("\u0391", r"$\\Upalpha$"),
            ("\u0392", r"$\\Upbeta$"),
            ("\u0393", r"$\\Upgamma$"),
            ("\u0394", r"$\\Updelta$"),
            ("\u0395", r"$\\Upepsilon$"),
            ("\u0396", r"$\\Upzeta$"),
            ("\u0397", r"$\\Upeta$"),
            ("\u0398", r"$\\Uptheta$"),
            ("\u0399", r"$\\Upiota$"),
            ("\u039a", r"$\\Upkappa$"),
            ("\u039b", r"$\\Uplambda$"),
            ("\u039c", r"$\\Upmu$"),
            ("\u039d", r"$\\Upnu$"),
            ("\u039e", r"$\\Upxi$"),
            ("\u039f", r"$\\Upomekron$"),
            ("\u03a0", r"$\\Uppi$"),
            ("\u03a1", r"$\\Uprho$"),
            ("\u03a3", r"$\\Upsigma$"),  # no \u0302
            ("\u03a4", r"$\\Uptau$"),
            ("\u03a5", r"$\\Upupsilon$"),
            ("\u03a6", r"$\\Upvarphi$"),
            ("\u03a7", r"$\\Upchi$"),
            ("\u03a8", r"$\\Uppsi$"),
            ("\u03a9", r"$\\Upomega$"),
            # lowercase greek letters
            ("\u03b1", r"$\\upalpha$"),
            ("\u03b2", r"$\\upbeta$"),
            ("\u03b3", r"$\\upgamma$"),
            ("\u03b4", r"$\\updelta$"),
            ("\u03b5", r"$\\upepsilon$"),
            ("\u03b6", r"$\\upzeta$"),
            ("\u03b7", r"$\\upeta$"),
            ("\u03b8", r"$\\uptheta$"),
            ("\u03b9", r"$\\upiota$"),
            ("\u03ba", r"$\\upkappa$"),
            ("\u03bb", r"$\\uplambda$"),
            ("\u03bc", r"$\\upmu$"),
            ("\u03bd", r"$\\upnu$"),
            ("\u03be", r"$\\upxi$"),
            ("\u03bf", r"o"),  # no \upomicron
            ("\u03c0", r"$\\uppi$"),
            ("\u03c1", r"$\\uprho$"),
            ("\u03c2", r"$\\upvarsigma$"),
            ("\u03c3", r"$\\upsigma$"),
            ("\u03c4", r"$\\uptau$"),
            ("\u03c5", r"$\\upupsilon$"),
            ("\u03c6", r"$\\upvarphi$"),
            ("\u03c7", r"$\\upchi$"),
            ("\u03c8", r"$\\uppsi$"),
            ("\u03c9", r"$\\upomega$"),
            # acutes, accents, etc...
            ("\u03ae", r"$\\acute{\\upeta}$"),
            ("\u1e17", r"$\\acute{\\bar{\\mathrm{e}}}$"),
            ("\u03ac", r"$\\acute{\\upalpha}$"),
            ("\u00e1", r"$\\acute{\\mathrm{a}}$"),
            ("\u03cc", r"$\\acute{o}$"),  # no \upomicron
            ("\u014d", r"$\\bar{\\mathrm{o}}$"),
            ("\u1f45", r"$\\acute{o}$"),  # no \omicron
        ],
    )
    _html_style = dict(
        sep="<p>\n",
        figwidth='width="{width:.0f}"',
        figure='<img src="{path}" alt="{caption}"{figwidth}>',
        header='<h{level} id="{lowerlabel}">{label}</h{level}>',
        link='<a href="{lowerurl}">{name}</a>',
        point="      <li>{point}</li>\n",
        points="    <ul>\n      {points}\n    </ul>\n",
        annotation="  <dd><strong>{key}:</strong>\n{value}  </dd>\n",
        substitutions=[
            (r"&", r"&#8210;"),
            (r"<p>", r"<p>\n\n"),
            (r"\u2018([^\u2019]*)\u2019", r"<q>\1</q>"),
            (r"\u2019", r"'"),
            (r"\u2260", r"&ne;"),
            (r"\u2264", r"&le;"),
            (r"\u2265", r"&ge;"),
            (r"\u226A", r"&x226A;"),
            (r"\u226B", r"&x226B;"),
            (r'"Y$', r""),  # strange noice added by owlready2
        ],
    )

    def __init__(self, onto, style="markdown"):
        if isinstance(style, str):
            if style == "markdown_tex":
                style = self._markdown_style.copy()
                style.update(self._markdown_tex_extra_style)
            else:
                style = getattr(self, f"_{style}_style")
        self.onto = onto
        self.style = style
        self.url_regex = re.compile(r"https?:\/\/[^\s ]+")

    def get_default_template(self):
        """Returns default template."""
        title = os.path.splitext(
            os.path.basename(self.onto.base_iri.rstrip("/#"))
        )[0]
        irilink = self.style.get("link", "{name}").format(
            name=self.onto.base_iri,
            url=self.onto.base_iri,
            lowerurl=self.onto.base_iri,
        )
        template = dedent(
            """\
        %HEADER {title}
        Documentation of {irilink}

        %HEADER Relations level=2
        %ALL object_properties

        %HEADER Classes level=2
        %ALL classes

        %HEADER Individuals level=2
        %ALL individuals

        %HEADER Appendix               level=1
        %HEADER "Relation taxonomies"  level=2
        %ALLFIG object_properties

        %HEADER "Class taxonomies"     level=2
        %ALLFIG classes
        """
        ).format(ontology=self.onto, title=title, irilink=irilink)
        return template

    def get_header(self, label, header_level=1):
        """Returns `label` formatted as a header of given level."""
        header_style = self.style.get("header", "{label}\n")
        return header_style.format(
            "", level=header_level, label=label, lowerlabel=label.lower()
        )

    def get_figure(self, path, caption="", width=None):
        """Returns a formatted insert-figure-directive."""
        figwidth_style = self.style.get("figwidth", "")
        figure_style = self.style.get("figure", "")
        figwidth = figwidth_style.format(width=width) if width else ""
        return figure_style.format(
            path=path, caption=caption, figwidth=figwidth
        )

    def itemdoc(
        self, item, header_level=3, show_disjoints=False
    ):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
        """Returns documentation of `item`.

        Parameters
        ----------
        item : obj | label
            The class, individual or relation to document.
        header_level : int
            Header level. Defaults to 3.
        show_disjoints : Bool
            Whether to show `disjoint_with` relations.
        """
        onto = self.onto
        if isinstance(item, str):
            item = self.onto.get_by_label(item)

        header_style = self.style.get("header", "{label}\n")
        link_style = self.style.get("link", "{name}")
        point_style = self.style.get("point", "{point}")
        points_style = self.style.get("points", "{points}")
        annotation_style = self.style.get("annotation", "{key}: {value}\n")
        substitutions = self.style.get("substitutions", [])

        # Logical "sorting" of annotations
        order = dict(
            definition="00",
            axiom="01",
            theorem="02",
            elucidation="03",
            domain="04",
            range="05",
            example="06",
        )

        doc = []

        # Header
        label = get_label(item)
        doc.append(
            header_style.format(
                "", level=header_level, label=label, lowerlabel=label.lower()
            )
        )

        # Add warning about missing prefLabel
        if not hasattr(item, "prefLabel") or not item.prefLabel.first():
            doc.append(
                annotation_style.format(
                    key="Warning", value="Missing prefLabel"
                )
            )

        # Add iri
        doc.append(
            annotation_style.format(
                key="IRI", value=asstring(item.iri, link_style), ontology=onto
            )
        )

        # Add annotations
        if isinstance(item, owlready2.Thing):
            annotations = item.get_individual_annotations()
        else:
            annotations = item.get_annotations()

        for key in sorted(
            annotations.keys(), key=lambda key: order.get(key, key)
        ):
            for value in annotations[key]:
                if self.url_regex.match(value):
                    doc.append(
                        annotation_style.format(
                            key=key, value=asstring(value, link_style)
                        )
                    )
                else:
                    for reg, sub in substitutions:
                        value = re.sub(reg, sub, value)
                    doc.append(annotation_style.format(key=key, value=value))

        # ...add relations from is_a
        points = []
        non_prop = (
            owlready2.ThingClass,  # owlready2.Restriction,
            owlready2.And,
            owlready2.Or,
            owlready2.Not,
        )
        for prop in item.is_a:
            if isinstance(prop, non_prop) or (
                isinstance(item, owlready2.PropertyClass)
                and isinstance(prop, owlready2.PropertyClass)
            ):
                points.append(
                    point_style.format(
                        point="is_a " + asstring(prop, link_style),
                        ontology=onto,
                    )
                )
            else:
                points.append(
                    point_style.format(
                        point=asstring(prop, link_style), ontology=onto
                    )
                )

        # ...add equivalent_to relations
        for entity in item.equivalent_to:
            points.append(
                point_style.format(
                    point="equivalent_to " + asstring(entity, link_style)
                )
            )

        # ...add disjoint_with relations
        if show_disjoints and hasattr(item, "disjoint_with"):
            subjects = set(item.disjoint_with(reduce=True))
            points.append(
                point_style.format(
                    point="disjoint_with "
                    + ", ".join(asstring(_, link_style) for _ in subjects),
                    ontology=onto,
                )
            )

        # ...add disjoint_unions
        if hasattr(item, "disjoint_unions"):
            for unions in item.disjoint_unions:
                string = ", ".join(asstring(_, link_style) for _ in unions)
                points.append(
                    point_style.format(
                        point=f"disjoint_union_of {string}", ontology=onto
                    )
                )

        # ...add inverse_of relations
        if hasattr(item, "inverse_property") and item.inverse_property:
            points.append(
                point_style.format(
                    point="inverse_of "
                    + asstring(item.inverse_property, link_style)
                )
            )

        # ...add domain restrictions
        for domain in getattr(item, "domain", ()):
            points.append(
                point_style.format(
                    point=f"domain {asstring(domain, link_style)}"
                )
            )

        # ...add range restrictions
        for restriction in getattr(item, "range", ()):
            points.append(
                point_style.format(
                    point=f"range {asstring(restriction, link_style)}"
                )
            )

        # Add points (from is_a)
        if points:
            value = points_style.format(points="".join(points), ontology=onto)
            doc.append(
                annotation_style.format(
                    key="Subclass of", value=value, ontology=onto
                )
            )

        # Instances (individuals)
        if hasattr(item, "instances"):
            points = []
            for entity in [
                _ for _ in item.instances() if item in _.is_instance_of
            ]:
                points.append(
                    point_style.format(
                        point=asstring(entity, link_style), ontology=onto
                    )
                )
            if points:
                value = points_style.format(
                    points="".join(points), ontology=onto
                )
                doc.append(
                    annotation_style.format(
                        key="Individuals", value=value, ontology=onto
                    )
                )

        return "\n".join(doc)

    def itemsdoc(self, items, header_level=3):
        """Returns documentation of `items`."""
        sep_style = self.style.get("sep", "\n")
        doc = []
        for item in items:
            doc.append(self.itemdoc(item, header_level))
            doc.append(sep_style.format(ontology=self.onto))
        return "\n".join(doc)

get_default_template()

Returns default template.

Source code in ontopy/ontodoc.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
def get_default_template(self):
    """Returns default template."""
    title = os.path.splitext(
        os.path.basename(self.onto.base_iri.rstrip("/#"))
    )[0]
    irilink = self.style.get("link", "{name}").format(
        name=self.onto.base_iri,
        url=self.onto.base_iri,
        lowerurl=self.onto.base_iri,
    )
    template = dedent(
        """\
    %HEADER {title}
    Documentation of {irilink}

    %HEADER Relations level=2
    %ALL object_properties

    %HEADER Classes level=2
    %ALL classes

    %HEADER Individuals level=2
    %ALL individuals

    %HEADER Appendix               level=1
    %HEADER "Relation taxonomies"  level=2
    %ALLFIG object_properties

    %HEADER "Class taxonomies"     level=2
    %ALLFIG classes
    """
    ).format(ontology=self.onto, title=title, irilink=irilink)
    return template

get_figure(path, caption='', width=None)

Returns a formatted insert-figure-directive.

Source code in ontopy/ontodoc.py
211
212
213
214
215
216
217
218
def get_figure(self, path, caption="", width=None):
    """Returns a formatted insert-figure-directive."""
    figwidth_style = self.style.get("figwidth", "")
    figure_style = self.style.get("figure", "")
    figwidth = figwidth_style.format(width=width) if width else ""
    return figure_style.format(
        path=path, caption=caption, figwidth=figwidth
    )

get_header(label, header_level=1)

Returns label formatted as a header of given level.

Source code in ontopy/ontodoc.py
204
205
206
207
208
209
def get_header(self, label, header_level=1):
    """Returns `label` formatted as a header of given level."""
    header_style = self.style.get("header", "{label}\n")
    return header_style.format(
        "", level=header_level, label=label, lowerlabel=label.lower()
    )

itemdoc(item, header_level=3, show_disjoints=False)

Returns documentation of item.

Parameters
obj | label

The class, individual or relation to document.

int

Header level. Defaults to 3.

Bool

Whether to show disjoint_with relations.

Source code in ontopy/ontodoc.py
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
def itemdoc(
    self, item, header_level=3, show_disjoints=False
):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
    """Returns documentation of `item`.

    Parameters
    ----------
    item : obj | label
        The class, individual or relation to document.
    header_level : int
        Header level. Defaults to 3.
    show_disjoints : Bool
        Whether to show `disjoint_with` relations.
    """
    onto = self.onto
    if isinstance(item, str):
        item = self.onto.get_by_label(item)

    header_style = self.style.get("header", "{label}\n")
    link_style = self.style.get("link", "{name}")
    point_style = self.style.get("point", "{point}")
    points_style = self.style.get("points", "{points}")
    annotation_style = self.style.get("annotation", "{key}: {value}\n")
    substitutions = self.style.get("substitutions", [])

    # Logical "sorting" of annotations
    order = dict(
        definition="00",
        axiom="01",
        theorem="02",
        elucidation="03",
        domain="04",
        range="05",
        example="06",
    )

    doc = []

    # Header
    label = get_label(item)
    doc.append(
        header_style.format(
            "", level=header_level, label=label, lowerlabel=label.lower()
        )
    )

    # Add warning about missing prefLabel
    if not hasattr(item, "prefLabel") or not item.prefLabel.first():
        doc.append(
            annotation_style.format(
                key="Warning", value="Missing prefLabel"
            )
        )

    # Add iri
    doc.append(
        annotation_style.format(
            key="IRI", value=asstring(item.iri, link_style), ontology=onto
        )
    )

    # Add annotations
    if isinstance(item, owlready2.Thing):
        annotations = item.get_individual_annotations()
    else:
        annotations = item.get_annotations()

    for key in sorted(
        annotations.keys(), key=lambda key: order.get(key, key)
    ):
        for value in annotations[key]:
            if self.url_regex.match(value):
                doc.append(
                    annotation_style.format(
                        key=key, value=asstring(value, link_style)
                    )
                )
            else:
                for reg, sub in substitutions:
                    value = re.sub(reg, sub, value)
                doc.append(annotation_style.format(key=key, value=value))

    # ...add relations from is_a
    points = []
    non_prop = (
        owlready2.ThingClass,  # owlready2.Restriction,
        owlready2.And,
        owlready2.Or,
        owlready2.Not,
    )
    for prop in item.is_a:
        if isinstance(prop, non_prop) or (
            isinstance(item, owlready2.PropertyClass)
            and isinstance(prop, owlready2.PropertyClass)
        ):
            points.append(
                point_style.format(
                    point="is_a " + asstring(prop, link_style),
                    ontology=onto,
                )
            )
        else:
            points.append(
                point_style.format(
                    point=asstring(prop, link_style), ontology=onto
                )
            )

    # ...add equivalent_to relations
    for entity in item.equivalent_to:
        points.append(
            point_style.format(
                point="equivalent_to " + asstring(entity, link_style)
            )
        )

    # ...add disjoint_with relations
    if show_disjoints and hasattr(item, "disjoint_with"):
        subjects = set(item.disjoint_with(reduce=True))
        points.append(
            point_style.format(
                point="disjoint_with "
                + ", ".join(asstring(_, link_style) for _ in subjects),
                ontology=onto,
            )
        )

    # ...add disjoint_unions
    if hasattr(item, "disjoint_unions"):
        for unions in item.disjoint_unions:
            string = ", ".join(asstring(_, link_style) for _ in unions)
            points.append(
                point_style.format(
                    point=f"disjoint_union_of {string}", ontology=onto
                )
            )

    # ...add inverse_of relations
    if hasattr(item, "inverse_property") and item.inverse_property:
        points.append(
            point_style.format(
                point="inverse_of "
                + asstring(item.inverse_property, link_style)
            )
        )

    # ...add domain restrictions
    for domain in getattr(item, "domain", ()):
        points.append(
            point_style.format(
                point=f"domain {asstring(domain, link_style)}"
            )
        )

    # ...add range restrictions
    for restriction in getattr(item, "range", ()):
        points.append(
            point_style.format(
                point=f"range {asstring(restriction, link_style)}"
            )
        )

    # Add points (from is_a)
    if points:
        value = points_style.format(points="".join(points), ontology=onto)
        doc.append(
            annotation_style.format(
                key="Subclass of", value=value, ontology=onto
            )
        )

    # Instances (individuals)
    if hasattr(item, "instances"):
        points = []
        for entity in [
            _ for _ in item.instances() if item in _.is_instance_of
        ]:
            points.append(
                point_style.format(
                    point=asstring(entity, link_style), ontology=onto
                )
            )
        if points:
            value = points_style.format(
                points="".join(points), ontology=onto
            )
            doc.append(
                annotation_style.format(
                    key="Individuals", value=value, ontology=onto
                )
            )

    return "\n".join(doc)

itemsdoc(items, header_level=3)

Returns documentation of items.

Source code in ontopy/ontodoc.py
414
415
416
417
418
419
420
421
def itemsdoc(self, items, header_level=3):
    """Returns documentation of `items`."""
    sep_style = self.style.get("sep", "\n")
    doc = []
    for item in items:
        doc.append(self.itemdoc(item, header_level))
        doc.append(sep_style.format(ontology=self.onto))
    return "\n".join(doc)

append_pandoc_options(options, updates)

Append updates to pandoc options options.

Parameters

sequence

Sequence with initial Pandoc options.

sequence of str

Sequence of strings of the form "--longoption=value", where longoption is a valid pandoc long option and value is the new value. The "=value" part is optional.

Strings of the form "no-longoption" will filter out "--longoption" from options.

Returns

list

Updated pandoc options.

Source code in ontopy/ontodoc.py
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
def append_pandoc_options(options, updates):
    """Append `updates` to pandoc options `options`.

    Parameters
    ----------
    options : sequence
        Sequence with initial Pandoc options.
    updates : sequence of str
        Sequence of strings of the form "--longoption=value", where
        ``longoption`` is a valid pandoc long option and ``value`` is the
        new value.  The "=value" part is optional.

        Strings of the form "no-longoption" will filter out "--longoption"
        from `options`.

    Returns
    -------
    new_options : list
        Updated pandoc options.
    """
    # Valid pandoc options starting with "--no-XXX"
    no_options = set("no-highlight")

    if not updates:
        return list(options)

    curated_updates = {}
    for update in updates:
        key, sep, value = update.partition("=")
        curated_updates[key.lstrip("-")] = value if sep else None
        filter_out = set(
            _
            for _ in curated_updates
            if _.startswith("no-") and _ not in no_options
        )
        _filter_out = set(f"--{_[3:]}" for _ in filter_out)
        new_options = [
            opt for opt in options if opt.partition("=")[0] not in _filter_out
        ]
        new_options.extend(
            [
                f"--{key}" if value is None else f"--{key}={value}"
                for key, value in curated_updates.items()
                if key not in filter_out
            ]
        )
    return new_options

get_docpp(ontodoc, infile, figdir='genfigs', figformat='png', maxwidth=None, imported=False)

Read infile and return a new docpp instance.

Source code in ontopy/ontodoc.py
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
def get_docpp(  # pylint: disable=too-many-arguments
    ontodoc,
    infile,
    figdir="genfigs",
    figformat="png",
    maxwidth=None,
    imported=False,
):
    """Read `infile` and return a new docpp instance."""
    if infile:
        with open(infile, "rt") as handle:
            template = handle.read()
        basedir = os.path.dirname(infile)
    else:
        template = ontodoc.get_default_template()
        basedir = "."

    docpp = DocPP(
        template,
        ontodoc,
        basedir=basedir,
        figdir=figdir,
        figformat=figformat,
        maxwidth=maxwidth,
        imported=imported,
    )

    return docpp

get_figformat(fmt)

Infer preferred figure format from output format.

Source code in ontopy/ontodoc.py
1401
1402
1403
1404
1405
1406
1407
1408
1409
def get_figformat(fmt):
    """Infer preferred figure format from output format."""
    if fmt == "pdf":
        figformat = "pdf"  # XXX
    elif "html" in fmt:
        figformat = "svg"
    else:
        figformat = "png"
    return figformat

get_format(outfile, fmt=None)

Infer format from outfile and format.

Source code in ontopy/ontodoc.py
1379
1380
1381
1382
1383
1384
1385
1386
1387
def get_format(outfile, fmt=None):
    """Infer format from outfile and format."""
    if fmt is None:
        fmt = os.path.splitext(outfile)[1]
    if not fmt:
        fmt = "html"
    if fmt.startswith("."):
        fmt = fmt[1:]
    return fmt

get_maxwidth(fmt)

Infer preferred max figure width from output format.

Source code in ontopy/ontodoc.py
1412
1413
1414
1415
1416
1417
1418
def get_maxwidth(fmt):
    """Infer preferred max figure width from output format."""
    if fmt == "pdf":
        maxwidth = 668
    else:
        maxwidth = 1024
    return maxwidth

get_options(opts, **kwargs)

Returns a dict with options from the sequence opts with "name=value" pairs. Valid option names and default values are provided with the keyword arguments.

Source code in ontopy/ontodoc.py
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
def get_options(opts, **kwargs):
    """Returns a dict with options from the sequence `opts` with
    "name=value" pairs. Valid option names and default values are
    provided with the keyword arguments."""
    res = AttributeDict(kwargs)
    for opt in opts:
        if "=" not in opt:
            raise InvalidTemplateError(
                f'Missing "=" in template option: {opt!r}'
            )
        name, value = opt.split("=", 1)
        if name not in res:
            raise InvalidTemplateError(f"Invalid template option: {name!r}")
        res_type = type(res[name])
        res[name] = res_type(value)
    return res

get_style(fmt)

Infer style from output format.

Source code in ontopy/ontodoc.py
1390
1391
1392
1393
1394
1395
1396
1397
1398
def get_style(fmt):
    """Infer style from output format."""
    if fmt == "simple-html":
        style = "html"
    elif fmt in ("tex", "latex", "pdf"):
        style = "markdown_tex"
    else:
        style = "markdown"
    return style

load_pandoc_option_file(yamlfile)

Loads pandoc options from yamlfile and return a list with corresponding pandoc command line arguments.

Source code in ontopy/ontodoc.py
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
def load_pandoc_option_file(yamlfile):
    """Loads pandoc options from `yamlfile` and return a list with
    corresponding pandoc command line arguments."""
    with open(yamlfile) as handle:
        pandoc_options = yaml.safe_load(handle)
    options = pandoc_options.pop("input-files", [])
    variables = pandoc_options.pop("variables", {})

    for key, value in pandoc_options.items():
        if isinstance(value, bool):
            if value:
                options.append(f"--{key}")
        else:
            options.append(f"--{key}={value}")

    for key, value in variables.items():
        if key == "date" and value == "now":
            value = time.strftime("%B %d, %Y")
        options.append(f"--variable={key}:{value}")

    return options

run_pandoc(genfile, outfile, fmt, pandoc_option_files=(), pandoc_options=(), verbose=True)

Runs pandoc.

Parameters

str

Name of markdown input file.

str

Output file name.

str

Output format.

sequence

List of files with additional pandoc options. Default is to read "pandoc-options.yaml" and "pandoc-FORMAT-options.yml", where FORMAT is the output format.

sequence

Additional pandoc options overriding options read from pandoc_option_files.

bool

Whether to print the pandoc command before execution.

Raises

subprocess.CalledProcessError If the pandoc process returns with non-zero status. The returncode attribute will hold the exit code.

Source code in ontopy/ontodoc.py
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
def run_pandoc(  # pylint: disable=too-many-arguments
    genfile,
    outfile,
    fmt,
    pandoc_option_files=(),
    pandoc_options=(),
    verbose=True,
):
    """Runs pandoc.

    Parameters
    ----------
    genfile : str
        Name of markdown input file.
    outfile : str
        Output file name.
    fmt : str
        Output format.
    pandoc_option_files : sequence
        List of files with additional pandoc options.  Default is to read
        "pandoc-options.yaml" and "pandoc-FORMAT-options.yml", where
        `FORMAT` is the output format.
    pandoc_options : sequence
        Additional pandoc options overriding options read from
        `pandoc_option_files`.
    verbose : bool
        Whether to print the pandoc command before execution.

    Raises
    ------
    subprocess.CalledProcessError
        If the pandoc process returns with non-zero status.  The `returncode`
        attribute will hold the exit code.
    """
    # Create pandoc argument list
    args = [genfile]
    files = ["pandoc-options.yaml", f"pandoc-{fmt}-options.yaml"]
    if pandoc_option_files:
        files = pandoc_option_files
    for fname in files:
        if os.path.exists(fname):
            args.extend(load_pandoc_option_file(fname))
        else:
            warnings.warn(f"missing pandoc option file: {fname}")

    # Update pandoc argument list
    args = append_pandoc_options(args, pandoc_options)

    # pdf output requires a special attention...
    if fmt == "pdf":
        pdf_engine = "pdflatex"
        for arg in args:
            if arg.startswith("--pdf-engine"):
                pdf_engine = arg.split("=", 1)[1]
                break
        with TemporaryDirectory() as tmpdir:
            run_pandoc_pdf(tmpdir, pdf_engine, outfile, args, verbose=verbose)
    else:
        args.append(f"--output={outfile}")
        cmd = ["pandoc"] + args
        if verbose:
            print()
            print("* Executing command:")
            print(" ".join(shlex.quote(_) for _ in cmd))
        subprocess.check_call(cmd)  # nosec

run_pandoc_pdf(latex_dir, pdf_engine, outfile, args, verbose=True)

Run pandoc for pdf generation.

Source code in ontopy/ontodoc.py
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
def run_pandoc_pdf(latex_dir, pdf_engine, outfile, args, verbose=True):
    """Run pandoc for pdf generation."""
    basename = os.path.join(
        latex_dir, os.path.splitext(os.path.basename(outfile))[0]
    )

    # Run pandoc
    texfile = basename + ".tex"
    args.append(f"--output={texfile}")
    cmd = ["pandoc"] + args
    if verbose:
        print()
        print("* Executing commands:")
        print(" ".join(shlex.quote(s) for s in cmd))
    subprocess.check_call(cmd)  # nosec

    # Fixing tex output
    texfile2 = basename + "2.tex"
    with open(texfile, "rt") as handle:
        content = handle.read().replace(r"\$\Uptheta\$", r"$\Uptheta$")
    with open(texfile2, "wt") as handle:
        handle.write(content)

    # Run latex
    pdffile = basename + "2.pdf"
    cmd = [
        pdf_engine,
        texfile2,
        "-halt-on-error",
        f"-output-directory={latex_dir}",
    ]
    if verbose:
        print()
        print(" ".join(shlex.quote(s) for s in cmd))
    output = subprocess.check_output(cmd, timeout=60)  # nosec
    output = subprocess.check_output(cmd, timeout=60)  # nosec

    # Workaround for non-working "-output-directory" latex option
    if not os.path.exists(pdffile):
        if os.path.exists(os.path.basename(pdffile)):
            pdffile = os.path.basename(pdffile)
            for ext in "aux", "out", "toc", "log":
                filename = os.path.splitext(pdffile)[0] + "." + ext
                if os.path.exists(filename):
                    os.remove(filename)
        else:
            print()
            print(output)
            print()
            raise RuntimeError("latex did not produce pdf file: " + pdffile)

    # Copy pdffile
    if not os.path.exists(outfile) or not os.path.samefile(pdffile, outfile):
        if verbose:
            print()
            print(f"move {pdffile} to {outfile}")
        shutil.move(pdffile, outfile)