Passed
Push — develop ( b3bfc7...6fb855 )
by Dean
02:36
created

CacheManager.discover()   B

Complexity

Conditions 6

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 29.4373
Metric Value
cc 6
dl 0
loc 38
ccs 2
cts 15
cp 0.1333
crap 29.4373
rs 7.5384

1 Method

Rating   Name   Duplication   Size   Complexity  
A CacheManager.process() 0 8 3
1 1
from plugin.core.environment import Environment
2 1
from plugin.core.libraries.constants import CONTENTS_PATH
3 1
from plugin.core.libraries.helpers import StorageHelper, SystemHelper
4
5 1
import filecmp
6 1
import logging
7 1
import os
8
9 1
log = logging.getLogger(__name__)
10
11
12 1
class CacheManager(object):
13 1
    @classmethod
14
    def sync(cls):
15
        """Synchronize native libraries cache, adding/updating/removing items to match bundled libraries.
16
17
        :rtype: str
18
        """
19
20
        source, destination = cls.get_paths()
21
22
        if not source or not destination:
23
            return None
24
25
        # Compare directories, discover tasks
26
        changes = filecmp.dircmp(source, destination)
27
        tasks = cls.discover(changes)
28
29
        # Execute tasks
30
        if tasks and not cls.execute(source, destination, tasks):
31
            return None
32
33
        return os.path.join(Environment.path.plugin_data, 'Libraries')
34
35 1
    @classmethod
36 1
    def discover(cls, changes, base_path='', tasks=None):
37
        """"Discover actions required to update the cache.
38
39
        :param changes: Changes between bundle + cache directories
40
        :type changes: filecmp.dircmp
41
42
        :param base_path: Current directory of changes
43
        :type base_path: str
44
45
        :param tasks: Current tasks
46
        :type tasks: list or None
47
48
        :rtype: list
49
        """
50
        if tasks is None:
51
            tasks = []
52
53
        def process(action, names):
54
            for name in names:
55
                # Ignore "*.pyc" files
56
                if name.endswith('.pyc'):
57
                    continue
58
59
                # Append task to list
60
                tasks.append((action, os.path.join(base_path, name)))
61
62
        # Create tasks from `changes`
63
        process('add', changes.left_only)
64
        process('delete', changes.right_only)
65
        process('update', changes.diff_files)
66
67
        # Process sub directories
68
        for name, child in changes.subdirs.items():
69
            cls.discover(child, os.path.join(base_path, name), tasks)
70
71
        # Tasks retrieved
72
        return tasks
73
74 1
    @classmethod
75
    def execute(cls, source, destination, tasks):
76
        """Execute tasks on directories
77
78
        :param source: Native libraries source directory
79
        :type source: str
80
81
        :param destination: Native libraries cache directory
82
        :type destination: str
83
84
        :param tasks: Tasks to execute
85
        :type tasks: list
86
87
        :rtype: bool
88
        """
89
        success = True
90
91
        for action, path in tasks:
92
            if action in ['add', 'update']:
93
                action_success = StorageHelper.copy(os.path.join(source, path), os.path.join(destination, path))
94
            elif action == 'delete':
95
                action_success = StorageHelper.delete(os.path.join(destination, path))
96
            else:
97
                log.warn('Unknown task: %r - %r', action, path)
98
                action_success = False
99
100
            if not action_success:
101
                success = False
102
103
        return success
104
105 1
    @staticmethod
106
    def get_paths():
107
        """Retrieve system-specific native libraries source + destination path
108
109
        :rtype: (str, str)
110
        """
111
        # Retrieve system details
112
        system = SystemHelper.name()
113
        architecture = SystemHelper.architecture()
114
115
        if not architecture:
116
            return None, None
117
118
        # Build list of acceptable architectures
119
        architectures = [architecture]
120
121
        if architecture == 'i686':
122
            # Fallback to i386
123
            architectures.append('i386')
124
125
        # Look for matching libraries
126
        for arch in architectures + ['universal']:
127
            # Build source path
128
            source = os.path.join(CONTENTS_PATH, 'Libraries', system, arch)
129
130
            # Ensure `source` directory exists
131
            if not os.path.exists(source):
132
                continue
133
134
            # Build path for native dependencies
135
            destination = os.path.join(Environment.path.plugin_data, 'Libraries', system, arch)
136
137
            # Ensure `destination` directory has been created
138
            if not StorageHelper.create_directories(destination):
139
                # Directory couldn't be created
140
                return None, None
141
142
            return source, destination
143
144
        # No libraries could be found
145
        log.error('Unable to find native libraries for platform (name: %r, architecture: %r)', system, architecture)
146
        return None, None
147