Passed
Push — develop ( 3c901c...de3ad7 )
by Dean
02:43
created

LibrariesManager   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Test Coverage

Coverage 67.24%
Metric Value
dl 0
loc 246
ccs 78
cts 116
cp 0.6724
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 1
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
10 1
import logging
11 1
import os
12 1
import sys
13
14 1
log = logging.getLogger(__name__)
15
16
17 1
class LibrariesManager(object):
18 1
    @classmethod
19 1
    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
        # Use `cache` value from advanced configuration
27 1
        cache = Configuration.advanced['libraries'].get_boolean('cache', cache)
28
29
        # Retrieve libraries path (and cache libraries, if enabled)
30 1
        libraries_path = cls._libraries_path(cache)
31
32 1
        if not libraries_path:
33
            return
34
35 1
        log.info('Using native libraries at %r', StorageHelper.to_relative_path(libraries_path))
36
37
        # Remove current native library directories from `sys.path`
38 1
        cls.reset()
39
40
        # Insert platform specific library paths
41 1
        cls._insert_paths(libraries_path)
42
43
        # Display library paths in logfile
44 1
        for path in sys.path:
45 1
            path = os.path.abspath(path)
46
47 1
            if StorageHelper.is_framework_path(path):
48
                continue
49
50 1
            log.info('[PATH] %s', StorageHelper.to_relative_path(path))
51
52 1
    @staticmethod
53
    def test():
54
        """Test native libraries to ensure they can be correctly loaded"""
55 1
        log.info('Testing native library support...')
56
57 1
        metadata = {}
58
59 1
        for test in LIBRARY_TESTS:
60
            # Run tests
61 1
            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
                # Write message to logfile
65
                log.error('%s: unavailable - %s', test.name, result.get('message'), exc_info=result.get('exc_info'))
66
67
                if not test.optional:
68
                    return
69
70
                continue
71
72
            # Test successful
73 1
            t_metadata = result.get('metadata') or {}
74 1
            t_versions = t_metadata.get('versions')
75
76 1
            if t_versions:
77 1
                if len(t_versions) > 1:
78 1
                    log.info('%s: available (%s)', test.name, ', '.join([
79
                        '%s: %s' % (key, value)
80
                        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 1
                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 1
        versions = metadata.get('versions') or {}
94
95 1
        RAVEN.tags.update(dict([
96
            ('%s.version' % key, value)
97
            for key, value in versions.items()
98
        ]))
99
100 1
    @classmethod
101
    def reset(cls):
102
        """Remove all the native library directives from `sys.path`"""
103
104 1
        for path in sys.path:
105 1
            path = os.path.abspath(path)
106
107 1
            if not path.lower().startswith(CONTENTS_PATH.lower()):
108 1
                continue
109
110
            # Convert to relative path
111 1
            path_rel = os.path.relpath(path, CONTENTS_PATH)
112
113
            # 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 1
            path_rel = path_rel.replace('\\', '/')
118
119
            # Ignore non-native library directories
120 1
            if path_rel not in NATIVE_DIRECTORIES:
121 1
                continue
122
123
            # Remove from `sys.path`
124
            PathHelper.remove(path)
125
126 1
    @classmethod
127 1
    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 1
        if libraries_path and os.path.exists(libraries_path):
138
            log.info('Using libraries at %r', StorageHelper.to_relative_path(libraries_path))
139
            RAVEN.tags.update({'libraries.source': 'custom'})
140
            return libraries_path
141
142
        # Use system libraries (if bundled libraries have been disabled in "advanced.ini")
143 1
        if not Configuration.advanced['libraries'].get_boolean('bundled', True):
144
            log.info('Bundled libraries have been disabled, using system libraries')
145
            RAVEN.tags.update({'libraries.source': 'system'})
146
            return None
147
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
        cache_path = Configuration.advanced['libraries'].get('cache_path')
159
160
        # Try cache libraries to `cache_path`
161
        libraries_path = CacheManager.sync(cache_path)
162
163
        if not libraries_path:
164
            log.info('Unable to cache libraries, using bundled libraries directly')
165
            return Environment.path.libraries
166
167
        log.info('Cached libraries to %r', StorageHelper.to_relative_path(libraries_path))
168
        return libraries_path
169
170 1
    @classmethod
171
    def _insert_paths(cls, libraries_path):
172
        # Retrieve system details
173 1
        system = SystemHelper.name()
174 1
        architecture = SystemHelper.architecture()
175
176 1
        if not architecture:
177
            return
178
179 1
        log.debug('System: %r, Architecture: %r', system, architecture)
180
181
        # Insert architecture specific libraries
182 1
        architectures = [architecture]
183
184 1
        if architecture == 'i686':
185
            # Fallback to i386
186
            architectures.append('i386')
187
188 1
        for arch in architectures + ['universal']:
189 1
            cls._insert_architecture_paths(libraries_path, system, arch)
190
191 1
    @classmethod
192
    def _insert_architecture_paths(cls, libraries_path, system, architecture):
193 1
        architecture_path = os.path.join(libraries_path, system, architecture)
194
195 1
        if not os.path.exists(architecture_path):
196 1
            return
197
198
        # Architecture libraries
199 1
        PathHelper.insert(libraries_path, system, architecture)
200
201
        # System libraries
202 1
        if system == 'Windows':
203
            # Windows libraries (VC++ specific)
204
            cls._insert_paths_windows(libraries_path, system, architecture)
205
        else:
206
            # Darwin/FreeBSD/Linux libraries
207 1
            cls._insert_paths_unix(libraries_path, system, architecture)
208
209 1
    @staticmethod
210
    def _insert_paths_unix(libraries_path, system, architecture):
211
        # UCS specific libraries
212 1
        ucs = UNICODE_MAP.get(sys.maxunicode)
213 1
        log.debug('UCS: %r', ucs)
214
215 1
        if ucs:
216 1
            PathHelper.insert(libraries_path, system, architecture, ucs)
217
218
        # CPU specific libraries
219 1
        cpu_type = SystemHelper.cpu_type()
220 1
        page_size = SystemHelper.page_size()
221
222 1
        log.debug('CPU Type: %r', cpu_type)
223 1
        log.debug('Page Size: %r', page_size)
224
225 1
        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 1
        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 1
        RAVEN.tags.update({
240
            'cpu.type': cpu_type,
241
            'memory.page_size': page_size,
242
            'python.ucs': ucs
243
        })
244
245 1
    @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