GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#44)
by
unknown
01:14
created

qtsass.watchers.polling   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 31
eloc 92
dl 0
loc 148
rs 9.92
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A PollingWatcher.setup() 0 6 1
A PollingThread.run() 0 11 3
A PollingThread.stopped() 0 3 1
A PollingWatcher.stop() 0 2 1
A PollingWatcher._dispatch() 0 4 2
A PollingThread.__init__() 0 10 1
A PollingThread.shutdown() 0 3 1
A PollingWatcher.start() 0 2 1
A PollingWatcher.connect() 0 2 1
A PollingWatcher._take_snapshot() 0 16 4
A PollingWatcher.disconnect() 0 2 1
A PollingThread.started() 0 3 1
A PollingWatcher.join() 0 2 1
B PollingWatcher._diff_snapshots() 0 13 7
A PollingThread.stop() 0 6 3
A PollingWatcher._check_snapshot() 0 6 2
1
# -*- coding: utf-8 -*-
2
# -----------------------------------------------------------------------------
3
# Copyright (c) 2015 Yann Lanthony
4
# Copyright (c) 2017-2018 Spyder Project Contributors
5
#
6
# Licensed under the terms of the MIT License
7
# (See LICENSE.txt for details)
8
# -----------------------------------------------------------------------------
9
"""Contains the Qt implementation of the Watcher api."""
10
11
# yapf: disable
12
13
from __future__ import absolute_import, print_function
14
15
# Standard library imports
16
import atexit
17
import os
18
import time
19
import threading
20
21
# Local imports
22
from qtsass.watchers.api import Watcher
23
from qtsass.importers import norm_path
24
25
26
class PollingThread(threading.Thread):
27
    """A daemon thread that continually fires a callback at the specified
28
    interval."""
29
30
    def __init__(self, callback, interval):
31
        super(PollingThread, self).__init__()
32
        self.daemon = True
33
34
        self.callback = callback
35
        self.interval = interval
36
        self._shutdown = threading.Event()
37
        self._stopped = threading.Event()
38
        self._started = threading.Event()
39
        atexit.register(self.stop)
40
41
    @property
42
    def started(self):
43
        return self._started.is_set()
44
45
    @property
46
    def stopped(self):
47
        return self._stopped.is_set()
48
49
    @property
50
    def shutdown(self):
51
        return self._shutdown.is_set()
52
53
    def stop(self):
54
        if not self.started and not self.shutdown:
55
            return
56
57
        self._shutdown.set()
58
        self._stopped.wait()
59
60
    def run(self):
61
        try:
62
            self._started.set()
63
64
            while True:
65
                self.callback()
66
                if self._shutdown.wait(self.interval):
67
                    break
68
69
        finally:
70
            self._stopped.set()
71
72
73
class PollingWatcher(Watcher):
74
    """Polls a directory recursively for changes.
75
76
    Detects file and directory changes, deletions, and creations. Recursion
77
    depth is limited to 2 levels. We use a limit because the scss file we're
78
    watching for changes could be sitting in the root of a project rather than
79
    a dedicated scss directory. That could lead to snapshots taking too long
80
    to build and diff. It's probably safe to assume that users aren't nesting
81
    scss deeper than a couple of levels.
82
    """
83
84
    def setup(self):
85
        self._snapshot_depth = 2
86
        self._snapshot = self._take_snapshot()
87
        self._callbacks = set()
88
        self._stop = False
89
        self._thread = PollingThread(self._check_snapshot, interval=1)
90
91
    def connect(self, fn):
92
        self._callbacks.add(fn)
93
94
    def disconnect(self, fn):
95
        self._callbacks.discard(fn)
96
97
    def start(self):
98
        self._thread.start()
99
100
    def stop(self):
101
        self._thread.stop()
102
103
    def join(self):
104
        self._thread.join()
105
106
    def _take_snapshot(self):
107
        snapshot = {}
108
        base_depth = len(norm_path(self._watch_dir).split('/'))
109
110
        for root, _, files in os.walk(self._watch_dir):
111
112
            path = norm_path(root)
113
            if len(path.split('/')) - base_depth > self._snapshot_depth:
114
                break
115
116
            snapshot[path] = os.path.getmtime(path)
117
            for f in files:
118
                path = norm_path(root, f)
119
                snapshot[path] = os.path.getmtime(path)
120
121
        return snapshot
122
123
    def _diff_snapshots(self, prev_snapshot, next_snapshot):
124
        changes = {}
125
        for path in set(prev_snapshot.keys()) | set(next_snapshot.keys()):
126
            if path in prev_snapshot and path not in next_snapshot:
127
                changes[path] = 'Deleted'
128
            elif path not in prev_snapshot and path in next_snapshot:
129
                changes[path] = 'Created'
130
            else:
131
                prev_mtime = prev_snapshot[path]
132
                next_mtime = next_snapshot[path]
133
                if next_mtime > prev_mtime:
134
                    changes[path] = 'Changed'
135
        return changes
136
137
    def _check_snapshot(self):
138
        next_snapshot = self._take_snapshot()
139
        changes = self._diff_snapshots(self._snapshot, next_snapshot)
140
        if changes:
141
            self._snapshot = next_snapshot
142
            self._dispatch()
143
144
    def _dispatch(self):
145
        css = self.compile()
146
        for callback in self._callbacks:
147
            callback(css)
148