diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c235a57..0c1a2f0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,14 @@ - repo: git@github.com:asottile/reorder_python_imports - sha: v0.3.0 - commit_stage: [commit] + sha: v0.3.2 + commit_stage: + - commit hooks: - id: reorder-python-imports stages: commit_stage - - repo: git://github.com/pre-commit/pre-commit-hooks - sha: v0.6.0 - commit_stage: [commit] + sha: v0.8.0 + commit_stage: + - commit hooks: - id: trailing-whitespace - id: check-ast @@ -15,16 +16,15 @@ - id: check-merge-conflict - id: check-symlinks - id: debug-statements - #- id: detect-aws-credentials - id: detect-private-key - id: double-quote-string-fixer - id: end-of-file-fixer - id: fix-encoding-pragma - id: flake8 stages: commit_stage - - repo: local - push_stage: [push] + push_stage: + - push hooks: - id: check-tox name: Tox Tests diff --git a/.travis.yml b/.travis.yml index 9b5a6e8..de125e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,14 @@ language: python python: - - 2.6 - - 2.7 - - pypy - - 3.2 - 3.3 - 3.4 + - 3.5 + - 3.6 - nightly - - pypy3 -matrix: - allow_failures: - - python: 2.6 - - python: 2.7 - - python: pypy +addons: + apt: + packages: + - pandoc install: - pip install -r requirements.txt - pip install -r requirements-dev.txt diff --git a/README.md b/README.md index cc47d65..9c6b339 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,10 @@ pip3 install qmon ``` This is how it'll appear in hipchat: + ![hipchat_window](http://i.imgur.com/G1vnPUm.png) Find us: + * [PyPi](https://pypi.python.org/pypi/qmon) diff --git a/qmon/main.py b/qmon/main.py index 6f8e54a..55022a7 100644 --- a/qmon/main.py +++ b/qmon/main.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- # # main.py - All commandline interactions # This file is a part of QMon. @@ -18,7 +19,6 @@ # You should have received a copy of the GNU General Public License # along with QMon. If not, see . # - from __future__ import print_function import logging @@ -26,6 +26,7 @@ import qmon.monitor from qmon import __version__ +from qmon import monitor def print_version(): @@ -43,6 +44,8 @@ def parse_known_args(): parser.add_argument('--host', required=True, help='Redis host') parser.add_argument('--port', '-p', help='Redis port') parser.add_argument('--room', '-r', help='HipChat Room') + parser.add_argument('--sleep-time', '-t', default=monitor.TIME_GAP_IN_SECONDS, help='Number of seconds to sleep') + parser.add_argument('--stop-once-empty', '-s', action='store_true', help='Stop when no items left in queue') parser.add_argument('-V', '--version', action='store_true', dest='version', @@ -55,7 +58,14 @@ def main(): args, otherthings, parser = parse_known_args() setup_logging() - qmon.monitor.monitor_queue(queue_name=args.queue_name, host=args.host, port=args.port, room=args.room) + qmon.monitor.monitor_queue( + queue_name=args.queue_name, + host=args.host, + port=args.port, + room=args.room, + sleep_time=args.sleep_time, + stop_once_empty=args.stop_once_empty + ) return 0 diff --git a/qmon/monitor.py b/qmon/monitor.py index 15bd4b0..a659bae 100644 --- a/qmon/monitor.py +++ b/qmon/monitor.py @@ -30,9 +30,15 @@ def __should_i_notify_subscribers_now(): return should_notify -def monitor_queue(queue_name, host, port, room): +def monitor_queue(queue_name, host, port, room, sleep_time=TIME_GAP_IN_SECONDS, stop_once_empty=False): + sleep_time = int(sleep_time) + if sleep_time < 1: + raise ValueError('Sleep time too low. Please specify sleep time in seconds.') + if room is None: + raise ValueError('Please specify a valid room name') logger.info('Connecting to hipchat') - hc = hypchat.HypChat(os.environ['HIP_TOKEN']) + hip_token = os.environ['HIP_TOKEN'] # Please specify your token using: export HIP_TOKEN=my_hipchat_token + hc = hypchat.HypChat(hip_token) room = hc.get_room(room) room.notification( @@ -45,7 +51,7 @@ def monitor_queue(queue_name, host, port, room): last_notify_items_length = 0 last_items_length = 0 rd = qmon.status.redis_connection_cached(host, port) - while items_in_queue: + while not stop_once_empty or items_in_queue: items_in_queue = qmon.status.get_items_in_queue(queue_name, redis_conn=rd, default=0) if last_items_length != items_in_queue: message = '{} queue status: {:,d}'.format(queue_name, items_in_queue) @@ -54,7 +60,7 @@ def monitor_queue(queue_name, host, port, room): if notified: last_notify_items_length = items_in_queue last_items_length = items_in_queue - time.sleep(TIME_GAP_IN_SECONDS) + time.sleep(sleep_time) room.notification( 'All done for {queue}.'.format(queue=queue_name), diff --git a/requirements-dev.txt b/requirements-dev.txt index 953186b..866d1ff 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,3 +5,5 @@ nose2 mock coverage unittest2 + +pypandoc diff --git a/setup.py b/setup.py index 0155e85..b1dc8ed 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,11 @@ -# coding=utf-8 +# -*- coding: utf-8 -*- from distutils.core import setup from setuptools import find_packages def get_version(): - return '0.1.1' + return '0.2.0' def get_requirements(): @@ -18,26 +18,36 @@ def get_long_description(): try: import pypandoc long_description = pypandoc.convert('README.md', 'rst') - except (IOError, ImportError): + + readme_note = """\ + .. note:: + For the latest source, discussion, etc, please visit the + `GitHub repository `_\n\n""" + long_description = readme_note + long_description + with open('README.rst', 'w') as fhan: + fhan.write(long_description) + except (IOError, ImportError) as e: + raise e with open('README.md') as fhan: long_description = fhan.read() return long_description -add_keywords = dict( - entry_points={ - 'console_scripts': ['qmon = qmon.main:main'], - }, ) - -setup( - name='QMon', - description='Redis Monitor - monitor number of items and more for any type of redis queue', - version=get_version(), - packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), - license='GPLv3+', - author='Shubham Chaudhary', - author_email='me@shubhamchaudhary.in', - url='https://github.com/shubhamchaudhary/qmon', - long_description=get_long_description(), - install_requires=get_requirements(), - **add_keywords) +if __name__ == '__main__': + add_keywords = dict( + entry_points={ + 'console_scripts': ['qmon = qmon.main:main'], + }, ) + + setup( + name='QMon', + description='Redis Monitor - monitor number of items and more for any type of redis queue', + version=get_version(), + packages=find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']), + license='GPLv3+', + author='Shubham Chaudhary', + author_email='me@shubhamchaudhary.in', + url='https://github.com/shubhamchaudhary/qmon', + long_description=get_long_description(), + install_requires=get_requirements(), + **add_keywords) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/test_monitor.py b/tests/test_monitor.py new file mode 100644 index 0000000..8e5ac34 --- /dev/null +++ b/tests/test_monitor.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +import unittest +from unittest import mock + +from qmon.monitor import monitor_queue + + +@mock.patch('qmon.monitor.time.sleep') +@mock.patch('qmon.monitor.os.environ') +@mock.patch('qmon.monitor.hypchat.HypChat') +@mock.patch('qmon.monitor.qmon.status.get_items_in_queue') +@mock.patch('qmon.monitor.qmon.status.redis_connection_cached') +class TestMonitor(unittest.TestCase): + def setup_mock(self, mock_hypchat, mock_items_in_queue, mock_os, mock_redis, items_in_queue=None): + if not items_in_queue: + items_in_queue = [0] + mock_items_in_queue.side_effect = items_in_queue + redis = mock.MagicMock() + mock_redis.return_value = redis + mock_os.__getitem__.return_value = 'dummy_token' + client = mock.MagicMock() + room = mock.MagicMock() + client.get_room.return_value = room + mock_hypchat.return_value = client + return room + + def test_monitor_queue__no_items__only_start_notification_and_end_notification( + self, mock_redis, mock_items_in_queue, mock_hypchat, mock_os, mock_sleep + ): + room = self.setup_mock(mock_hypchat, mock_items_in_queue, mock_os, mock_redis, items_in_queue=[0]) + + monitor_queue( + 'dummy_queue', + 'dummy_host', + 10, + 'dummy_room', + stop_once_empty=True, + ) + + room.notification.assert_has_calls([ + mock.call('Starting to monitor queue=dummy_queue on port=10', color='yellow', notify=True), + mock.call('All done for dummy_queue.', color='yellow', notify=True), + ]) + self.assertEqual(2, room.notification.call_count) + + def test_monitor_queue__some_items__send_notification( + self, mock_redis, mock_items_in_queue, mock_hypchat, mock_os, mock_sleep + ): + room = self.setup_mock(mock_hypchat, mock_items_in_queue, mock_os, mock_redis, items_in_queue=[30, 0]) + + monitor_queue( + 'dummy_queue', + 'dummy_host', + 10, + 'dummy_room', + stop_once_empty=True, + ) + + room.notification.assert_has_calls([ + mock.call('dummy_queue queue status: 30', color='gray', notify=False), + mock.call('dummy_queue queue status: 0', color='gray', notify=False), + ]) + self.assertEqual(4, room.notification.call_count)