Passed
Push — develop ( fc562f...b9b638 )
by Dean
02:34
created

LibrariesManager._insert_paths_windows()   A

Complexity

Conditions 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 4.6796
Metric Value
cc 2
dl 0
loc 18
ccs 1
cts 8
cp 0.125
crap 4.6796
rs 9.4285
1 1
from plugin.core.environment import Environment
2 1
from plugin.core.helpers.variable import merge
3 1
from plugin.core.libraries.cache import CacheManager
4 1
from plugin.core.libraries.constants import CONTENTS_PATH, NATIVE_DIRECTORIES, UNICODE_MAP
5 1
from plugin.core.libraries.helpers import PathHelper, StorageHelper, SystemHelper
6 1
from plugin.core.libraries.tests import LIBRARY_TESTS
7 1
from plugin.core.logger.handlers.error_reporter import RAVEN
8
9 1
import logging
10 1
import os
11 1
import sys
12
13 1
log = logging.getLogger(__name__)
14
15
16 1
class LibrariesManager(object):
17 1
    @classmethod
18 1
    def setup(cls, cache=False):
19
        """Setup native library directories
20
21
        :param cache: Enable native library caching
22
        :type cache: bool
23
        """
24
25
        # Retrieve libraries path (and cache libraries, if enabled)
26 1
        libraries_path = cls._libraries_path(cache)
27
28 1
        log.info('Using native libraries at %r', StorageHelper.to_relative_path(libraries_path))
29
30
        # Insert platform specific library paths
31 1
        cls._insert_paths(libraries_path)
32
33
        # Display library paths in logfile
34 1
        for path in sys.path:
35 1
            path = os.path.abspath(path)
36
37 1
            if not StorageHelper.is_relative_path(path):
38 1
                continue
39
40 1
            log.info('[PATH] %s', StorageHelper.to_relative_path(path))
41
42 1
    @staticmethod
43
    def test():
44
        """Test native libraries to ensure they can be correctly loaded"""
45 1
        log.info('Testing native library support...')
46
47 1
        metadata = {}
48
49 1
        for test in LIBRARY_TESTS:
50
            # Run tests
51 1
            result = test.run()
0 ignored issues
show
Bug introduced by
The Class Cryptography does not seem to have a member named run.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
Bug introduced by
The Class Apsw does not seem to have a member named run.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
Bug introduced by
The Class LList does not seem to have a member named run.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
Bug introduced by
The Class PyOpenSSL does not seem to have a member named run.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
52
53 1
            if not result.get('success'):
54
                # Write message to logfile
55
                log.error('%s: unavailable - %s', test.name, result.get('message'), exc_info=result.get('exc_info'))
56
57
                if not test.optional:
58
                    return
59
60
                continue
61
62
            # Test successful
63 1
            t_metadata = result.get('metadata') or {}
64 1
            t_versions = t_metadata.get('versions')
65
66 1
            if t_versions:
67 1
                if len(t_versions) > 1:
68 1
                    log.info('%s: available (%s)', test.name, ', '.join([
69
                        '%s: %s' % (key, value)
70
                        for key, value in t_versions.items()
71
                    ]))
72
                else:
73 1
                    key = t_versions.keys()[0]
74
75 1
                    log.info('%s: available (%s)', test.name, t_versions[key])
76
            else:
77 1
                log.info('%s: available', test.name)
78
79
            # Merge result into `metadata`
80 1
            merge(metadata, t_metadata, recursive=True)
81
82
        # Include versions in error reports
83 1
        versions = metadata.get('versions') or {}
84
85 1
        RAVEN.tags.update(dict([
86
            ('%s.version' % key, value)
87
            for key, value in versions.items()
88
        ]))
89
90 1
    @classmethod
91
    def reset(cls):
92
        """Remove all the native library directives from `sys.path`"""
93
94
        for path in sys.path:
95
            path = os.path.abspath(path)
96
97
            if not path.lower().startswith(CONTENTS_PATH.lower()):
98
                continue
99
100
            # Convert to relative path
101
            path_rel = os.path.relpath(path, CONTENTS_PATH)
102
103
            # Take the first two fragments
104
            path_rel = os.path.sep.join(path_rel.split(os.path.sep)[:2])
105
106
            # Ignore non-native library directories
107
            if path_rel not in NATIVE_DIRECTORIES:
108
                continue
109
110
            # Remove from `sys.path`
111
            PathHelper.remove(path)
112
113 1
    @classmethod
114 1
    def _libraries_path(cls, cache=False):
115
        """Retrieve the native libraries base directory (and cache the libraries if enabled)
116
117
        :param cache: Enable native library caching
118
        :type cache: bool
119
        """
120
121 1
        if not cache:
122 1
            return Environment.path.libraries
123
124
        # Cache native libraries
125
        libraries_path = CacheManager.sync()
126
127
        if libraries_path:
128
            # Reset native library directories in `sys.path`
129
            cls.reset()
130
131
            return libraries_path
132
133
        return Environment.path.libraries
134
135 1
    @classmethod
136
    def _insert_paths(cls, libraries_path):
137
        # Retrieve system details
138 1
        system = SystemHelper.name()
139 1
        architecture = SystemHelper.architecture()
140
141 1
        if not architecture:
142
            return
143
144 1
        log.debug('System: %r, Architecture: %r', system, architecture)
145
146
        # Insert architecture specific libraries
147 1
        architectures = [architecture]
148
149 1
        if architecture == 'i686':
150
            # Fallback to i386
151
            architectures.append('i386')
152
153 1
        for arch in reversed(architectures + ['universal']):
154 1
            cls._insert_architecture_paths(libraries_path, system, arch)
155
156 1
    @classmethod
157
    def _insert_architecture_paths(cls, libraries_path, system, architecture):
158 1
        architecture_path = os.path.join(libraries_path, system, architecture)
159
160 1
        if not os.path.exists(architecture_path):
161 1
            return
162
163
        # Architecture libraries
164 1
        PathHelper.insert(libraries_path, system, architecture)
165
166
        # System libraries
167 1
        if system == 'Windows':
168
            # Windows libraries (VC++ specific)
169
            cls._insert_paths_windows(libraries_path, system, architecture)
170
        else:
171
            # Darwin/FreeBSD/Linux libraries
172 1
            cls._insert_paths_unix(libraries_path, system, architecture)
173
174 1
    @staticmethod
175
    def _insert_paths_unix(libraries_path, system, architecture):
176 1
        ucs = UNICODE_MAP.get(sys.maxunicode)
177
178 1
        log.debug('UCS: %r', ucs)
179
180
        # UCS libraries
181 1
        if ucs:
182 1
            PathHelper.insert(libraries_path, system, architecture, ucs)
183
184
        # Include attributes in error reports
185 1
        RAVEN.tags.update({
186
            'python.ucs': ucs
187
        })
188
189 1
    @staticmethod
190
    def _insert_paths_windows(libraries_path, system, architecture):
191
        vcr = SystemHelper.vcr_version() or 'vc12'  # Assume "vc12" if call fails
192
        ucs = UNICODE_MAP.get(sys.maxunicode)
193
194
        log.debug('VCR: %r, UCS: %r', vcr, ucs)
195
196
        # VC++ libraries
197
        PathHelper.insert(libraries_path, system, architecture, vcr)
198
199
        # UCS libraries
200
        if ucs:
201
            PathHelper.insert(libraries_path, system, architecture, vcr, ucs)
202
203
        # Include attributes in error reports
204
        RAVEN.tags.update({
205
            'python.ucs': ucs,
206
            'vcr.version': vcr
207
        })
208