Skip to content

Commit

Permalink
Version 0.1.1 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Hui committed Jul 14, 2010
1 parent 4d4b4a9 commit 635f3b9
Show file tree
Hide file tree
Showing 35 changed files with 1,455 additions and 166 deletions.
11 changes: 11 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ChangeLog
=========

0.1.1
-----
- Added traceback for exceptions caught.
- Fixed misnamed methods due to refactoring.

0.1.0
-----
- Initial Release
19 changes: 19 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2010 Jeff Hui

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
7 changes: 7 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
LICENSE.txt
README.rst
setup.py
sniffer/__init__.py
sniffer/main.py
sniffer/modules_restore_point.py
sniffer/runner.py
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include README.rst LICENSE.txt CHANGES.txt
113 changes: 113 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
Overview
========

``sniffer`` is a autotest tool for Python_ using the nosetest_ library.

Sniffer will automatically re-run tests if your code changes. And with another third-party
library (see below), the CPU usage of file system monitoring is reduced in comparison
to pure-python solutions. However, sniffer will still work without any of those libraries.

.. _Python: http://python.org/
.. _nosetest: http://code.google.com/p/python-nose/

Usage
-----

To install::

pip install nose
pip install sniffer

Simply run ``sniffer`` in your project directory.

You can use ``sniffer --help`` for options And like autonose_, you can pass the nose
arguments: ``-x--with-doctest`` or ``-x--config``.

The problem with autonose_, is that the autodetect can be slow to detect changes. This is due
to the pure python implementation - manually walking through the file system to see what's
changed. Although the default install of sniffer shares the same problem, installing a
third-party library can help fix the problem. The library is dependent on your operating system:

- If you use **Linux**, you'll need to install pyinotify_.
- If you use **Windows**, you'll need to install pywin32_.
- If you use **Mac OS X** 10.5+ (Leopard), you'll need to install MacFSEvents_.

**As a word of warning**, Windows and OSX libraries are *untested* as of now. This is because I
haven't gotten around to testing in Windows, and I don't have a Mac :(.

.. _nose: http://code.google.com/p/python-nose/
.. _easy_install: http://pypi.python.org/pypi/setuptools
.. _pip: http://pypi.python.org/pypi/pip
.. _autonose: http://github.com/gfxmonk/autonose
.. _pyinotify: http://trac.dbzteam.org/pyinotify
.. _pywin32: http://sourceforge.net/projects/pywin32/
.. _MacFSEvents: http://pypi.python.org/pypi/MacFSEvents/0.2.1


Other Uses
==========

Running with Other Test Frameworks
----------------------------------

If you want to run another unit testing framework, you can do so by overriding ``sniffer.Sniffer``,
which is the class that handles running tests, or whatever you want. Specifically, you'll want to
override the ``run``, method to configure what you need to be done.

The property, ``test_args``, are arguments gathered through ``--config=blah`` and ``-x.*``
configuration options. You should perform you imports inside the function instead of outside,
to let the class reload the test framework (and reduce possibilities of multiple-run bugs).

After subclassing, set sniffer_cls parameter to your custom class when calling run or main.

Using the FileSystem monitoring code
------------------------------------

If you simply want to use the file system monitor code, ``import sniffer.Scanner``. Behind
the scenes, the library will figure out what libraries are available to use and which
monitor technique to use.

Right now, this is lacking some documentation, but here's a small example.

Creating the scanner is simple::

from sniffer import Scanner

paths = ('/path/to/watch/', '/another/path')
scanner = Scanner(paths)

Here we pass a tuple of paths to monitor. Now we need to get notification when events occur::

# when file is created
scanner.observe('created', lambda path: print "Created", path)

# when file is modified
scanner.observe('modified', lambda path: print "Modified", path)

# when file is deleted
scanner.observe('deleted', lambda path: print "Deleted", path)

# when scanner.loop() is called
scanner.observe('init', lambda: print "Scanner started listening.")

In addition, we can use the same function to listen to multiple events::

# listen to multiple events
scanner.observe(('created', 'modified', 'deleted'), lambda path: "Triggered:", path)

Finally, we start our blocking loop::

# blocks
scanner.loop()

Current Issues
==============

Being relatively new, there are bound to be bugs. Most notable is third-party libraries.
Only the Linux install was tested (being in Ubuntu), so Windows & OSX implementations were
done *without testing*. Patches are welcomed though :)

For linux, there is an exception that is sometimes thrown when terminating.

Currently the program only looks for changes in the current working directory. This isn't the
best solution: it doesn't understand how changes to your source code affects it.
74 changes: 74 additions & 0 deletions build/bdist.linux-i686/egg/sniffer/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
Main runners. Bootloads Sniffer class.
"""
from optparse import OptionParser
from scanner import Scanner
from runner import Sniffer
from metadata import __version__
import os
import sys

__all__ = ['run', 'main']

def run(sniffer_cls=Sniffer, wait_time=0.5, clear=True, args=(), debug=False):
"""
Runs the auto tester loop. Internally, the runner instanciates the sniffer_cls and
scanner class.
``sniffer_cls`` The class to run. Usually this is set to but a subclass of scanner.
Defaults to Sniffer. Sniffer class documentation for more information.
``wait_time`` The time, in seconds, to wait between polls. This is dependent on
the underlying scanner implementation. OS-specific libraries may choose
to ignore this parameter. Defaults to 0.5 seconds.
``clear`` Boolean. Set to True to clear the terminal before running the sniffer,
(alias, the unit tests). Defaults to True.
``args`` The arguments to pass to the sniffer/test runner. Defaults to ().
``debug`` Boolean. Sets the scanner and sniffer in debug mode, printing more internal
information. Defaults to False (and should usually be False).
"""
if debug:
scanner = Scanner(('.',), logger=sys.stdout)
else:
scanner = Scanner(('.',))
sniffer = sniffer_cls(tuple(args), clear, debug)
sniffer.observe_scanner(scanner)
scanner.loop(wait_time)

def main(sniffer_cls=Sniffer, test_args=(), progname=sys.argv[0], args=sys.argv[1:]):
"""
Runs the program. This is used when you want to run this program standalone.
``sniffer_cls`` A class (usually subclassed of Sniffer) that hooks into the scanner and
handles running the test framework. Defaults to Sniffer class.
``test_args`` This function normally extracts args from ``--test-arg ARG`` command. A
preset argument list can be passed. Defaults to an empty tuple.
``program`` Program name. Defaults to sys.argv[0].
``args`` Command line arguments. Defaults to sys.argv[1:]
"""
parser = OptionParser(version="%prog " + __version__)
parser.add_option('-w', '--wait', dest="wait_time", metavar="TIME", default=0.5, type="float",
help="Wait time, in seconds, before possibly rerunning tests. "
"(default: %default)")
parser.add_option('--no-clear', dest="clear_on_run", default=True, action="store_false",
help="Disable the clearing of screen")
parser.add_option('--debug', dest="debug", default=False, action="store_true",
help="Enabled debugging output. (default: %default)")
parser.add_option('-x', '--test-arg', dest="test_args", default=[], action="append",
help="Arguments to pass to nose (use multiple times to pass multiple "
"arguments.)")
(options, args) = parser.parse_args(args)
test_args = test_args + tuple(options.test_args)
if options.debug:
print "Options:", options
print "Test Args:", test_args
try:
print "Starting watch..."
run(sniffer_cls, options.wait_time, options.clear_on_run, test_args, options.debug)
except KeyboardInterrupt:
print "Good bye."
except Exception:
return sys.exit(1)
return sys.exit(0)

if __name__ == '__main__':
main()
30 changes: 30 additions & 0 deletions build/lib.linux-i686-2.6/sniffer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Sniffer - An automatic test runner for Nose
==========
This is a simple program that runs nose anytime the current working
directory (or its subdirectories) have changed (added, modified, or
delete files).
*Note:* By default, sniffer polls the directory tree to determine what changed.
This method was used because of its:
- simplicity: It's pretty easy to write.
- cross-platform: Unlike third-party libs, which only work on one OS.
- standard: Uses only standard modules.
Alternatively, third party modules can be installed to increase performance.
The library to install is dependent on your operating system:
- Linux: install pyinotify
- Windows: install pywin32
- OSX: install MacFSEvents
"""
from metadata import *
from scanner import Scanner
from runner import Sniffer
from main import main, run

if __name__ == '__main__':
main()
74 changes: 74 additions & 0 deletions build/lib.linux-i686-2.6/sniffer/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
Main runners. Bootloads Sniffer class.
"""
from optparse import OptionParser
from scanner import Scanner
from runner import Sniffer
from metadata import __version__
import os
import sys

__all__ = ['run', 'main']

def run(sniffer_cls=Sniffer, wait_time=0.5, clear=True, args=(), debug=False):
"""
Runs the auto tester loop. Internally, the runner instanciates the sniffer_cls and
scanner class.
``sniffer_cls`` The class to run. Usually this is set to but a subclass of scanner.
Defaults to Sniffer. Sniffer class documentation for more information.
``wait_time`` The time, in seconds, to wait between polls. This is dependent on
the underlying scanner implementation. OS-specific libraries may choose
to ignore this parameter. Defaults to 0.5 seconds.
``clear`` Boolean. Set to True to clear the terminal before running the sniffer,
(alias, the unit tests). Defaults to True.
``args`` The arguments to pass to the sniffer/test runner. Defaults to ().
``debug`` Boolean. Sets the scanner and sniffer in debug mode, printing more internal
information. Defaults to False (and should usually be False).
"""
if debug:
scanner = Scanner(('.',), logger=sys.stdout)
else:
scanner = Scanner(('.',))
sniffer = sniffer_cls(tuple(args), clear, debug)
sniffer.observe_scanner(scanner)
scanner.loop(wait_time)

def main(sniffer_cls=Sniffer, test_args=(), progname=sys.argv[0], args=sys.argv[1:]):
"""
Runs the program. This is used when you want to run this program standalone.
``sniffer_cls`` A class (usually subclassed of Sniffer) that hooks into the scanner and
handles running the test framework. Defaults to Sniffer class.
``test_args`` This function normally extracts args from ``--test-arg ARG`` command. A
preset argument list can be passed. Defaults to an empty tuple.
``program`` Program name. Defaults to sys.argv[0].
``args`` Command line arguments. Defaults to sys.argv[1:]
"""
parser = OptionParser(version="%prog " + __version__)
parser.add_option('-w', '--wait', dest="wait_time", metavar="TIME", default=0.5, type="float",
help="Wait time, in seconds, before possibly rerunning tests. "
"(default: %default)")
parser.add_option('--no-clear', dest="clear_on_run", default=True, action="store_false",
help="Disable the clearing of screen")
parser.add_option('--debug', dest="debug", default=False, action="store_true",
help="Enabled debugging output. (default: %default)")
parser.add_option('-x', '--test-arg', dest="test_args", default=[], action="append",
help="Arguments to pass to nose (use multiple times to pass multiple "
"arguments.)")
(options, args) = parser.parse_args(args)
test_args = test_args + tuple(options.test_args)
if options.debug:
print "Options:", options
print "Test Args:", test_args
try:
print "Starting watch..."
run(sniffer_cls, options.wait_time, options.clear_on_run, test_args, options.debug)
except KeyboardInterrupt:
print "Good bye."
except Exception:
return sys.exit(1)
return sys.exit(0)

if __name__ == '__main__':
main()
13 changes: 13 additions & 0 deletions build/lib.linux-i686-2.6/sniffer/metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
__all__ = [
'__author__', '__author_email__', '__copyright__', '__credits__',
'__license__', '__version__'
]


__author__ = "Jeff Hui"
__author_email__ = '[email protected]'
__copyright__ = "Copyright 2010, Jeff Hui"
__credits__ = ["Jeff Hui"]

__license__ = "MIT"
__version__ = "0.1.0"
19 changes: 19 additions & 0 deletions build/lib.linux-i686-2.6/sniffer/modules_restore_point.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import sys

__all__ = ['ModulesRestorePoint']

class ModulesRestorePoint(object):
def __init__(self, sys_modules=sys.modules):
self._saved_modules = None
self._sys_modules = sys_modules
self.save()

def save(self):
"""Saves the currently loaded modules for restore."""
self._saved_modules = set(self._sys_modules.keys())

def restore(self):
"""Unloads all modules that weren't loaded when save_modules was called."""
sys = set(self._sys_modules.keys())
for mod_name in sys.difference(self._saved_modules):
del self._sys_modules[mod_name]
Loading

0 comments on commit 635f3b9

Please sign in to comment.