changeset 5:a825f00fe062

add a different call method, rename the old one, and have a test for it
author Jeff Hammel <jhammel@mozilla.com>
date Thu, 19 May 2011 12:26:29 -0700
parents 5ce55f6c8964
children 886009132409
files pyloader/invoke.py tests/doctest.txt tests/test.py
diffstat 3 files changed, 105 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/pyloader/invoke.py	Tue May 10 19:00:57 2011 -0700
+++ b/pyloader/invoke.py	Thu May 19 12:26:29 2011 -0700
@@ -3,7 +3,7 @@
 from cast import cast
 from cast import str2args
 
-def call(obj, *args, **kwargs):
+def strcall(obj, *args, **kwargs):
     """call a thing with string arguments"""
     inspected = obj
     if inspect.isclass(obj):
@@ -21,6 +21,23 @@
         kwargs = dict([(key, cast(value)) for key, value in kwargs.items()])
     return obj(*args, **kwargs)
 
+def call(obj, **kwargs):
+    """
+    call an object with the subset of kwargs appropriate to the object.
+    this assumes that the obj does not take **kwargs
+    """
+    inspected = obj
+    if inspect.isclass(obj):
+        inspected = obj.__init__ # inspect the ctor
+        args = inspect.getargspec(inspected).args[1:]
+    else:
+        args = inspect.getargspec(obj).args[:]
+    kw = {} # kwargs to invoke obj with
+    for arg in args:
+        if arg in kwargs:
+            kw[arg] = kwargs[arg]
+    return obj(**kw)
+
 def main(args=sys.argv[1:]):
     """CLI entry point"""
     from loader import load
@@ -33,7 +50,7 @@
         parser.exit()
     obj = load(args[0])
     obj_args, obj_kwargs = str2args(' '.join(args[1:]))
-    print call(obj, *obj_args, **obj_kwargs)
+    print strcall(obj, *obj_args, **obj_kwargs)
 
 if __name__ == '__main__':
     main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/doctest.txt	Thu May 19 12:26:29 2011 -0700
@@ -0,0 +1,26 @@
+Test pyloader
+=============
+
+Test the call method::
+
+    >>> from pyloader.invoke import call
+    >>> options = {'badarg': 'bar', 'a': 7, 'b': 9, 'c': 10}
+    >>> def foo(a, b=1, c=2): 
+    ...     return a + 10*b + 100*c
+    >>> foo(7, 9, 10)
+    1097
+
+You can't call methods with bad arguments OOTB in python::
+
+    >>> error = None
+    >>> try:
+    ...     foo(**options)
+    ... except TypeError, e:
+    ...     error = e
+    >>> type(error)
+    <type 'exceptions.TypeError'>
+
+But you can with call::
+
+    >>> call(foo, **options)
+    1097
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test.py	Thu May 19 12:26:29 2011 -0700
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+"""
+doctest runner
+"""
+
+import doctest
+import os
+import sys
+from optparse import OptionParser
+
+
+def run_tests(raise_on_error=False, report_first=False):
+
+    # add results here
+    results = {}
+
+    # doctest arguments
+    directory = os.path.dirname(os.path.abspath(__file__))
+    extraglobs = {'here': directory}
+    doctest_args = dict(extraglobs=extraglobs, raise_on_error=raise_on_error)
+    if report_first:
+        doctest_args['optionflags'] = doctest.REPORT_ONLY_FIRST_FAILURE
+
+    # gather tests
+    tests =  [ test for test in os.listdir(directory)
+               if test.endswith('.txt') ]
+
+    # run the tests
+    for test in tests:
+        try:
+            results[test] = doctest.testfile(test, **doctest_args)
+        except doctest.DocTestFailure, failure:
+            raise
+        except doctest.UnexpectedException, failure:
+            raise failure.exc_info[0], failure.exc_info[1], failure.exc_info[2]
+
+    return results
+
+def main(args=sys.argv[1:]):
+
+    # parse command line args
+    parser = OptionParser()
+    parser.add_option('--raise', dest='raise_on_error',
+                      default=False, action='store_true',
+                      help="raise on first error")
+    parser.add_option('--report-first', dest='report_first',
+                      default=False, action='store_true',
+                      help="report the first error only (all tests will still run)")
+    options, args = parser.parse_args(args)
+
+    # run the tests
+    results = run_tests(report_first=options.report_first,
+                        raise_on_error=options.raise_on_error)
+    if sum([i.failed for i in results.values()]):
+        sys.exit(1) # error
+                
+
+if __name__ == '__main__':
+    main()