Skip to content

Commit

Permalink
New --skip and --only toaster options to toast files by regular expre…
Browse files Browse the repository at this point in the history
…ssion.
  • Loading branch information
amorilia committed Dec 28, 2009
1 parent 75cfc3e commit 603b422
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -24,6 +24,8 @@ Release 2.0.6 (in development)

* Bugfix for niftoaster dump spell.

* New --skip and --only toaster options to toast files by regular expression.

Release 2.0.5 (Nov 23, 2009)
============================

Expand Down
6 changes: 0 additions & 6 deletions TODO.rst
Expand Up @@ -2,12 +2,6 @@

.. todo::

- Add option to toaster to skip files that match a regular
expression.

- Add option to toaster to toast only those files that match a
regular expression.

- Add multithreading support to toaster (branching a separate
Python process for each file to save memory).

Expand Down
72 changes: 66 additions & 6 deletions pyffi/spells/__init__.py
Expand Up @@ -141,6 +141,7 @@
import optparse
import os
import os.path
import re # for regex parsing (--skip, --only)
import subprocess
import sys # sys.stdout
import tempfile
Expand Down Expand Up @@ -565,6 +566,14 @@ class Toaster(object):
exclude_types = []
"""Tuple of types corresponding to the exclude key of :attr:`options`."""

only_regexs = []
"""Tuple of regular expressions corresponding to the only key of
:attr:`options`."""

skip_regexs = []
"""Tuple of regular expressions corresponding to the skip key of
:attr:`options`."""

def __init__(self, spellclass=None, options=None):
"""Initialize the toaster.
Expand Down Expand Up @@ -688,10 +697,11 @@ def cli(self):
"""
# parse options and positional arguments
usage = "%prog [options] <spell1> <spell2> ... <file>|<folder>"
description = """Apply the spells <spell1>, <spell2>, and so on,
on <file>, or recursively on <folder>."""
errormessage_numargs = """incorrect number of arguments
(use the --help option for help)"""
description = (
"Apply the spells <spell1>, <spell2>, and so on,"
" on <file>, or recursively on <folder>.")
errormessage_numargs = (
"incorrect number of arguments (use the --help option for help)")

parser = optparse.OptionParser(
usage,
Expand Down Expand Up @@ -767,13 +777,35 @@ def cli(self):
parser.add_option("--series", dest="series",
action="store_true",
help="run spells in series rather than in parallel")
parser.add_option("--skip", dest="skip",
type="string",
action="append",
metavar="SKIP",
help=
"skip all files whose names match the regular"
" expression SKIP (takes precedence over --only);"
" if specified multiple times, the expressions are"
" 'ored' together")
parser.add_option("--only", dest="only",
type="string",
action="append",
metavar="ONLY",
help=
"only toast files whose names (i) match the"
" regular expression ONLY, and (ii) do not match"
" any regular expression specified with --skip;"
" if specified multiple times, the expressions are"
" 'ored' together; if not specified, all files"
" are toasted by default, except those listed"
" with --skip")
parser.set_defaults(raisetesterror=False, verbose=1, pause=False,
exclude=[], include=[], examples=False,
spells=False,
interactive=True,
helpspell=False, dryrun=False, prefix="", arg="",
createpatch=False, applypatch=False, diffcmd="",
series=False)
series=False,
skip=[], only=[])
(options, args) = parser.parse_args()

# convert options to dictionary
Expand All @@ -799,6 +831,12 @@ def cli(self):
getattr(self.FILEFORMAT, block_type)
for block_type in self.options.get("exclude", ()))

# update skip and only regular expressions
self.skip_regexs = tuple(
re.compile(regex) for regex in self.options.get("skip", ()))
self.only_regexs = tuple(
re.compile(regex) for regex in self.options.get("only", ()))

# check errors
if options.createpatch and options.applypatch:
parser.error("options --diff and --patch are mutually exclusive")
Expand Down Expand Up @@ -877,6 +915,23 @@ def cli(self):
if options.pause and options.interactive:
raw_input("Press enter...")

def inspect_filename(self, filename):
"""Returns whether to toast a filename or not, based on
skip_regexs and only_regexs.
"""
if any(regex.match(filename) for regex in self.skip_regexs):
# matches some --skip regex, so do not toast
return False
if not self.only_regexs:
# --only not specified: then by default we match all files
return True
if any(regex.match(filename) for regex in self.only_regexs):
# matches at least one --only regex, so toast
return True
else:
# no --only matches, so do not toast
return False

def toast(self, top):
"""Walk over all files in a directory tree and cast spells
on every file.
Expand Down Expand Up @@ -947,6 +1002,11 @@ def _toast(self, stream):
"""Run toaster on particular stream and data.
Used as helper function.
"""
# inspect the file name
if not self.inspect_filename(stream.name):
self.msg("=== %s (skipped) ===" % stream.name)
return

dryrun = self.options.get("dryrun", False)
prefix = self.options.get("prefix", "")
createpatch = self.options.get("createpatch", False)
Expand All @@ -956,7 +1016,7 @@ def _toast(self, stream):
data = self.FILEFORMAT.Data()

self.msgblockbegin("=== %s ===" % stream.name)
try:
try:
# inspect the file (reads only the header)
data.inspect(stream)

Expand Down
51 changes: 51 additions & 0 deletions tests/nif/niftoaster.txt
Expand Up @@ -55,6 +55,16 @@ Options:
precisely 3 arguments, oldfile, newfile, and
patchfile.
--series run spells in series rather than in parallel
--skip=SKIP skip all files whose names match the regular
expression SKIP (takes precedence over --only); if
specified multiple times, the expressions are 'ored'
together
--only=ONLY only toast files whose names (i) match the regular
expression ONLY, and (ii) do not match any regular
expression specified with --skip; if specified
multiple times, the expressions are 'ored' together;
if not specified, all files are toasted by default,
except those listed with --skip

The --examples switch
---------------------
Expand Down Expand Up @@ -277,6 +287,47 @@ pyffi.toaster:INFO: writing to temporary file
pyffi.toaster:INFO: comparing file sizes
pyffi.toaster:INFO:Finished.

The --skip and --only switches
------------------------------

>>> import sys
>>> sys.path.append("scripts/nif")
>>> import niftoaster
>>> sys.argv = "niftoaster.py --skip .*texture.* --skip .*skin.* --only .*fix.* --only .*center.* check_read tests/nif/".split()
>>> niftoaster.NifToaster().cli() # doctest: +ELLIPSIS
pyffi.toaster:INFO:=== tests/nif/invalid.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_centerradius.nif ===
pyffi.toaster:INFO: --- check_read ---
pyffi.toaster:INFO:=== tests/nif/test_check_tangentspace1.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_check_tangentspace2.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_check_tangentspace3.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_check_tangentspace4.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_convexverticesshape.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_dump_tex.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_fix_clampmaterialalpha.nif ===
pyffi.toaster:INFO: --- check_read ---
pyffi.toaster:INFO:=== tests/nif/test_fix_cleanstringpalette.nif ===
pyffi.toaster:INFO: --- check_read ---
pyffi.toaster:INFO:=== tests/nif/test_fix_detachhavoktristripsdata.nif ===
pyffi.toaster:INFO: --- check_read ---
pyffi.toaster:INFO:=== tests/nif/test_fix_disableparallax.nif ===
pyffi.toaster:INFO: --- check_read ---
pyffi.toaster:INFO:=== tests/nif/test_fix_ffvt3rskinpartition.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_fix_mergeskeletonroots.nif ===
pyffi.toaster:INFO: --- check_read ---
pyffi.toaster:INFO:=== tests/nif/test_fix_tangentspace.nif ===
pyffi.toaster:INFO: --- check_read ---
pyffi.toaster:INFO:=== tests/nif/test_fix_texturepath.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_mopp.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_opt_dupgeomdata.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_opt_dupverts.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_opt_emptyproperties.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_opt_mergeduplicates.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_skincenterradius.nif (skipped) ===
pyffi.toaster:INFO:=== tests/nif/test_vertexcolor.nif (skipped) ===
pyffi.toaster:INFO:Finished.

The check_bhkbodycenter spell
-----------------------------

Expand Down

0 comments on commit 603b422

Please sign in to comment.