@ -24,7 +24,7 @@ parameters given by the setting SOUND_QUALITY. This script requires
 | 
				
			|||||||
Sox (and soxi).
 | 
					Sox (and soxi).
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
import atexit
 | 
					import atexit
 | 
				
			||||||
import concurrent.futures as futures
 | 
					from concurrent import futures
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
@ -142,9 +142,9 @@ class MonitorHandler(PatternMatchingEventHandler):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pool = None
 | 
					    pool = None
 | 
				
			||||||
    jobs = {}
 | 
					    jobs = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, subdir, pool, **sync_kw):
 | 
					    def __init__(self, subdir, pool, jobs=None, **sync_kw):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        :param str subdir: sub-directory in program dirs to monitor \
 | 
					        :param str subdir: sub-directory in program dirs to monitor \
 | 
				
			||||||
            (SOUND_ARCHIVES_SUBDIR or SOUND_EXCERPTS_SUBDIR);
 | 
					            (SOUND_ARCHIVES_SUBDIR or SOUND_EXCERPTS_SUBDIR);
 | 
				
			||||||
@ -154,6 +154,7 @@ class MonitorHandler(PatternMatchingEventHandler):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        self.subdir = subdir
 | 
					        self.subdir = subdir
 | 
				
			||||||
        self.pool = pool
 | 
					        self.pool = pool
 | 
				
			||||||
 | 
					        self.jobs = jobs or {}
 | 
				
			||||||
        self.sync_kw = sync_kw
 | 
					        self.sync_kw = sync_kw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        patterns = [
 | 
					        patterns = [
 | 
				
			||||||
@ -199,29 +200,27 @@ class MonitorHandler(PatternMatchingEventHandler):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class SoundMonitor:
 | 
					class SoundMonitor:
 | 
				
			||||||
    """Monitor for filesystem changes in order to synchronise database and
 | 
					    """Monitor for filesystem changes in order to synchronise database and
 | 
				
			||||||
    analyse files."""
 | 
					    analyse files of a provided program."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def report(self, program=None, component=None, logger=logging, *content):
 | 
					    def report(self, program=None, component=None, *content, logger=logging):
 | 
				
			||||||
        if not component:
 | 
					        content = " ".join([str(c) for c in content])
 | 
				
			||||||
        logger.info(
 | 
					        logger.info(
 | 
				
			||||||
                "%s: %s", str(program), " ".join([str(c) for c in content])
 | 
					            f"{program}: {content}"
 | 
				
			||||||
            )
 | 
					            if not component
 | 
				
			||||||
        else:
 | 
					            else f"{program}, {component}: {content}"
 | 
				
			||||||
            logger.info(
 | 
					 | 
				
			||||||
                "%s, %s: %s",
 | 
					 | 
				
			||||||
                str(program),
 | 
					 | 
				
			||||||
                str(component),
 | 
					 | 
				
			||||||
                " ".join([str(c) for c in content]),
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def scan(self, logger=logging):
 | 
					    def scan(self, logger=logging):
 | 
				
			||||||
        """For all programs, scan dirs."""
 | 
					        """For all programs, scan dirs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Return scanned directories.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        logger.info("scan all programs...")
 | 
					        logger.info("scan all programs...")
 | 
				
			||||||
        programs = Program.objects.filter()
 | 
					        programs = Program.objects.filter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dirs = []
 | 
					        dirs = []
 | 
				
			||||||
        for program in programs:
 | 
					        for program in programs:
 | 
				
			||||||
            logger.info("#%d %s", program.id, program.title)
 | 
					            logger.info(f"#{program.id} {program.title}")
 | 
				
			||||||
            self.scan_for_program(
 | 
					            self.scan_for_program(
 | 
				
			||||||
                program,
 | 
					                program,
 | 
				
			||||||
                settings.SOUND_ARCHIVES_SUBDIR,
 | 
					                settings.SOUND_ARCHIVES_SUBDIR,
 | 
				
			||||||
@ -234,7 +233,7 @@ class SoundMonitor:
 | 
				
			|||||||
                logger=logger,
 | 
					                logger=logger,
 | 
				
			||||||
                type=Sound.TYPE_EXCERPT,
 | 
					                type=Sound.TYPE_EXCERPT,
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            dirs.append(os.path.join(program.abspath))
 | 
					            dirs.append(program.abspath)
 | 
				
			||||||
        return dirs
 | 
					        return dirs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def scan_for_program(
 | 
					    def scan_for_program(
 | 
				
			||||||
@ -272,7 +271,12 @@ class SoundMonitor:
 | 
				
			|||||||
            if sound.check_on_file():
 | 
					            if sound.check_on_file():
 | 
				
			||||||
                SoundFile(sound.file.path).sync(sound=sound, **sync_kwargs)
 | 
					                SoundFile(sound.file.path).sync(sound=sound, **sync_kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _running = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def monitor(self, logger=logging):
 | 
					    def monitor(self, logger=logging):
 | 
				
			||||||
 | 
					        if self._running:
 | 
				
			||||||
 | 
					            raise RuntimeError("already running")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        """Run in monitor mode."""
 | 
					        """Run in monitor mode."""
 | 
				
			||||||
        with futures.ThreadPoolExecutor() as pool:
 | 
					        with futures.ThreadPoolExecutor() as pool:
 | 
				
			||||||
            archives_handler = MonitorHandler(
 | 
					            archives_handler = MonitorHandler(
 | 
				
			||||||
@ -307,5 +311,13 @@ class SoundMonitor:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            atexit.register(leave)
 | 
					            atexit.register(leave)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            while True:
 | 
					            self._running = True
 | 
				
			||||||
 | 
					            while self._running:
 | 
				
			||||||
                time.sleep(1)
 | 
					                time.sleep(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            leave()
 | 
				
			||||||
 | 
					            atexit.unregister(leave)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def stop(self):
 | 
				
			||||||
 | 
					        """Stop monitor() loop."""
 | 
				
			||||||
 | 
					        self._running = False
 | 
				
			||||||
 | 
				
			|||||||
@ -44,20 +44,45 @@ InterfaceTarget = namedtuple(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WrapperMixin:
 | 
					class WrapperMixin:
 | 
				
			||||||
    def __init__(self, target=None, ns=None, ns_attr=None, **kwargs):
 | 
					    type_interface = None
 | 
				
			||||||
 | 
					    """For instance of class wrapped by an Interface, this is the wrapping
 | 
				
			||||||
 | 
					    interface of the class."""
 | 
				
			||||||
 | 
					    instances = None
 | 
				
			||||||
 | 
					    ns = None
 | 
				
			||||||
 | 
					    ns_attr = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(
 | 
				
			||||||
 | 
					        self, target=None, ns=None, ns_attr=None, type_interface=None, **kwargs
 | 
				
			||||||
 | 
					    ):
 | 
				
			||||||
        self.target = target
 | 
					        self.target = target
 | 
				
			||||||
        if ns:
 | 
					        if ns:
 | 
				
			||||||
            self.inject(ns, ns_attr)
 | 
					            self.inject(ns, ns_attr)
 | 
				
			||||||
 | 
					        if self.type_interface:
 | 
				
			||||||
 | 
					            self._set_type_interface(type_interface)
 | 
				
			||||||
        super().__init__(**kwargs)
 | 
					        super().__init__(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _set_type_interface(self, type_interface):
 | 
				
			||||||
 | 
					        if self.type_interface:
 | 
				
			||||||
 | 
					            raise RuntimeError("a type interface is already assigned")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.type_interface = type_interface
 | 
				
			||||||
 | 
					        if not type_interface.instances:
 | 
				
			||||||
 | 
					            type_interface.instances = [self]
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            type_interface.instances.append(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def ns_target(self):
 | 
					    def ns_target(self):
 | 
				
			||||||
 | 
					        """Actual namespace's target (using ns.ns_attr)"""
 | 
				
			||||||
        if self.ns and self.ns_attr:
 | 
					        if self.ns and self.ns_attr:
 | 
				
			||||||
            return getattr(self.ns, self.ns_attr, None)
 | 
					            return getattr(self.ns, self.ns_attr, None)
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def inject(self, ns=None, ns_attr=None):
 | 
					    def inject(self, ns=None, ns_attr=None):
 | 
				
			||||||
        if ns and ns_attr:
 | 
					        """Inject interface into namespace at given key."""
 | 
				
			||||||
 | 
					        if not (ns and ns_attr):
 | 
				
			||||||
 | 
					            raise ValueError("ns and ns_attr must be provided together")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ns_target = getattr(ns, ns_attr, None)
 | 
					        ns_target = getattr(ns, ns_attr, None)
 | 
				
			||||||
        if self.target is ns_target:
 | 
					        if self.target is ns_target:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
@ -66,16 +91,17 @@ class WrapperMixin:
 | 
				
			|||||||
                "self target already injected. It must be "
 | 
					                "self target already injected. It must be "
 | 
				
			||||||
                "`release` before `inject`."
 | 
					                "`release` before `inject`."
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.target = ns_target
 | 
					        self.target = ns_target
 | 
				
			||||||
            setattr(ns, ns_attr, self.parent)
 | 
					        setattr(ns, ns_attr, self.interface)
 | 
				
			||||||
        elif not ns or not ns_attr:
 | 
					
 | 
				
			||||||
            raise ValueError("ns and ns_attr must be provided together")
 | 
					 | 
				
			||||||
        self.ns = ns
 | 
					        self.ns = ns
 | 
				
			||||||
        self.ns_attr = ns_attr
 | 
					        self.ns_attr = ns_attr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def release(self):
 | 
					    def release(self):
 | 
				
			||||||
        if self.ns_target is self:
 | 
					        """Remove injection from previously injected parent, reset target."""
 | 
				
			||||||
            setattr(self.target.namespace, self.target.name, self.target)
 | 
					        if self.ns_target is self.interface:
 | 
				
			||||||
 | 
					            setattr(self.ns, self.ns_attr, self.target)
 | 
				
			||||||
        self.target = None
 | 
					        self.target = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,7 +109,9 @@ class SpoofMixin:
 | 
				
			|||||||
    traces = None
 | 
					    traces = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, funcs=None, **kwargs):
 | 
					    def __init__(self, funcs=None, **kwargs):
 | 
				
			||||||
        self.reset(funcs or {})
 | 
					        self.reset(
 | 
				
			||||||
 | 
					            funcs or {},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        super().__init__(**kwargs)
 | 
					        super().__init__(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reset(self, funcs=None):
 | 
					    def reset(self, funcs=None):
 | 
				
			||||||
@ -152,23 +180,19 @@ class SpoofMixin:
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        func = self.funcs[name]
 | 
					        func = self.funcs[name]
 | 
				
			||||||
        if callable(func):
 | 
					        if callable(func):
 | 
				
			||||||
            return func(*a, **kw)
 | 
					            return func(self, *a, **kw)
 | 
				
			||||||
        return func
 | 
					        return func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class InterfaceMeta(SpoofMixin, WrapperMixin):
 | 
					class Interface:
 | 
				
			||||||
    calls = None
 | 
					    class IMeta(SpoofMixin, WrapperMixin):
 | 
				
			||||||
    """Calls done."""
 | 
					        def __init__(self, interface, **kwargs):
 | 
				
			||||||
 | 
					            self.interface = interface
 | 
				
			||||||
    def __init__(self, parent, **kwargs):
 | 
					 | 
				
			||||||
        self.parent = parent
 | 
					 | 
				
			||||||
            super().__init__(**kwargs)
 | 
					            super().__init__(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def __getitem__(self, name):
 | 
					        def __getitem__(self, name):
 | 
				
			||||||
            return self.traces[name]
 | 
					            return self.traces[name]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class Interface:
 | 
					 | 
				
			||||||
    _imeta = None
 | 
					    _imeta = None
 | 
				
			||||||
    """This contains a InterfaceMeta instance related to Interface one.
 | 
					    """This contains a InterfaceMeta instance related to Interface one.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -182,7 +206,7 @@ class Interface:
 | 
				
			|||||||
            _imeta_kw.setdefault("funcs", _funcs)
 | 
					            _imeta_kw.setdefault("funcs", _funcs)
 | 
				
			||||||
        if _target is not None:
 | 
					        if _target is not None:
 | 
				
			||||||
            _imeta_kw.setdefault("target", _target)
 | 
					            _imeta_kw.setdefault("target", _target)
 | 
				
			||||||
        self._imeta = InterfaceMeta(self, **_imeta_kw)
 | 
					        self._imeta = self.IMeta(self, **_imeta_kw)
 | 
				
			||||||
        self.__dict__.update(kwargs)
 | 
					        self.__dict__.update(kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
@ -195,19 +219,29 @@ class Interface:
 | 
				
			|||||||
        return cls(**kwargs)
 | 
					        return cls(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _irelease(self):
 | 
					    def _irelease(self):
 | 
				
			||||||
 | 
					        """Shortcut to `self._imeta.release`."""
 | 
				
			||||||
        self._imeta.release()
 | 
					        self._imeta.release()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _trace(self, *args, **kw):
 | 
					    def _trace(self, *args, **kw):
 | 
				
			||||||
 | 
					        """Shortcut to `self._imeta.get_trace`."""
 | 
				
			||||||
        return self._imeta.get_trace(*args, **kw)
 | 
					        return self._imeta.get_trace(*args, **kw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _traces(self, *args, **kw):
 | 
					    def _traces(self, *args, **kw):
 | 
				
			||||||
 | 
					        """Shortcut to `self._imeta.get_traces`."""
 | 
				
			||||||
        return self._imeta.get_traces(*args, **kw)
 | 
					        return self._imeta.get_traces(*args, **kw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __call__(self, *args, **kwargs):
 | 
					    def __call__(self, *args, **kwargs):
 | 
				
			||||||
        target = self._imeta.target
 | 
					        target = self._imeta.target
 | 
				
			||||||
 | 
					        print("is it class?", self, target, inspect.isclass(target))
 | 
				
			||||||
        if inspect.isclass(target):
 | 
					        if inspect.isclass(target):
 | 
				
			||||||
            target = target(*args, **kwargs)
 | 
					            target = target(*args, **kwargs)
 | 
				
			||||||
            return type(self)(target, _imeta_kw={"funcs": self._imeta.funcs})
 | 
					            return type(self)(
 | 
				
			||||||
 | 
					                target,
 | 
				
			||||||
 | 
					                _imeta_kw={"type_interface": self, "funcs": self._imeta.funcs},
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if "__call__" in self._imeta.funcs:
 | 
				
			||||||
 | 
					            return self._imeta.call("__call__", args, kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._imeta.add("__call__", args, kwargs)
 | 
					        self._imeta.add("__call__", args, kwargs)
 | 
				
			||||||
        return self._imeta.target(*args, **kwargs)
 | 
					        return self._imeta.target(*args, **kwargs)
 | 
				
			||||||
@ -216,3 +250,7 @@ class Interface:
 | 
				
			|||||||
        if attr in self._imeta.funcs:
 | 
					        if attr in self._imeta.funcs:
 | 
				
			||||||
            return lambda *args, **kwargs: self._imeta.call(attr, args, kwargs)
 | 
					            return lambda *args, **kwargs: self._imeta.call(attr, args, kwargs)
 | 
				
			||||||
        return getattr(self._imeta.target, attr)
 | 
					        return getattr(self._imeta.target, attr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        iface = super().__str__()
 | 
				
			||||||
 | 
					        return f"{iface}::{self._imeta.target}"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,10 @@
 | 
				
			|||||||
 | 
					from concurrent import futures
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.utils import timezone as tz
 | 
					from django.utils import timezone as tz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from aircox.conf import settings
 | 
				
			||||||
from aircox.models import Sound
 | 
					from aircox.models import Sound
 | 
				
			||||||
from aircox.controllers import sound_monitor
 | 
					from aircox.controllers import sound_monitor
 | 
				
			||||||
from aircox.test import Interface, interface
 | 
					from aircox.test import Interface, interface
 | 
				
			||||||
@ -25,8 +27,8 @@ def logger():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
def interfaces(logger):
 | 
					def interfaces():
 | 
				
			||||||
    return {
 | 
					    items = {
 | 
				
			||||||
        "SoundFile": Interface.inject(
 | 
					        "SoundFile": Interface.inject(
 | 
				
			||||||
            sound_monitor,
 | 
					            sound_monitor,
 | 
				
			||||||
            "SoundFile",
 | 
					            "SoundFile",
 | 
				
			||||||
@ -41,8 +43,11 @@ def interfaces(logger):
 | 
				
			|||||||
                "sleep": None,
 | 
					                "sleep": None,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        "datetime": Interface.inject(sound_monitor, "datetime", {now: now}),
 | 
					        "datetime": Interface.inject(sound_monitor, "datetime", {"now": now}),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    yield items
 | 
				
			||||||
 | 
					    for item in items.values():
 | 
				
			||||||
 | 
					        item._irelease()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture
 | 
					@pytest.fixture
 | 
				
			||||||
@ -65,6 +70,23 @@ def modified_task(interfaces):
 | 
				
			|||||||
    return sound_monitor.ModifiedTask()
 | 
					    return sound_monitor.ModifiedTask()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture
 | 
				
			||||||
 | 
					def monitor_handler(interfaces):
 | 
				
			||||||
 | 
					    pool = Interface(
 | 
				
			||||||
 | 
					        None,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "submit": lambda imeta, *a, **kw: Interface(
 | 
				
			||||||
 | 
					                None,
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "add_done_callback": None,
 | 
				
			||||||
 | 
					                    "done": False,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    return sound_monitor.MonitorHandler("/tmp", pool=pool, sync_kw=13)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestTask:
 | 
					class TestTask:
 | 
				
			||||||
    def test___init__(self, task):
 | 
					    def test___init__(self, task):
 | 
				
			||||||
        assert task.timestamp is not None
 | 
					        assert task.timestamp is not None
 | 
				
			||||||
@ -111,7 +133,7 @@ class TestModifiedTask:
 | 
				
			|||||||
        dt_now = now + modified_task.timeout_delta - tz.timedelta(hours=10)
 | 
					        dt_now = now + modified_task.timeout_delta - tz.timedelta(hours=10)
 | 
				
			||||||
        datetime = Interface.inject(sound_monitor, "datetime", {"now": dt_now})
 | 
					        datetime = Interface.inject(sound_monitor, "datetime", {"now": dt_now})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def sleep(n):
 | 
					        def sleep(imeta, n):
 | 
				
			||||||
            datetime._imeta.funcs[
 | 
					            datetime._imeta.funcs[
 | 
				
			||||||
                "now"
 | 
					                "now"
 | 
				
			||||||
            ] = modified_task.timestamp + tz.timedelta(hours=10)
 | 
					            ] = modified_task.timestamp + tz.timedelta(hours=10)
 | 
				
			||||||
@ -129,31 +151,124 @@ class TestModifiedTask:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestMonitorHandler:
 | 
					class TestMonitorHandler:
 | 
				
			||||||
    def test_monitor___init__(self):
 | 
					    def test_on_created(self, monitor_handler, event):
 | 
				
			||||||
        pass
 | 
					        monitor_handler._submit = monitor_handler.pool.submit
 | 
				
			||||||
 | 
					        monitor_handler.on_created(event)
 | 
				
			||||||
 | 
					        trace_args, trace_kwargs = monitor_handler.pool._trace("submit")
 | 
				
			||||||
 | 
					        assert isinstance(trace_args[0], sound_monitor.CreateTask)
 | 
				
			||||||
 | 
					        assert trace_args[1:] == (event, "new")
 | 
				
			||||||
 | 
					        assert trace_kwargs == monitor_handler.sync_kw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_on_created(self):
 | 
					    def test_on_deleted(self, monitor_handler, event):
 | 
				
			||||||
        pass
 | 
					        monitor_handler._submit = monitor_handler.pool.submit
 | 
				
			||||||
 | 
					        monitor_handler.on_deleted(event)
 | 
				
			||||||
 | 
					        trace_args, _ = monitor_handler.pool._trace("submit")
 | 
				
			||||||
 | 
					        assert isinstance(trace_args[0], sound_monitor.DeleteTask)
 | 
				
			||||||
 | 
					        assert trace_args[1:] == (event, "del")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_on_deleted(self):
 | 
					    def test_on_moved(self, monitor_handler, event):
 | 
				
			||||||
        pass
 | 
					        monitor_handler._submit = monitor_handler.pool.submit
 | 
				
			||||||
 | 
					        monitor_handler.on_moved(event)
 | 
				
			||||||
 | 
					        trace_args, trace_kwargs = monitor_handler.pool._trace("submit")
 | 
				
			||||||
 | 
					        assert isinstance(trace_args[0], sound_monitor.MoveTask)
 | 
				
			||||||
 | 
					        assert trace_args[1:] == (event, "mv")
 | 
				
			||||||
 | 
					        assert trace_kwargs == monitor_handler.sync_kw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_on_moved(self):
 | 
					    def test_on_modified(self, monitor_handler, event):
 | 
				
			||||||
        pass
 | 
					        monitor_handler._submit = monitor_handler.pool.submit
 | 
				
			||||||
 | 
					        monitor_handler.on_modified(event)
 | 
				
			||||||
 | 
					        trace_args, trace_kwargs = monitor_handler.pool._trace("submit")
 | 
				
			||||||
 | 
					        assert isinstance(trace_args[0], sound_monitor.ModifiedTask)
 | 
				
			||||||
 | 
					        assert trace_args[1:] == (event, "up")
 | 
				
			||||||
 | 
					        assert trace_kwargs == monitor_handler.sync_kw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_on_modified(self):
 | 
					    def test__submit(self, monitor_handler, event):
 | 
				
			||||||
        pass
 | 
					        handler = Interface()
 | 
				
			||||||
 | 
					        handler, created = monitor_handler._submit(
 | 
				
			||||||
 | 
					            handler, event, "prefix", kw=13
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        assert created
 | 
				
			||||||
 | 
					        assert handler.future._trace("add_done_callback")
 | 
				
			||||||
 | 
					        assert monitor_handler.pool._trace("submit") == (
 | 
				
			||||||
 | 
					            (handler, event),
 | 
				
			||||||
 | 
					            {"kw": 13},
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test__submit(self):
 | 
					        key = f"prefix:{event.src_path}"
 | 
				
			||||||
        pass
 | 
					        assert monitor_handler.jobs.get(key) == handler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture
 | 
				
			||||||
 | 
					def monitor_interfaces():
 | 
				
			||||||
 | 
					    items = {
 | 
				
			||||||
 | 
					        "atexit": Interface.inject(
 | 
				
			||||||
 | 
					            sound_monitor, "atexit", {"register": None, "leave": None}
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        "observer": Interface.inject(
 | 
				
			||||||
 | 
					            sound_monitor,
 | 
				
			||||||
 | 
					            "Observer",
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "schedule": None,
 | 
				
			||||||
 | 
					                "start": None,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    yield items
 | 
				
			||||||
 | 
					    for item in items.values():
 | 
				
			||||||
 | 
					        item.release()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture
 | 
				
			||||||
 | 
					def monitor():
 | 
				
			||||||
 | 
					    yield sound_monitor.SoundMonitor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SoundMonitor:
 | 
					class SoundMonitor:
 | 
				
			||||||
    def test_report(self):
 | 
					    def test_report(self, monitor, program, logger):
 | 
				
			||||||
        pass
 | 
					        monitor.report(program, "component", "content", logger=logger)
 | 
				
			||||||
 | 
					        msg = f"{program}, component: content"
 | 
				
			||||||
 | 
					        assert logger._trace("info", args=True) == (msg,)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_scan(self):
 | 
					    def test_scan(self, monitor, program, logger):
 | 
				
			||||||
        pass
 | 
					        interface = Interface(None, {"scan_for_program": None})
 | 
				
			||||||
 | 
					        monitor.scan_for_program = interface.scan_for_program
 | 
				
			||||||
 | 
					        dirs = monitor.scan(logger)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_monitor(self):
 | 
					        assert logger._traces("info") == (
 | 
				
			||||||
        pass
 | 
					            "scan all programs...",
 | 
				
			||||||
 | 
					            f"#{program.id} {program.title}",
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        assert dirs == [program.abspath]
 | 
				
			||||||
 | 
					        assert interface._traces("scan_for_program") == (
 | 
				
			||||||
 | 
					            ((program, settings.SOUND_ARCHIVES_SUBDIR), {"logger": logger})(
 | 
				
			||||||
 | 
					                (program, settings.SOUND_EXCERPTS_SUBDIR), {"logger": logger}
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_monitor(self, monitor, monitor_interfaces, logger):
 | 
				
			||||||
 | 
					        def sleep(*args, **kwargs):
 | 
				
			||||||
 | 
					            monitor.stop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        time = Interface.inject(sound_monitor, "time", {"sleep": sleep})
 | 
				
			||||||
 | 
					        monitor.monitor(logger=logger)
 | 
				
			||||||
 | 
					        time._irelease()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        observers = monitor_interfaces["observer"].instances
 | 
				
			||||||
 | 
					        observer = observers and observers[0]
 | 
				
			||||||
 | 
					        assert observer
 | 
				
			||||||
 | 
					        schedules = observer._traces("schedule")
 | 
				
			||||||
 | 
					        for (handler, *_), kwargs in schedules:
 | 
				
			||||||
 | 
					            assert isinstance(handler, sound_monitor.MonitorHandler)
 | 
				
			||||||
 | 
					            assert isinstance(handler.pool, futures.ThreadPoolExecutor)
 | 
				
			||||||
 | 
					            assert (handler.subdir, handler.type) in (
 | 
				
			||||||
 | 
					                (settings.SOUND_ARCHIVES_SUBDIR, Sound.TYPE_ARCHIVE),
 | 
				
			||||||
 | 
					                (settings.SOUND_EXCERPTS_SUBDIR, Sound.TYPE_EXCERPT),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert observer._trace("start")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        atexit = monitor_interfaces["atexit"]
 | 
				
			||||||
 | 
					        assert atexit._trace("register")
 | 
				
			||||||
 | 
					        assert atexit._trace("unregister")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assert observers
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user