Test Failed
Push — develop ( 20bb73...3c901c )
by Dean
02:47
created

LibrariesManager   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Test Coverage

Coverage 64.21%
Metric Value
dl 0
loc 246
ccs 61
cts 95
cp 0.6421
rs 8.2857
wmc 39

9 Methods

Rating   Name   Duplication   Size   Complexity  
C _insert_paths_unix() 0 34 7
A _insert_paths_windows() 0 18 2
B setup() 0 33 4
B _libraries_path() 0 29 5
B reset() 0 25 4
A _insert_paths() 0 20 4
A _insert_architecture_paths() 0 17 3
A _cache_libraries() 0 13 2
D test() 0 46 8
1 1
from plugin.core.configuration import Configuration
0 ignored issues
show
Bug introduced by
The name configuration does not seem to exist in module plugin.core.
Loading history...
Configuration introduced by
Unable to import 'plugin.core.configuration' (invalid syntax (<string>, line 38))

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
2 1
from plugin.core.environment import Environment
3 1
from plugin.core.helpers.variable import merge
4 1
from plugin.core.libraries.cache import CacheManager
5 1
from plugin.core.libraries.constants import CONTENTS_PATH, NATIVE_DIRECTORIES, UNICODE_MAP
6 1
from plugin.core.libraries.helpers import PathHelper, StorageHelper, SystemHelper
7 1
from plugin.core.libraries.tests import LIBRARY_TESTS
8
from plugin.core.logger.handlers.error_reporter import RAVEN
0 ignored issues
show
Bug introduced by
The name error_reporter does not seem to exist in module plugin.core.logger.handlers.
Loading history...
Configuration introduced by
Unable to import 'plugin.core.logger.handlers.error_reporter' (invalid syntax (<string>, line 133))

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
9 1
10 1
import logging
11 1
import os
12
import sys
13 1
14
log = logging.getLogger(__name__)
15
16 1
17 1
class LibrariesManager(object):
18 1
    @classmethod
19
    def setup(cls, cache=False):
20
        """Setup native library directories
21
22
        :param cache: Enable native library caching
23
        :type cache: bool
24
        """
25
26 1
        # Use `cache` value from advanced configuration
27
        cache = Configuration.advanced['libraries'].get_boolean('cache', cache)
28 1
29
        # Retrieve libraries path (and cache libraries, if enabled)
30
        libraries_path = cls._libraries_path(cache)
31 1
32
        if not libraries_path:
33
            return
34 1
35 1
        log.info('Using native libraries at %r', StorageHelper.to_relative_path(libraries_path))
36
37 1
        # Remove current native library directories from `sys.path`
38 1
        cls.reset()
39
40 1
        # Insert platform specific library paths
41
        cls._insert_paths(libraries_path)
42 1
43
        # Display library paths in logfile
44
        for path in sys.path:
45 1
            path = os.path.abspath(path)
46
47 1
            if StorageHelper.is_framework_path(path):
48
                continue
49 1
50
            log.info('[PATH] %s', StorageHelper.to_relative_path(path))
51 1
52
    @staticmethod
53 1
    def test():
54
        """Test native libraries to ensure they can be correctly loaded"""
55
        log.info('Testing native library support...')
56
57
        metadata = {}
58
59
        for test in LIBRARY_TESTS:
60
            # Run tests
61
            result = test.run()
0 ignored issues
show
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...
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 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...
62
63 1
            if not result.get('success'):
64 1
                # Write message to logfile
65
                log.error('%s: unavailable - %s', test.name, result.get('message'), exc_info=result.get('exc_info'))
66 1
67 1
                if not test.optional:
68 1
                    return
69
70
                continue
71
72
            # Test successful
73 1
            t_metadata = result.get('metadata') or {}
74
            t_versions = t_metadata.get('versions')
75 1
76
            if t_versions:
77 1
                if len(t_versions) > 1:
78
                    log.info('%s: available (%s)', test.name, ', '.join([
79
                        '%s: %s' % (key, value)
80 1
                        for key, value in t_versions.items()
81
                    ]))
82
                else:
83 1
                    key = t_versions.keys()[0]
84
85 1
                    log.info('%s: available (%s)', test.name, t_versions[key])
86
            else:
87
                log.info('%s: available', test.name)
88
89
            # Merge result into `metadata`
90 1
            merge(metadata, t_metadata, recursive=True)
91
92
        # Include versions in error reports
93
        versions = metadata.get('versions') or {}
94
95
        RAVEN.tags.update(dict([
96
            ('%s.version' % key, value)
97
            for key, value in versions.items()
98
        ]))
99
100
    @classmethod
101
    def reset(cls):
102
        """Remove all the native library directives from `sys.path`"""
103
104
        for path in sys.path:
105
            path = os.path.abspath(path)
106
107
            if not path.lower().startswith(CONTENTS_PATH.lower()):
108
                continue
109
110
            # Convert to relative path
111
            path_rel = os.path.relpath(path, CONTENTS_PATH)
112
113 1
            # Take the first two fragments
114 1
            path_rel = os.path.sep.join(path_rel.split(os.path.sep)[:2])
115
116
            # Convert to unix-style separators (/)
117
            path_rel = path_rel.replace('\\', '/')
118
119
            # Ignore non-native library directories
120
            if path_rel not in NATIVE_DIRECTORIES:
121 1
                continue
122 1
123
            # Remove from `sys.path`
124
            PathHelper.remove(path)
125
126
    @classmethod
127
    def _libraries_path(cls, cache=False):
128
        """Retrieve the native libraries base directory (and cache the libraries if enabled)
129
130
        :param cache: Enable native library caching
131
        :type cache: bool
132
        """
133
134
        # Use specified libraries path (from "advanced.ini')
135 1
        libraries_path = Configuration.advanced['libraries'].get('libraries_path')
136
137
        if libraries_path and os.path.exists(libraries_path):
138 1
            log.info('Using libraries at %r', StorageHelper.to_relative_path(libraries_path))
139 1
            RAVEN.tags.update({'libraries.source': 'custom'})
140
            return libraries_path
141 1
142
        # Use system libraries (if bundled libraries have been disabled in "advanced.ini")
143
        if not Configuration.advanced['libraries'].get_boolean('bundled', True):
144 1
            log.info('Bundled libraries have been disabled, using system libraries')
145
            RAVEN.tags.update({'libraries.source': 'system'})
146
            return None
147 1
148
        # Cache libraries (if enabled)
149 1
        if cache:
150
            RAVEN.tags.update({'libraries.source': 'cache'})
151
            return cls._cache_libraries()
152
153 1
        RAVEN.tags.update({'libraries.source': 'bundle'})
154 1
        return Environment.path.libraries
155
156 1
    @classmethod
157
    def _cache_libraries(cls):
158 1
        cache_path = Configuration.advanced['libraries'].get('cache_path')
159
160 1
        # Try cache libraries to `cache_path`
161 1
        libraries_path = CacheManager.sync(cache_path)
162
163
        if not libraries_path:
164 1
            log.info('Unable to cache libraries, using bundled libraries directly')
165
            return Environment.path.libraries
166
167 1
        log.info('Cached libraries to %r', StorageHelper.to_relative_path(libraries_path))
168
        return libraries_path
169
170
    @classmethod
171
    def _insert_paths(cls, libraries_path):
172 1
        # Retrieve system details
173
        system = SystemHelper.name()
174 1
        architecture = SystemHelper.architecture()
175
176
        if not architecture:
177 1
            return
178 1
179
        log.debug('System: %r, Architecture: %r', system, architecture)
180 1
181 1
        # Insert architecture specific libraries
182
        architectures = [architecture]
183
184 1
        if architecture == 'i686':
185 1
            # Fallback to i386
186
            architectures.append('i386')
187 1
188 1
        for arch in architectures + ['universal']:
189
            cls._insert_architecture_paths(libraries_path, system, arch)
190 1
191
    @classmethod
192
    def _insert_architecture_paths(cls, libraries_path, system, architecture):
193
        architecture_path = os.path.join(libraries_path, system, architecture)
194
195
        if not os.path.exists(architecture_path):
196
            return
197 1
198
        # Architecture libraries
199
        PathHelper.insert(libraries_path, system, architecture)
200
201
        # System libraries
202
        if system == 'Windows':
203
            # Windows libraries (VC++ specific)
204 1
            cls._insert_paths_windows(libraries_path, system, architecture)
205
        else:
206
            # Darwin/FreeBSD/Linux libraries
207
            cls._insert_paths_unix(libraries_path, system, architecture)
208
209
    @staticmethod
210 1
    def _insert_paths_unix(libraries_path, system, architecture):
211
        # UCS specific libraries
212
        ucs = UNICODE_MAP.get(sys.maxunicode)
213
        log.debug('UCS: %r', ucs)
214
215
        if ucs:
216
            PathHelper.insert(libraries_path, system, architecture, ucs)
217
218
        # CPU specific libraries
219
        cpu_type = SystemHelper.cpu_type()
220
        page_size = SystemHelper.page_size()
221
222
        log.debug('CPU Type: %r', cpu_type)
223
        log.debug('Page Size: %r', page_size)
224
225
        if cpu_type:
226
            PathHelper.insert(libraries_path, system, architecture, cpu_type)
227
228
            if page_size:
229
                PathHelper.insert(libraries_path, system, architecture, '%s_%s' % (cpu_type, page_size))
230
231
        # UCS + CPU specific libraries
232
        if cpu_type and ucs:
233
            PathHelper.insert(libraries_path, system, architecture, cpu_type, ucs)
234
235
            if page_size:
236
                PathHelper.insert(libraries_path, system, architecture, '%s_%s' % (cpu_type, page_size), ucs)
237
238
        # Include attributes in error reports
239
        RAVEN.tags.update({
240
            'cpu.type': cpu_type,
241
            'memory.page_size': page_size,
242
            'python.ucs': ucs
243
        })
244
245
    @staticmethod
246
    def _insert_paths_windows(libraries_path, system, architecture):
247
        vcr = SystemHelper.vcr_version() or 'vc12'  # Assume "vc12" if call fails
248
        ucs = UNICODE_MAP.get(sys.maxunicode)
249
250
        log.debug('VCR: %r, UCS: %r', vcr, ucs)
251
252
        # VC++ libraries
253
        PathHelper.insert(libraries_path, system, architecture, vcr)
254
255
        # UCS libraries
256
        if ucs:
257
            PathHelper.insert(libraries_path, system, architecture, vcr, ucs)
258
259
        # Include attributes in error reports
260
        RAVEN.tags.update({
261
            'python.ucs': ucs,
262
            'vcr.version': vcr
263
        })
264