Passed
Push — master ( aeb165...d9ae97 )
by Dean
03:04
created

LibrariesManager.setup()   B

Complexity

Conditions 4

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

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