Commits

Jonathan Eunice committed 8caeffa

updated testing with tox and pytest

  • Participants
  • Parent commits ed475c7

Comments (0)

Files changed (8)

 syntax: glob
 *.swp.{py,txt,html,css,js}
 *.pyc
+.tox
 .DS_Store
 build/*
 dist/*
         
 to consume optional ``prefix`` and ``suffix`` flat arguments.
 
-Next Steps
-==========
- 
-  * add property like setters
-  * extend setters to multiple options
-  * clean up quoteroptions as a result
-
 Notes
 =====
 
    options that combine multiple changes into one compact option. These would
    probably not have stored values themselves. It would require setting the
    "dependent" option values via side-effect rather than functional return values.
+   
+ * Commenced automated multi-version testing with
+   `pytest <http://pypi.python.org/pypi/pytest>`_
+   and `tox <http://pypi.python.org/pypi/tox>`_. Now
+   successfully packaged for, and tested against, Python 2.6, 2.7, 3.2, and 3.3.
  
  * The author, `Jonathan Eunice <mailto:jonathan.eunice@gmail.com>`_ or
    `@jeunice on Twitter <http://twitter.com/jeunice>`_
 ::
 
     pip install options
+
+To ``easy_install`` under a specific Python version (3.3 in this example)::
+
+    python3.3 -m easy_install options
     
-(You may need to prefix this with "sudo " to authorize installation.)
+(You may need to prefix these with "sudo " to authorize installation.)
+
+"""
+Helper functions for function-like objects.
+"""
+
+function_type = type(lambda: True)   # define our own because not def'd in py26
+
+def is_function(obj):
+    """
+    Answers, is obj a function?
+    """
+    return isinstance(obj, function_type)
+    
+def real_func(flike):
+    """
+    Given a function-like object (function or staticmethod), return the real function.
+    """
+    if is_function(flike):
+        return flike
+    elif hasattr(flike, 'im_func'):      # Not clear this still useful
+        return flike.im_func
+    elif hasattr(flike, '__func__'):    # static method
+        return flike.__func__
+    elif hasattr(flike, '__get__'):     # static method in py26
+        return flike.__get__(True)
+    else:
+        raise ValueError("doesn't seem to be a function-like object")
+    
+def func_code(flike):
+    """
+    Given a function like object (function, static method, etc.), return its
+    functional code object. Attempts to bridge the gap between different versions'
+    introspective namings.
+    """
+
+    if hasattr(flike, 'func_code'):  # Python 2.6 or 2.7, most functions
+        return flike.func_code
+    elif hasattr(flike, '__code__'):   # Python 3
+        return flike.__code__
+    else:       
+        raise ValueError("don't know where to find function's code")
 from stuf import orderedstuf
 from chainstuf import chainstuf
 from nulltype import NullType
+from funclike import *
+import sys
 
 Unset      = NullType('Unset')
 Prohibited = NullType('Prohibited')
             continue
         if k not in first:
             keys.append(k)
-    return ', '.join([ "{}={}".format(k, repr(m[k])) for k in keys ])
+    return ', '.join([ "{0}={1}".format(k, repr(m[k])) for k in keys ])
 
 class Options(orderedstuf):
     """
         self._magic = {}
     
     def __repr__(self):
-        return "{}({})".format(self.__class__.__name__, attrs(self))
+        return "{0}({1})".format(self.__class__.__name__, attrs(self))
     
     def set(self, **kwargs):
         # To set values at the base options level, create a temporary next level,
         processing is done to the base Options. These are assumed to have whatever
         adjustments are needed when they are originally set.
         """
-        magical = self.get('_magic', {})
+        self.setdefault('_magic', {})
         for k, v in kwargs.items():
-            if hasattr(v, '__func__'):  # special handling for static methods
-                v = v.__func__
-            magical[k] = v
-        self._magic = magical
+            self._magic[k] = real_func(v)
     
     def magical(self, key):
         """
         # dict handed to Options.__init__ is not in order!
         
         # should there be a leftovers_ok option that raises an error on push()
-        # if there are leftovers?
+        # if there are leftovers?    
 
 class OptionsChain(chainstuf):
     
         magicfn = self._magic.get(key, None)
         if magicfn is None:
             return value
-        argcount = magicfn.func_code.co_argcount
+        argcount = func_code(magicfn).co_argcount
         if argcount == 1:
             return magicfn(value)
         elif argcount == 2:
         elif argcount == 3:
             return magicfn(None, value, self)
         else:
-            raise ValueError('magic function should have 1-3 arguments, not {}'.format(argcount))
+            raise ValueError('magic function should have 1-3 arguments, not {0}'.format(argcount))
 
     def _process(self, base, kwargs):
         """
             n_layers += len(grandpa.maps) - 1
             
         guts = attrs(self, first=list(grandpa.keys()), underscores=True)
-        return "{}({} layers: {})".format(self.__class__.__name__, n_layers, guts)
+        return "{0}({1} layers: {2})".format(self.__class__.__name__, n_layers, guts)
     
     def push(self, *args):
         """
             if v is Unset:
                 del self.maps[0][k]
             elif self[k] is Prohibited:
-                raise KeyError("changes to '{}' are prohibited".format(k))
+                raise KeyError("changes to '{0}' are prohibited".format(k))
             else:
                 self.maps[0][k] = v
                 
+[pytest]
+python_files = test/*.py
+
 
 setup(
     name='options',
-    version=verno("0.027"),
+    version=verno("0.101"),
     author='Jonathan Eunice',
     author_email='jonathan.eunice@gmail.com',
     description='Container for flexible class, instance, and function call options',
     long_description=open('README.rst').read(),
     url='https://bitbucket.org/jeunice/options',
-    py_modules=['options', 'nulltype'],
-    install_requires=['stuf', 'otherstuf', 'six'],
+    py_modules=['options', 'nulltype', 'funclike'],
+    install_requires=['stuf', 'otherstuf>=0.6', 'six'],
+    tests_require = ['tox', 'pytest'],
+    zip_safe = True,
+    keywords='options config configuration parameters arguments',
     classifiers=linelist("""
         Development Status :: 3 - Alpha
         Operating System :: OS Independent
         License :: OSI Approved :: BSD License
         Intended Audience :: Developers
         Programming Language :: Python
+        Programming Language :: Python :: 2.6
+        Programming Language :: Python :: 2.7
+        Programming Language :: Python :: 3.2
+        Programming Language :: Python :: 3.3
         Topic :: Software Development :: Libraries :: Python Modules
     """)
 )

File test/test.py

-
-from testharness import import_from_parent, test_run
-
-import_from_parent()
 
 from options import *
     
     t.set(man='boy')
     assert t.options.man == 'BOY'
     t.set(man='girl')
-    assert t.options.man == 'GIRL'    
-    
-if __name__ == '__main__':
-    test_run()
+    assert t.options.man == 'GIRL'
+    
+[tox]
+envlist = py26, py27, py32, py33
+
+[testenv]
+changedir=test
+deps=pytest       # install pytest in the venvs
+commands=py.test {posargs}