Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added re pattern to exclude based on the basename of a file #101

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ Need to exclude directories from being observed or collected for tests?
$ ptw --ignore ./deep-directory --ignore ./integration_tests
```

Need to exclude watching files that match a particular regular expression (e.g., emacs lock files)?

```bash
$ ptw --dont-watch-files '^\.#'
```

See the full list of options:

```
Expand Down Expand Up @@ -127,6 +133,7 @@ Options:
This also enables --wait to prevent pdb interruption.
--spool <delay> Re-run after a delay (in milliseconds), allowing for
more file system events to queue up (default: 200 ms).
--dont-watch-files <str> Regular expression for excluding files from being watched
-p --poll Use polling instead of OS events (useful in VMs).
-v --verbose Increase verbosity of the output.
-q --quiet Decrease verbosity of the output (precedence over -v).
Expand Down
145 changes: 76 additions & 69 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
pytest-watch Continuous pytest runner
=======================================
pytest-watch -- Continuous pytest runner
========================================

`Current version on PyPI <http://pypi.python.org/pypi/pytest-watch/>`__
`Say Thanks! <https://saythanks.io/to/joeyespo>`__
|Current version on PyPI| |Say Thanks!|

**pytest-watch** a zero-config CLI tool that runs
`pytest <http://pytest.org/>`__, and re-runs it when a file in your
Expand All @@ -13,7 +12,7 @@ Motivation
----------

Whether or not you use the test-driven development method, running tests
continuously is far more productive than waiting until youre finished
continuously is far more productive than waiting until you're finished
programming to test your code. Additionally, manually running
``py.test`` each time you want to see if any tests were broken has more
wait-time and cognitive overhead than merely listening for a
Expand All @@ -25,16 +24,16 @@ Installation

.. code:: bash

$ pip install pytest-watch
$ pip install pytest-watch

Usage
-----

.. code:: bash

$ cd myproject
$ ptw
* Watching /path/to/myproject
$ cd myproject
$ ptw
* Watching /path/to/myproject

*Note: It can also be run using its full name ``pytest-watch``.*

Expand All @@ -44,96 +43,99 @@ when tests pass or fail:

- **OSX**

``$ ptw --onpass "say passed" --onfail "say failed"``
``$ ptw --onpass "say passed" --onfail "say failed"``

.. code:: bash
``bash $ ptw --onpass "growlnotify -m \"All tests passed!\"" \ --onfail "growlnotify -m \"Tests failed\""``

$ ptw --onpass "growlnotify -m \"All tests passed!\"" \
--onfail "growlnotify -m \"Tests failed\""

using `GrowlNotify <http://growl.info/downloads#generaldownloads>`__.
using `GrowlNotify <http://growl.info/downloads#generaldownloads>`__.

- **Windows**

.. code:: bat

> ptw --onfail flash
``bat > ptw --onfail flash``

using `Console Flash <http://github.com/joeyespo/console-flash>`__
using `Console Flash <http://github.com/joeyespo/console-flash>`__

You can also run a command before the tests run, e.g. seeding your test
You can also run a command before the tests run, e.g. seeding your test
database:

.. code:: bash

$ ptw --beforerun init_db.py
$ ptw --beforerun init_db.py

Or after they finish, e.g. deleting a sqlite file. Note that this script
Or after they finish, e.g. deleting a sqlite file. Note that this script
receives the exit code of ``py.test`` as an argument.

.. code:: bash

$ ptw --afterrun cleanup_db.py
$ ptw --afterrun cleanup_db.py

You can also use a custom runner script for full ``py.test`` control:

.. code:: bash

$ ptw --runner "python custom_pytest_runner.py"
$ ptw --runner "python custom_pytest_runner.py"

Heres an minimal runner script that runs ``py.test`` and prints its
Here's an minimal runner script that runs ``py.test`` and prints its
exit code:

.. code:: py

# custom_pytest_runner.py
# custom_pytest_runner.py

import sys
import pytest
import sys
import pytest

print('py.test exited with code:', pytest.main(sys.argv[1:]))
print('py.test exited with code:', pytest.main(sys.argv[1:]))

Need to exclude directories from being observed or collected for tests?

.. code:: bash

$ ptw --ignore ./deep-directory --ignore ./integration_tests
$ ptw --ignore ./deep-directory --ignore ./integration_tests

Need to exclude watching files that match a particular regular
expression (e.g., emacs lock files)?

.. code:: bash

$ ptw --dont-watch-files '^\.#'

See the full list of options:

::

$ ptw --help
Usage: ptw [options] [--ignore <dir>...] [<directory>...] [-- <pytest-args>...]

Options:
--ignore <dir> Ignore directory from being watched and during
collection (multi-allowed).
--ext <exts> Comma-separated list of file extensions that can
trigger a new test run when changed (default: .py).
Use --ext=* to allow any file (including .pyc).
--config <file> Load configuration from `file` instead of trying to
locate one of the implicit configuration files.
-c --clear Clear the screen before each run.
-n --nobeep Do not beep on failure.
-w --wait Waits for all tests to complete before re-running.
Otherwise, tests are interrupted on filesystem events.
--beforerun <cmd> Run arbitrary command before tests are run.
--afterrun <cmd> Run arbitrary command on completion or interruption.
The exit code of "py.test" is passed as an argument.
--onpass <cmd> Run arbitrary command on pass.
--onfail <cmd> Run arbitrary command on failure.
--onexit <cmd> Run arbitrary command when exiting pytest-watch.
--runner <cmd> Run a custom command instead of "py.test".
--pdb Start the interactive Python debugger on errors.
This also enables --wait to prevent pdb interruption.
--spool <delay> Re-run after a delay (in milliseconds), allowing for
more file system events to queue up (default: 200 ms).
-p --poll Use polling instead of OS events (useful in VMs).
-v --verbose Increase verbosity of the output.
-q --quiet Decrease verbosity of the output (precedence over -v).
-V --version Print version and exit.
-h --help Print help and exit.
$ ptw --help
Usage: ptw [options] [--ignore <dir>...] [<directory>...] [-- <pytest-args>...]

Options:
--ignore <dir> Ignore directory from being watched and during
collection (multi-allowed).
--ext <exts> Comma-separated list of file extensions that can
trigger a new test run when changed (default: .py).
Use --ext=* to allow any file (including .pyc).
--config <file> Load configuration from `file` instead of trying to
locate one of the implicit configuration files.
-c --clear Clear the screen before each run.
-n --nobeep Do not beep on failure.
-w --wait Waits for all tests to complete before re-running.
Otherwise, tests are interrupted on filesystem events.
--beforerun <cmd> Run arbitrary command before tests are run.
--afterrun <cmd> Run arbitrary command on completion or interruption.
The exit code of "py.test" is passed as an argument.
--onpass <cmd> Run arbitrary command on pass.
--onfail <cmd> Run arbitrary command on failure.
--onexit <cmd> Run arbitrary command when exiting pytest-watch.
--runner <cmd> Run a custom command instead of "py.test".
--pdb Start the interactive Python debugger on errors.
This also enables --wait to prevent pdb interruption.
--spool <delay> Re-run after a delay (in milliseconds), allowing for
more file system events to queue up (default: 200 ms).
--dont-watch-files <str> Regular expression for excluding files from being watched
-p --poll Use polling instead of OS events (useful in VMs).
-v --verbose Increase verbosity of the output.
-q --quiet Decrease verbosity of the output (precedence over -v).
-V --version Print version and exit.
-h --help Print help and exit.

Configuration
-------------
Expand All @@ -144,15 +146,15 @@ persist them in your project. For example:

.. code:: ini

# pytest.ini
# pytest.ini

[pytest]
addopts = --maxfail=2
[pytest]
addopts = --maxfail=2


[pytest-watch]
ignore = ./integration-tests
nobeep = True
[pytest-watch]
ignore = ./integration-tests
nobeep = True

Alternatives
------------
Expand All @@ -163,7 +165,7 @@ Alternatives
make them pass. This can be a speed advantage when trying to get all
tests passing, but leaves out the discovery of new failures until
then. It also drops the colors outputted by py.test, whereas
pytest-watch doesnt.
pytest-watch doesn't.
- `Nosey <http://github.com/joeyespo/nosey>`__ is the original codebase
this was forked from. Nosey runs
`nose <http://nose.readthedocs.org/en/latest/>`__ instead of pytest.
Expand All @@ -183,9 +185,14 @@ file:

.. code:: bash

$ pandoc -t rst -o README.rst README.md
$ pandoc -t rst -o README.rst README.md

If your PR has been waiting a while, feel free to `ping me on
Twitter <https://twitter.com/joeyespo>`__.

Use this software often? :smiley:

.. |Current version on PyPI| image:: http://img.shields.io/pypi/v/pytest-watch.svg
:target: http://pypi.python.org/pypi/pytest-watch/
.. |Say Thanks!| image:: https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg
:target: https://saythanks.io/to/joeyespo
2 changes: 2 additions & 0 deletions pytest_watch/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
This also enables --wait to prevent pdb interruption.
--spool <delay> Re-run after a delay (in milliseconds), allowing for
more file system events to queue up (default: 200 ms).
--dont-watch-files <str> Regular expression for excluding files from being watched
-p --poll Use polling instead of OS events (useful in VMs).
-v --verbose Increase verbosity of the output.
-q --quiet Decrease verbosity of the output (precedence over -v).
Expand Down Expand Up @@ -109,6 +110,7 @@ def main(argv=None):
return watch(entries=directories,
ignore=args['--ignore'],
extensions=extensions,
dont_watch_files=args['--dont-watch-files'],
beep_on_failure=not args['--nobeep'],
auto_clear=args['--clear'],
wait=args['--wait'] or '--pdb' in pytest_args,
Expand Down
17 changes: 14 additions & 3 deletions pytest_watch/watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import sys
import subprocess
import time
import re

from traceback import format_exc

try:
Expand Down Expand Up @@ -77,10 +79,11 @@ class EventListener(FileSystemEventHandler):
"""
Listens for changes to files and re-runs tests after each change.
"""
def __init__(self, extensions=[], event_queue=None):
def __init__(self, extensions=[], event_queue=None, dont_watch_files=None):
super(EventListener, self).__init__()
self.event_queue = event_queue or Queue()
self.extensions = extensions or DEFAULT_EXTENSIONS
self.dont_watch_files = dont_watch_files

def on_any_event(self, event):
"""
Expand All @@ -100,6 +103,14 @@ def on_any_event(self, event):
if not event.is_directory and self.extensions != ALL_EXTENSIONS:
src_ext = os.path.splitext(src_path)[1].lower()
src_included = src_ext in self.extensions

if src_included and self.dont_watch_files:
base = os.path.basename(src_path)
p = re.compile( self.dont_watch_files)
if p.match( base):
print( 'File event matched --dont-watch-files pattern:', self.dont_watch_files, src_path)
src_included = False

dest_included = False
if dest_path:
dest_ext = os.path.splitext(dest_path)[1].lower()
Expand Down Expand Up @@ -216,7 +227,7 @@ def run_hook(cmd, *args):
subprocess.call(command, shell=True)


def watch(entries=[], ignore=[], extensions=[], beep_on_failure=True,
def watch(entries=[], ignore=[], extensions=[], dont_watch_files=None, beep_on_failure=True,
auto_clear=False, wait=False, beforerun=None, afterrun=None,
onpass=None, onfail=None, onexit=None, runner=None, spool=None,
poll=False, verbose=False, quiet=False, pytest_args=[]):
Expand All @@ -237,7 +248,7 @@ def watch(entries=[], ignore=[], extensions=[], beep_on_failure=True,
raise ValueError('Directory not found: ' + entry)

# Setup event handler
event_listener = EventListener(extensions)
event_listener = EventListener(extensions,dont_watch_files=dont_watch_files)

# Setup watchdog
observer = PollingObserver() if poll else Observer()
Expand Down