Giter Club home page Giter Club logo

watchdog_gevent's Introduction

watchdog_gevent

Build Status PyPI version

watchdog_gevent is a gevent-based observer for watchdog.

Requirements

Setup

pip install watchdog_gevent

Usage

Just import and use its observer:

from watchdog_gevent import Observer

This will automatically import the best observer for the current platform, preferring a gevent-based observer if gevent is installed and the threading module has been monkeypatched. This library only works if the threading module has been monkeypatched.

Limitations

The package only works if you use gevent to monkeypatch the threading module. Additionally, file and directory events are not emitted.

License

watchdog_event is licensed under Apache 2.0. Please see license for licensing details.

watchdog_gevent's People

Contributors

bogdanp avatar lincrosenbach avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

watchdog_gevent's Issues

Suppressing gevent.monkey.is_module_patched checking

Hi!

I have a question about this checking:

if not gevent.monkey.is_module_patched("threading"):  # pragma: no cover
    raise RuntimeError(
        "gevent observers require the 'threading' module to be "
        "monkeypatched by gevent."
    )

I have a small unittest:

import unittest

from app import create_app, db

class BaseTestCase(unittest.TestCase):
    def setUp(self):
        self.app = create_app('testing')
        self.app_context = self.app.app_context()
        self.app_context.push()
        self.client = self.app.test_client(use_cookies=True)
        db.create_all()

    def tearDown(self):
        db.session.close()
        db.drop_all()
        self.app_context.pop()

class AppBasicsTestCase(BaseTestCase):
    def test_app_is_exists(self):
        self.assertFalse(current_app is None)

    def test_app_is_testing_environment(self):
        self.assertTrue(current_app.config['TESTING'])

    def test_cli_is_working(self):
        runner = current_app.test_cli_runner()
        result = runner.invoke(args=['test'])
        self.assertEqual(result.exit_code, 0)
        result = runner.invoke(args=['test', '--coverage'])
        self.assertEqual(result.exit_code, 0)

When I run the test, I get a warning:

00:48 $ make test-show-warnings 
================================================= test session starts ==================================================
platform linux -- Python 3.10.4, pytest-7.1.3, pluggy-1.0.0 -- /home/wakko/Documents/Projects/rpa-console/.venv/bin/python3
cachedir: .pytest_cache
rootdir: /home/wakko/Documents/Projects/rpa-console/tests
plugins: cov-3.0.0
collected 3 items                                                                                                      

tests/test_app_basics.py::AppBasicsTestCase::test_app_is_exists PASSED                                           [ 33%]
tests/test_app_basics.py::AppBasicsTestCase::test_app_is_testing_environment PASSED                              [ 66%]
tests/test_app_basics.py::AppBasicsTestCase::test_cli_is_working PASSED                                          [100%]

=================================================== warnings summary ===================================================
.venv/lib/python3.10/site-packages/dramatiq/watcher.py:9
  /home/wakko/Documents/Projects/rpa-console/.venv/lib/python3.10/site-packages/dramatiq/watcher.py:9: ImportWarning: gevent observers require the 'threading' module to be monkeypatched by gevent.
    import watchdog_gevent

test_app_basics.py::AppBasicsTestCase::test_app_is_testing_environment
test_app_basics.py::AppBasicsTestCase::test_cli_is_working
  /home/wakko/Documents/Projects/rpa-console/.venv/lib/python3.10/site-packages/flask_security/core.py:1364: DeprecationWarning: Replacing login_manager is deprecated in 5.0.0 and will be removed in 5.1
    warnings.warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================ 3 passed, 3 warnings in 1.02s =============================================

How I can suppress this warning?

License file missing in Setup Script

Please, add reference in setup script to license file so it will be distributed together with the package.
The PKG-INFO on Pypi also seems to be missing some info:
Home-page: UNKNOWN
Author: UNKNOWN
Author-email: UNKNOWN
License: UNKNOWN
Description-Content-Type: UNKNOWN
Platform: UNKNOWN

Test failure

I have not looked into this but my guess is that it is related to the version of gevent used. It came up as build failure for the Nixpkgs package.

============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-8.1.1, pluggy-1.4.0
rootdir: /build/source
configfile: setup.cfg
testpaths: tests
collected 1 item                                                               

tests/test_observer.py F                                                 [100%]

=================================== FAILURES ===================================
____________________ test_can_watch_for_changes_with_gevent ____________________

    def test_can_watch_for_changes_with_gevent():
        # Given a gevent-based thread that counts stuff and sleeps every second
        count = 0
    
        def counter():
            nonlocal count
            while True:
                count += 1
                time.sleep(1)
    
        counter_thread = Thread(target=counter, daemon=True)
        counter_thread.start()
    
        # And an event handler that gets notified when this file changes
        events = []
    
        class Handler(FileSystemEventHandler):
            def on_any_event(self, event):
                events.append(event)
    
        # And an observer that dispatches to that handler
        try:
            observer = Observer()
>           observer.schedule(Handler(), rel(".."), recursive=True)

tests/test_observer.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <GeventObserver(Thread-2, initial daemon)>
event_handler = <tests.test_observer.test_can_watch_for_changes_with_gevent.<locals>.Handler object at 0x7ffff60cf6e0>
path = '/build/source', recursive = True, event_filter = None

    def schedule(self, event_handler, path, recursive=False, event_filter=None):
        """
        Schedules watching a path and calls appropriate methods specified
        in the given event handler in response to file system events.
    
        :param event_handler:
            An event handler instance that has appropriate event handling
            methods which will be called by the observer in response to
            file system events.
        :type event_handler:
            :class:`watchdog.events.FileSystemEventHandler` or a subclass
        :param path:
            Directory path that will be monitored.
        :type path:
            ``str``
        :param recursive:
            ``True`` if events will be emitted for sub-directories
            traversed recursively; ``False`` otherwise.
        :type recursive:
            ``bool``
        :param event_filter:
            Collection of event types to emit, or None for no filtering (default).
        :type event_filter:
            Optional[Iterable[:class:`watchdog.events.FileSystemEvent`]]
        :return:
            An :class:`ObservedWatch` object instance representing
            a watch.
        """
        with self._lock:
            watch = ObservedWatch(path, recursive, event_filter)
            self._add_handler_for_watch(event_handler, watch)
    
            # If we don't have an emitter for this watch already, create it.
            if self._emitter_for_watch.get(watch) is None:
>               emitter = self._emitter_class(event_queue=self.event_queue, watch=watch, timeout=self.timeout,
                                              event_filter=event_filter)
E               TypeError: GeventEmitter.__init__() got an unexpected keyword argument 'event_filter'

/nix/store/wgnc664dbcaykwhkaf3k9bvvy8skzcv3-python3.12-watchdog-4.0.0/lib/python3.12/site-packages/watchdog/observers/api.py:320: TypeError

During handling of the above exception, another exception occurred:

    def test_can_watch_for_changes_with_gevent():
        # Given a gevent-based thread that counts stuff and sleeps every second
        count = 0
    
        def counter():
            nonlocal count
            while True:
                count += 1
                time.sleep(1)
    
        counter_thread = Thread(target=counter, daemon=True)
        counter_thread.start()
    
        # And an event handler that gets notified when this file changes
        events = []
    
        class Handler(FileSystemEventHandler):
            def on_any_event(self, event):
                events.append(event)
    
        # And an observer that dispatches to that handler
        try:
            observer = Observer()
            observer.schedule(Handler(), rel(".."), recursive=True)
            observer.start()
    
            # When I touch this file
            subprocess.run(["touch", __file__])
    
            # And wait a second
            time.sleep(1)
    
            # Then the counter should have incremented
            assert count >= 2
    
            # And the event should have been observed
            assert events == [
                FileModifiedEvent(__file__)
            ]
        finally:
            observer.stop()
>           observer.join()

tests/test_observer.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <GeventObserver(Thread-2, initial daemon)>, timeout = None

    def join(self, timeout=None):
        """Wait until the thread terminates.
    
        This blocks the calling thread until the thread whose join() method is
        called terminates -- either normally or through an unhandled exception
        or until the optional timeout occurs.
    
        When the timeout argument is present and not None, it should be a
        floating point number specifying a timeout for the operation in seconds
        (or fractions thereof). As join() always returns None, you must call
        is_alive() after join() to decide whether a timeout happened -- if the
        thread is still alive, the join() call timed out.
    
        When the timeout argument is not present or None, the operation will
        block until the thread terminates.
    
        A thread can be join()ed many times.
    
        join() raises a RuntimeError if an attempt is made to join the current
        thread as that would cause a deadlock. It is also an error to join() a
        thread before it has been started and attempts to do so raises the same
        exception.
    
        """
        if not self._initialized:
            raise RuntimeError("Thread.__init__() not called")
        if not self._started.is_set():
>           raise RuntimeError("cannot join thread before it is started")
E           RuntimeError: cannot join thread before it is started

/nix/store/m51f0kclg01xdc0mjxqs5cpyaxqbqhg5-python3-3.12.3/lib/python3.12/threading.py:1142: RuntimeError
=========================== short test summary info ============================
FAILED tests/test_observer.py::test_can_watch_for_changes_with_gevent - RuntimeError: cannot join thread before it is started
============================== 1 failed in 0.04s ===============================
``

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.