Passed
Pull Request — develop (#399)
by
unknown
02:06
created

doorstop.core.reference_finder   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 84
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 12
eloc 43
dl 0
loc 84
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
B ReferenceFinder.find_ref() 0 41 8
A ReferenceFinder.find_file_reference() 0 23 4
1
# SPDX-License-Identifier: LGPL-3.0-only
2
3
"""Finding 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 ReferenceFinder:
17
    """Finds files referenced from an Item."""
18
19
    @staticmethod
20
    def find_ref(ref, tree, item_path):
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
33
        # Search for the external reference
34
        log.debug("searching for ref '{}'...".format(ref))
35
        pattern = r"(\b|\W){}(\b|\W)".format(re.escape(ref))
36
        log.trace("regex: {}".format(pattern))
37
        regex = re.compile(pattern)
38
        for path, filename, relpath in tree.vcs.paths:
39
            # Skip the item's file while searching
40
            if path == item_path:
41
                continue
42
            # Check for a matching filename
43
            if filename == ref:
44
                return relpath, None
45
            # Skip extensions that should not be considered text
46
            if os.path.splitext(filename)[-1] in settings.SKIP_EXTS:
47
                continue
48
            # Search for the reference in the file
49
            lines = pyficache.getlines(path)
50
            if lines is None:
51
                log.trace("unable to read lines from: {}".format(path))
52
                continue
53
            for lineno, line in enumerate(lines, start=1):
54
                if regex.search(line):
55
                    log.debug("found ref: {}".format(relpath))
56
                    return relpath, lineno
57
58
        msg = "external reference not found: {}".format(ref)
59
        raise DoorstopError(msg)
60
61
    @staticmethod
62
    def find_file_reference(ref_path, root, tree, item_path):
63
        """Find the external file reference.
64
65
        :raises: :class:`~doorstop.common.DoorstopError` when no
66
            reference is found
67
68
        :return: True when reference is found
69
70
        """
71
72
        log.debug("searching for ref '{}'...".format(ref_path))
73
        ref_full_path = os.path.join(root, ref_path)
74
75
        for path, filename, relpath in tree.vcs.paths:
76
            # Skip the item's file while searching
77
            if path == item_path:
78
                continue
79
            if path == ref_full_path:
80
                return True
81
82
        msg = "external reference not found: {}".format(ref_path)
83
        raise DoorstopError(msg)
84