Passed
Pull Request — develop (#399)
by
unknown
01:37
created

doorstop.core.finder.Finder.find_ref()   B

Complexity

Conditions 8

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 23
dl 0
loc 39
rs 7.3333
c 0
b 0
f 0
cc 8
nop 3
1
# SPDX-License-Identifier: LGPL-3.0-only
2
3
"""TODO: Helper for the Item class. Finds external references."""
4
5
import os
6
import re
7
8
import pyficache
9
10
from doorstop import common, settings
11
from doorstop.common import DoorstopError
12
13
log = common.logger(__name__)
14
15
16
class Finder:  # pylint: disable=R0902
17
    """TODO: Helper for the Item class. Finds external references."""
18
19
    @staticmethod
20
    def find_ref(tree, item_path, ref):
21
        """Get the external file reference and line number.
22
23
        :raises: :class:`~doorstop.common.DoorstopError` when no
24
            reference is found
25
26
        :return: relative path to file or None (when no reference
27
            set),
28
            line number (when found in file) or None (when found as
29
            filename) or None (when no reference set)
30
31
        """
32
        log.debug("searching for ref '{}'...".format(ref))
33
        pattern = r"(\b|\W){}(\b|\W)".format(re.escape(ref))
34
        log.trace("regex: {}".format(pattern))
35
        regex = re.compile(pattern)
36
        for path, filename, relpath in tree.vcs.paths:
37
            # Skip the item's file while searching
38
            if path == item_path:
39
                continue
40
            # Check for a matching filename
41
            if filename == ref:
42
                return relpath, None
43
            # Skip extensions that should not be considered text
44
            if os.path.splitext(filename)[-1] in settings.SKIP_EXTS:
45
                continue
46
            # Search for the reference in the file
47
            lines = pyficache.getlines(path)
48
            if lines is None:
49
                log.trace("unable to read lines from: {}".format(path))
50
                continue
51
            for lineno, line in enumerate(lines, start=1):
52
                if regex.search(line):
53
                    log.debug("found ref: {}".format(relpath))
54
                    return relpath, lineno
55
56
        msg = "external reference not found: {}".format(ref)
57
        raise DoorstopError(msg)
58
59
    @staticmethod
60
    def find_external_file_ref(root, tree, item_path, ref_path):
61
        log.debug("searching for ref '{}'...".format(ref_path))
62
        ref_full_path = os.path.join(root, ref_path)
63
64
        for path, filename, relpath in tree.vcs.paths:
65
            # Skip the item's file while searching
66
            if path == item_path:
67
                continue
68
            if path == ref_full_path:
69
                return True
70
71
        msg = "external reference not found: {}".format(ref_path)
72
        raise DoorstopError(msg)
73