Test Failed
Push — develop ( 2f2c0e...2adb06 )
by Dean
02:41
created

LibrariesManager.test()   D

Complexity

Conditions 8

Size

Total Lines 45

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 8
dl 0
loc 45
rs 4
1
from plugin.core.environment import Environment
2
from plugin.core.helpers.variable import merge
3
from plugin.core.libraries.constants import CONTENTS_PATH, NATIVE_DIRECTORIES, UNICODE_MAP
4
from plugin.core.libraries.helpers import PathHelper, StorageHelper, SystemHelper
5
from plugin.core.libraries.tests import LIBRARY_TESTS
6
from plugin.core.logger.handlers.error_reporter import RAVEN
7
8
import logging
9
import os
10
import sys
11
12
log = logging.getLogger(__name__)
13
14
15
class LibrariesManager(object):
16
    @classmethod
17
    def cache(cls):
18
        """Cache native libraries into the plugin data directory"""
19
20
        # Retrieve library platforms
21
        libraries_path = os.path.join(CONTENTS_PATH, 'Libraries')
22
        platforms = os.listdir(libraries_path)
23
24
        if 'Shared' in platforms:
25
            platforms.remove('Shared')
26
27
        # Create destination directory
28
        destination = os.path.join(Environment.path.plugin_data, 'Libraries')
29
30
        # Ensure destination exists
31
        StorageHelper.create_directories(destination)
32
33
        # Delete existing libraries
34
        for name in os.listdir(destination):
35
            path = os.path.join(destination, name)
36
37
            StorageHelper.delete_tree(path)
38
39
        # Copy libraries to directory
40
        for name in platforms:
41
            p_source = os.path.join(libraries_path, name)
42
            p_destination = os.path.join(destination, name)
43
44
            if not StorageHelper.copy_tree(p_source, p_destination):
45
                return None
46
47
        log.debug('Cached native libraries to %r', StorageHelper.to_relative_path(destination))
48
        return destination
49
50
    @classmethod
51
    def get_libraries_path(cls, cache=False):
52
        """Retrieve the native libraries base directory (and caching the libraries if enabled)"""
53
54
        if not cache:
55
            return Environment.path.libraries
56
57
        # Cache native libraries
58
        libraries_path = cls.cache()
59
60
        if libraries_path:
61
            # Reset native library directories in `sys.path`
62
            cls.reset()
63
64
            return libraries_path
65
66
        return Environment.path.libraries
67
68
    @classmethod
69
    def setup(cls, cache=False):
70
        """Setup native library directories"""
71
72
        # Retrieve libraries path
73
        libraries_path = cls.get_libraries_path(cache)
74
75
        log.info('Using native libraries at %r', StorageHelper.to_relative_path(libraries_path))
76
77
        # Retrieve system details
78
        system = SystemHelper.name()
79
        system_architecture = SystemHelper.architecture()
80
81
        if not system_architecture:
82
            return
83
84
        log.debug('System: %r, Architecture: %r', system, system_architecture)
85
86
        architectures = [system_architecture]
87
88
        if system_architecture == 'i686':
89
            # Fallback to i386
90
            architectures.append('i386')
91
92
        for architecture in reversed(architectures + ['universal']):
93
            # Common
94
            PathHelper.insert(libraries_path, system, architecture)
95
96
            # UCS
97
            if sys.maxunicode in UNICODE_MAP:
98
                PathHelper.insert(libraries_path, system, architecture, UNICODE_MAP[sys.maxunicode])
99
100
        # Log library paths
101
        for path in sys.path:
102
            path = os.path.abspath(path)
103
104
            if not StorageHelper.is_relative_path(path):
105
                continue
106
107
            log.info('[PATH] %s', StorageHelper.to_relative_path(path))
108
109
    @staticmethod
110
    def test():
111
        log.info('Testing native library support...')
112
113
        metadata = {}
114
115
        for test in LIBRARY_TESTS:
116
            # Run tests
117
            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 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 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...
118
119
            if not result.get('success'):
120
                # Write message to logfile
121
                log.error('%s: unavailable - %s', test.name, result.get('message'), exc_info=result.get('exc_info'))
122
123
                if not test.optional:
124
                    return
125
126
                continue
127
128
            # Test successful
129
            t_metadata = result.get('metadata') or {}
130
            t_versions = t_metadata.get('versions')
131
132
            if t_versions:
133
                if len(t_versions) > 1:
134
                    log.info('%s: available (%s)', test.name, ', '.join([
135
                        '%s: %s' % (key, value)
136
                        for key, value in t_versions.items()
137
                    ]))
138
                else:
139
                    key = t_versions.keys()[0]
140
141
                    log.info('%s: available (%s)', test.name, t_versions[key])
142
            else:
143
                log.info('%s: available', test.name)
144
145
            # Merge result into `metadata`
146
            merge(metadata, t_metadata, recursive=True)
147
148
        # Include versions in error reports
149
        versions = metadata.get('versions') or {}
150
151
        RAVEN.tags.update(dict([
152
            ('%s.version' % key, value)
153
            for key, value in versions.items()
154
        ]))
155
156
    @classmethod
157
    def reset(cls):
158
        """Remove all the native library directives from `sys.path`"""
159
160
        for path in sys.path:
161
            path = os.path.abspath(path)
162
163
            if not path.lower().startswith(CONTENTS_PATH.lower()):
164
                continue
165
166
            # Convert to relative path
167
            path_rel = os.path.relpath(path, CONTENTS_PATH)
168
169
            # Take the first two fragments
170
            path_rel = os.path.sep.join(path_rel.split(os.path.sep)[:2])
171
172
            # Ignore non-native library directories
173
            if path_rel not in NATIVE_DIRECTORIES:
174
                continue
175
176
            # Remove from `sys.path`
177
            PathHelper.remove(path)
178