doorstop.core.publisher.publish()   C
last analyzed

Complexity

Conditions 11

Size

Total Lines 90
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 46
dl 0
loc 90
rs 5.4
c 0
b 0
f 0
cc 11
nop 9

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like doorstop.core.publisher.publish() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
# SPDX-License-Identifier: LGPL-3.0-only
2
3
"""Functions to publish documents and items."""
4
5
import os
6
7
from doorstop import common, settings
8
from doorstop.common import DoorstopError
9
from doorstop.core.publishers.html import HtmlPublisher
10
from doorstop.core.publishers.latex import LaTeXPublisher
11
from doorstop.core.publishers.markdown import MarkdownPublisher
12
from doorstop.core.publishers.text import TextPublisher
13
from doorstop.core.types import is_tree, iter_documents
14
15
log = common.logger(__name__)
16
17
18
def publish(
19
    obj,
20
    path,
21
    ext=None,
22
    linkify=None,
23
    index=None,
24
    matrix=None,
25
    template=None,
26
    toc=True,
27
    **kwargs,
28
):
29
    """Publish an object to a given format.
30
31
    The function can be called in two ways:
32
33
    1. document or item-like object + output file path
34
    2. tree-like object + output directory path
35
36
    :param obj: (1) Item, list of Items, Document or (2) Tree
37
    :param path: (1) output file path or (2) output directory path
38
    :param ext: file extension to override output extension
39
    :param linkify: turn links into hyperlinks (for Markdown, HTML or LaTeX)
40
    :param index: create an index.html (for HTML)
41
    :param matrix: create a traceability matrix, traceability.csv
42
43
    :raises: :class:`doorstop.common.DoorstopError` for unknown file formats
44
45
    :return: output location if files created, else None
46
47
    """
48
    # Check that we have something to publish first.
49
    if is_tree(obj):
50
        if len(obj) == 0:
51
            raise DoorstopError("nothing to publish")
52
53
    # Determine the output format
54
    ext = ext or os.path.splitext(path)[-1] or ".html"
55
    publisher = check(ext, obj=obj)
56
57
    # Setup publisher.
58
    publisher.setPath(path)
59
    publisher.setup(linkify, index, matrix)
60
61
    # Process templates.
62
    publisher.processTemplates(template)
63
    log.info("Template = {}".format(publisher.getTemplate()))
64
    # Run all preparations.
65
    publisher.preparePublish()
66
67
    # Publish documents
68
    count = 0
69
    for obj2, path2 in iter_documents(obj, path, ext):
70
        count += 1
71
        # Run all special actions.
72
        publisher.publishAction(obj2, path2)
73
74
        # Publish content to the specified path
75
        log.info("publishing to {}...".format(publisher.getDocumentPath()))
76
        lines = publish_lines(
77
            obj2,
78
            ext,
79
            publisher=publisher,
80
            linkify=publisher.getLinkify(),
81
            template=publisher.getTemplate(),
82
            toc=toc,
83
            **kwargs,
84
        )
85
        common.write_lines(
86
            lines, publisher.getDocumentPath(), end=settings.WRITE_LINESEPERATOR
87
        )
88
        if obj2.copy_assets(publisher.getAssetsPath()):
89
            log.info(
90
                "Copied assets from %s to %s", obj.assets, publisher.getAssetsPath()
91
            )
92
93
    # Create index
94
    if publisher.getIndex():
95
        publisher.create_index(path, tree=obj if is_tree(obj) else None)
96
97
    # Create traceability matrix
98
    if (publisher.getIndex() or ext == ".tex") and (publisher.getMatrix()):
99
        publisher.create_matrix(path)
100
101
    # Run all concluding operations.
102
    publisher.concludePublish()
103
104
    # Return the published path
105
    msg = "published to {} file{}".format(count, "s" if count > 1 else "")
106
    log.info(msg)
107
    return path
108
109
110
def publish_lines(obj, ext=".txt", publisher=None, **kwargs):
111
    """Yield lines for a report in the specified format.
112
113
    :param obj: Item, list of Items, or Document to publish
114
    :param ext: file extension to specify the output format
115
116
    """
117
    if not publisher:
118
        publisher = check(ext, obj=obj)
119
    gen = publisher.get_line_generator()
120
    log.debug("yielding {} as lines of {}...".format(obj, ext))
121
    yield from gen(obj, **kwargs)
122
123
124
def check(ext, obj=None):
125
    """Confirm an extension is supported for publish.
126
127
    :raises: :class:`doorstop.common.DoorstopError` for unknown formats
128
129
    :return: publisher class if available
130
131
    """
132
    # Mapping from file extension to class.
133
    PUBLISHER_LIST = {
134
        ".txt": TextPublisher(obj, ext),
135
        ".md": MarkdownPublisher(obj, ext),
136
        ".html": HtmlPublisher(obj, ext),
137
        ".tex": LaTeXPublisher(obj, ext),
138
    }
139
140
    exts = ", ".join(ext for ext in PUBLISHER_LIST)
141
    msg = "unknown publish format: {} (options: {})".format(ext or None, exts)
142
    exc = DoorstopError(msg)
143
144
    try:
145
        publisherClass = PUBLISHER_LIST[ext]
146
    except KeyError:
147
        raise exc from None
148
    else:
149
        log.debug("found publisher class for: {}".format(ext))
150
        return publisherClass
151