dnslink._get_and_parse_record()   D
last analyzed

Complexity

Conditions 12

Size

Total Lines 51
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 12

Importance

Changes 0
Metric Value
cc 12
eloc 25
nop 4
dl 0
loc 51
ccs 22
cts 22
cp 1
crap 12
rs 4.8
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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 dnslink._get_and_parse_record() 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.

1
"""Python implementation of DNSLink protocol."""
2
3
# pylint: disable=multiple-statements
4
5 1
import dns.resolver
6
7
8 1
def resolve(domain, protocol=None, depth=16, resolver=None):
9
    """
10
    Resolve a DNSLink TXT record.
11
12
    :param str domain: a domain to resolve
13
    :param str protocol: a record protocol
14
    :param int depth: a recursion depth
15
    :param dns.resolver.Resolver: a DNSPython resolver
16
17
    :return: the DNSLink records
18
    :rtype: list[str]
19
    """
20
21
    # With `_dnslink` subdomain
22
23 1
    if not domain.startswith('_dnslink.'): name = '_dnslink.' + domain
24 1
    else: name = domain
25
26 1
    records = _get_and_parse_record(name, protocol, depth, resolver)
27 1
    if records: return records
28
29
    # Without `_dnslink` subdomain
30
31 1
    if domain.startswith('_dnslink.'): name = domain[9:]
32 1
    else: name = domain
33
34 1
    records = _get_and_parse_record(name, protocol, depth, resolver)
35 1
    if records: return records
36
37
    # Not found
38
39 1
    return []
40
41
42 1
def _get_and_parse_record(domain, protocol=None, depth=16, resolver=None):
43
    """
44
    Get and parse DNSLink TXT record.
45
46
    :param str domain: a domain to resolve
47
    :param str protocol: a record protocol
48
    :param int depth: a recursion depth
49
    :param dns.resolver.Resolver: a DNSPython resolver
50
51
    :return: the DNSLink records
52
    :rtype: list[str]
53
    """
54
55 1
    if not isinstance(resolver, dns.resolver.Resolver):
56 1
        resolver = dns.resolver.get_default_resolver()
57
58 1
    try:
59 1
        answers = resolver.query(domain, 'TXT')
60 1
    except dns.exception.DNSException:
61 1
        return []
62
63 1
    records = []
64
65 1
    for rdata in answers:
66 1
        for txt in rdata.strings:
67 1
            record = txt.decode('utf-8')
68
69
            # Not valid TXT record
70 1
            if not record.count('=') == 1:
71 1
                continue
72
73 1
            content = record.split('/', 3)
74
75
            # Not valid DNSLink record
76 1
            if not len(content) >= 3 or not content[0] == 'dnslink=':
77 1
                continue
78
79
            # Chaining record
80 1
            if content[1] == 'dnslink' and depth > 1:
81 1
                records.extend([
82
                    record + ('/' + content[3] if len(content) > 3 else '')
83
                    for record
84
                    in resolve(content[2], protocol, depth - 1, resolver)
85
                ])
86
87
            # Normal record
88 1
            elif protocol in (content[1], None):
89 1
                content[0] = ''
90 1
                records.append('/'.join(content))
91
92
    return records
93