Passed
Push — master ( 8ff2fc...2dab2b )
by P.R.
04:18 queued 10s
created

sdoc.sdoc2.node.HyperlinkNode   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 133
Duplicated Lines 88.72 %

Test Coverage

Coverage 31.48%

Importance

Changes 0
Metric Value
wmc 18
eloc 56
dl 118
loc 133
ccs 17
cts 54
cp 0.3148
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A HyperlinkNode.try_connect() 30 30 5
A HyperlinkNode.get_command() 5 5 1
A HyperlinkNode.set_scheme() 13 13 3
A HyperlinkNode.is_inline_command() 5 5 1
A HyperlinkNode.is_phrasing() 5 5 1
A HyperlinkNode.__init__() 9 9 1
A HyperlinkNode.get_html_attributes() 12 12 3
A HyperlinkNode.is_block_command() 5 5 1
A HyperlinkNode.prepare_content_tree() 12 12 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1 1
from typing import Dict
2 1
from urllib import error, request
3
4 1
import httplib2
5 1
from cleo.styles import OutputStyle
6
7 1
from sdoc.sdoc2.node.Node import Node
8 1
from sdoc.sdoc2.NodeStore import NodeStore
9
10
11 1 View Code Duplication
class HyperlinkNode(Node):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
12
    """
13
    SDoc2 node for hyperlinks.
14
    """
15
16
    # ------------------------------------------------------------------------------------------------------------------
17 1
    def __init__(self, io: OutputStyle, options: Dict[str, str], argument: str):
18
        """
19
        Object constructor.
20
21
        :param OutputStyle io: The IO object.
22
        :param dict[str,str] options: The options of the hyperlink.
23
        :param str argument: Not used.
24
        """
25
        super().__init__(io, 'hyperlink', options, argument)
26
27
    # ------------------------------------------------------------------------------------------------------------------
28 1
    def get_html_attributes(self) -> Dict[str, str]:
29
        """
30
        Checks valid html attributes for hyperlinks and returns a list of attributes.
31
        """
32
        valid_html_attributes = ('href', 'class', 'id', 'download', 'hreflang', 'media', 'rel', 'target', 'type')
33
        attributes_dict = {}
34
35
        for key, value in self._options.items():
36
            if key in valid_html_attributes:
37
                attributes_dict[key] = value
38
39
        return attributes_dict
40
41
    # ------------------------------------------------------------------------------------------------------------------
42 1
    def prepare_content_tree(self) -> None:
43
        """
44
        Prepares the content of the node. Checks url of 'href' attribute. Sets if needed.
45
        """
46
        # Setting scheme if we haven't.
47
        if 'href' in self._options:
48
            self.set_scheme(self._options['href'])
49
        else:
50
            self.set_scheme(self._argument)
51
52
        # Trying to connect
53
        self.try_connect()
54
55
    # ------------------------------------------------------------------------------------------------------------------
56 1
    def set_scheme(self, url: str):
57
        """
58
        Checks if we haven't got a scheme. Sets scheme if needed.
59
60
        :param str url: The url address with scheme or without.
61
        """
62
        if not request.urlparse(url).scheme:
63
            if url.startswith('ftp.'):
64
                url = 'ftp://{0!s}'.format(url)
65
                self._options['href'] = url
66
            else:
67
                url = 'http://{0!s}'.format(url)
68
                self._options['href'] = url
69
70
    # ------------------------------------------------------------------------------------------------------------------
71 1
    def try_connect(self) -> None:
72
        """
73
        Tries to connect to url. If have connection, checks the redirect. If redirect to 'https' protocol and
74
        host is the same, reset scheme in 'href' attribute.
75
        """
76
        try:
77
            response = request.urlopen(self._options['href'])
78
79
            # Check if we can connect to host.
80
            if response.getcode() not in range(200, 400):
81
                self.io.warning("Cannot connect to: '{0!s}'".format(self._options['href']))
82
            else:
83
                # If we connected, check the redirect.
84
                url = self._options['href'].lstrip('(http://)|(https://)')
85
                split_url = url.split('/')
86
87
                host = split_url[0]
88
                address = '/'.join(split_url[1:])
89
90
                connection = httplib2.HTTPConnectionWithTimeout(host)
91
                connection.request('HEAD', address)
92
                response = connection.getresponse()
93
94
                if response.status in range(301, 304):
95
                    # If host of redirected is the same, reset 'href' option
96
                    if response.getheader('Location').startswith('https://' + url):
97
                        self._options['href'].replace('http://', 'https://')
98
99
        except error.URLError:
100
            self.io.warning("Invalid url address: '{0!s}'".format(self._options['href']))
101
102
    # ------------------------------------------------------------------------------------------------------------------
103 1
    def get_command(self) -> str:
104
        """
105
        Returns the command of this node, i.e. hyperlink.
106
        """
107
        return 'hyperlink'
108
109
    # ------------------------------------------------------------------------------------------------------------------
110 1
    def is_phrasing(self) -> bool:
111
        """
112
        Returns True.
113
        """
114
        return True
115
116
    # ------------------------------------------------------------------------------------------------------------------
117 1
    def is_inline_command(self) -> bool:
118
        """
119
        Returns True.
120
        """
121
        return True
122
123
    # ------------------------------------------------------------------------------------------------------------------
124 1
    def is_block_command(self) -> bool:
125
        """
126
        Returns False.
127
        """
128
        return False
129
130
131
# ----------------------------------------------------------------------------------------------------------------------
132
NodeStore.register_inline_command('hyperlink', HyperlinkNode)
133