venusian-1.2.0/0000775000175000017500000000000013415173730014514 5ustar tseavertseaver00000000000000venusian-1.2.0/.gitignore0000664000175000017500000000044513155075417016513 0ustar tseavertseaver00000000000000*$py.class *.egg *.egg-info *.pt.py *.pyc *.pyo *.swp *.txt.py *~ .*.swp .cache/ .coverage .tox/ __pycache__/ build/ coverage*.xml coverage-py* coverage.xml dist/ docs/_build/ docs/_themes/ env*/ jyenv/ nosetests-py* nosetests.xml pypyenv/ pytest*.xml venusian.egg-info/ venusian/coverage.xml venusian-1.2.0/COPYRIGHT.txt0000664000175000017500000000015513155075417016632 0ustar tseavertseaver00000000000000Copyright (c) 2011 Agendaless Consulting and Contributors. (http://www.agendaless.com), All Rights Reserved venusian-1.2.0/rtd.txt0000664000175000017500000000001313155075417016044 0ustar tseavertseaver00000000000000-e .[docs] venusian-1.2.0/setup.cfg0000664000175000017500000000042313415173730016334 0ustar tseavertseaver00000000000000[easy_install] zip_ok = false [bdist_wheel] universal = 1 [tools:pytest] ignore = venusian/tests/fixtures/ testpaths = venusian/tests/ [aliases] dev = develop easy_install venusian[testing] docs = develop easy_install venusian[docs] [egg_info] tag_build = tag_date = 0 venusian-1.2.0/docs/0000775000175000017500000000000013415173730015444 5ustar tseavertseaver00000000000000venusian-1.2.0/docs/conf.py0000664000175000017500000001552413155075417016756 0ustar tseavertseaver00000000000000# -*- coding: utf-8 -*- # # venusian documentation build configuration file # # This file is execfile()d with the current directory set to its containing # dir. # # The contents of this file are pickled, so don't put values in the # namespace that aren't pickleable (module imports are okay, they're # removed automatically). # # All configuration values have a default value; values that are commented # out serve to show the default value. import sys import os import datetime import pkg_resources # Add and use Pylons theme if 'sphinx-build' in ' '.join(sys.argv): # protect against dumb importers from subprocess import call, Popen, PIPE p = Popen('which git', shell=True, stdout=PIPE) git = p.stdout.read().strip() cwd = os.getcwd() _themes = os.path.join(cwd, '_themes') if not os.path.isdir(_themes): call([git, 'clone', 'git://github.com/Pylons/pylons_sphinx_theme.git', '_themes']) else: os.chdir(_themes) call([git, 'checkout', 'master']) call([git, 'pull']) os.chdir(cwd) sys.path.append(os.path.abspath('_themes')) parent = os.path.dirname(os.path.dirname(__file__)) sys.path.append(os.path.abspath(parent)) wd = os.getcwd() os.chdir(parent) sys.path.append(parent) # Options for HTML output # ----------------------- sys.path.append(os.path.abspath('_themes')) html_theme_path = ['_themes'] html_theme = 'pylons' html_theme_options = dict(github_url='https://github.com/Pylons/venusian') # If your extensions are in another directory, add it here. If the # directory is relative to the documentation root, use os.path.abspath to # make it absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['.templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'venusian' year = datetime.datetime.now().year copyright = '2012-%s Pylons Project ' % year # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = pkg_resources.get_distribution('venusian').version # The full version, including alpha/beta/rc tags. release = version # There are two options for replacing |today|: either, you set today to # some non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be # searched for source files. #exclude_dirs = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' exclude_patterns = ['_themes/README.rst',] # Options for HTML output # ----------------------- html_theme_options = { 'github_url': 'https://github.com/Pylons/venusian' } # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. #html_style = 'pyramid.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as # html_title. #html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. #html_logo = '.static/logo_hi.gif' # The name of an image file (within the static path) to use as favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or # 32x32 pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) # here, relative to this directory. They are copied after the builtin # static files, so a file named "default.css" will overwrite the builtin # "default.css". #html_static_path = ['.static'] # If not '', a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as # _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages # will contain a tag referring to it. The value of this option must # be the base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'atemplatedoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, document class [howto/manual]). latex_documents = [ ('index', 'atemplate.tex', 'venusian Documentation', 'Pylons Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the # top of the title page. latex_logo = '.static/logo_hi.gif' # For "manual" documents, if this is true, then toplevel headings are # parts, not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True venusian-1.2.0/docs/Makefile0000664000175000017500000000521513155075417017113 0ustar tseavertseaver00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -W SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." text: mkdir -p _build/text _build/doctrees $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) _build/text @echo @echo "Build finished. The HTML pages are in _build/text." pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web _build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex cp _static/*.png _build/latex ./convert_images.sh cp _static/latex-warning.png _build/latex cp _static/latex-note.png _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub @echo @echo "Build finished. The epub file is in _build/epub." venusian-1.2.0/docs/glossary.rst0000664000175000017500000000052713155075417020051 0ustar tseavertseaver00000000000000.. _glossary: Glossary ======== .. glossary:: :sorted: scan Walk a module or package executing callbacks defined by venusian-aware decorators along the way. Martian The package venusian was inspired by, part of the :term:`Grok` project. Grok A Zope-based `web framework `. venusian-1.2.0/docs/.static/0000775000175000017500000000000013415173730017011 5ustar tseavertseaver00000000000000venusian-1.2.0/docs/.static/logo_hi.gif0000664000175000017500000000752413155075417021134 0ustar tseavertseaver00000000000000GIF89a3BBBًzι<<>>ԾԾտѻԿLKIԶľBBA@@?|||??>AAAȼEEEHGEڷcccյųMMM˽͸̾vur@??YYYxCCCþihc:::ӽ!,3 H*\ȰÇ#JHŋ3jȱdžIrCȲ˅.ȜI^F͟?ѬIhE*XMJu! j%D. MjJ?_Ŗ@D|"݅t['Y EHG8._7CW"7)Tv'r֦+RcN+'n c ZMd#g׎9f _HrA\FSAUR`rMq`E"Wҥ&7ĄB5BU t fM?~O?5WGՂ|,ȢC4D54Xc8+pA , 9$ C- Cy  & E(q0 t$ ܴf(D.@>yXM4+ 7H‚JhTBE BZ0'@YN16tQbP X*:ab lTåuQf jJ dBDNC tP<P _PPRlpʴ ;D2$$aQCk72/Dx0(g& J#̐A:r AD!8(a 8žT4΃»@ 3(h.@B氽T@L;|B١sC@khQ:QęCt 0`=GJX-W|A !,@3zrasA rnqRh2[K\ Djئ<9E*Z8p@!p.n B x| ՐtS%=a,$`h %@A'6xB †) 6x;50bLPxF+90bA00)4l#QC-D&lp}PGvG<ڠ'cC!u!,._h ZN_a $ @Ҏ+P2R(ՇMosP/`qg.b Ed_ D 2,%vvm!^+P/yz"<S?yH€WPR C4;@`b9-@_0jj`!`\8lyxw WH0 `cPaQ 0$ W}GJ l   aCЍpqQ@PA dvyGP pYP E" (P{ 0@@ 1 F@d s @ 900) ٟ ;venusian-1.2.0/docs/.static/repoze.css0000664000175000017500000000077513155075417021044 0ustar tseavertseaver00000000000000@import url('default.css'); body { background-color: #006339; } div.document { background-color: #dad3bd; } div.sphinxsidebar h3, h4, h5, a { color: #127c56 !important; } div.related { color: #dad3bd !important; background-color: #00744a; } div.related a { color: #dad3bd !important; } /* override the justify text align of the default */ div.body p { text-align: left !important; } /* fix google chrome
 tag renderings */

pre {
   line-height: normal !important;
}
venusian-1.2.0/docs/index.rst0000664000175000017500000004735613155075417017330 0ustar  tseavertseaver00000000000000.. _venusian:

Venusian
========

Venusian is a library which allows you to defer the action of
decorators.  Instead of taking actions when a function, method, or
class decorator is executed at import time, you can defer the action
until a separate "scan" phase.

This library is most useful for framework authors.  It is compatible with
CPython versions 2.7, and 3.3+.  It is also known to work on PyPy 1.5.

.. note::

   The name "Venusian" is a riff on a library named :term:`Martian`
   (which had its genesis in the :term:`Grok` web framework), from
   which the idea for Venusian was stolen.  Venusian is similar to
   Martian, but it offers less functionality, making it slightly
   simpler to use.

Overview
--------

Offering a decorator that wraps a function, method, or class can be a
convenience to your framework's users.  But the very purpose of a
decorator makes it likely to impede testability of the function or
class it decorates: use of a decorator often prevents the function it
decorates from being called with the originally passed arguments, or a
decorator may modify the return value of the decorated function.  Such
modifications to behavior are "hidden" in the decorator code itself.

For example, let's suppose your framework defines a decorator function
named ``jsonify`` which can wrap a function that returns an arbitrary
Python data structure and renders it to a JSON serialization:

.. code-block:: python
   :linenos:

    import json

    def jsonify(wrapped):
        def json_wrapper(request):
            result = wrapped(request)
            dumped = json.dumps(result)
            return dumped
        return json_wrapper

Let's also suppose a user has written an application using your
framework, and he has imported your jsonify decorator function, and
uses it to decorate an application function:

.. code-block:: python
   :linenos:

    from theframework import jsonify

    @jsonify
    def logged_in(request):
       return {'result':'Logged in'}

As a result of an import of the module containing the ``logged_in``
function, a few things happen:

- The user's ``logged_in`` function is replaced by the
  ``json_wrapper`` function.

- The only reference left to the original ``logged_in`` function is
  inside the frame stack of the call to the ``jsonify`` decorator.

This means, from the perspective of the application developer that the
original ``logged_in`` function has effectively "disappeared" when it
is decorated with your ``jsonify`` decorator.  Without bothersome
hackery, it can no longer be imported or retrieved by its original
author.

More importantly, it also means that if the developer wants to unit
test the ``logged_in`` function, he'll need to do so only indirectly:
he'll need to call the ``json_wrapper`` wrapper decorator function and
test that the json returned by the function contains the expected
values.  This will often imply using the ``json.loads`` function to
turn the result of the function *back* into a Python dictionary from
the JSON representation serialized by the decorator.

If the developer is a stickler for unit testing, however, he'll want
to test *only* the function he has actually defined, not the wrapper
code implied by the decorator your framework has provided.  This is
the very definition of unit testing (testing a "unit" without any
other integration with other code).  In this case, it is also more
convenient for him to be able to test the function without the
decorator: he won't need to use the ``json.loads`` function to turn
the result back into a dictionary to make test assertions against.
It's likely such a developer will try to find ways to get at the
original function for testing purposes.

To do so, he might refactor his code to look like this:

.. code-block:: python
   :linenos:

    from theframework import jsonify

    @jsonify
    def logged_in(request):
       return _logged_in(request)

    def _logged_in(request):
       return {'result':'Logged in'}

Then in test code he might import only the ``_logged_in`` function
instead of the decorated ``logged_in`` function for purposes of unit
testing.  In such a scenario, the concentious unit testing app
developer has to define two functions for each decorated function.  If
you're thinking "that looks pretty tedious", you're right.

To give the intrepid tester an "out", you might be tempted as a
framework author to leave a reference to the original function around
somewhere that the unit tester can import and use only for testing
purposes.  You might modify the ``jsonify`` decorator like so in order
to do that:

.. code-block:: python
   :linenos:

    import json
    def jsonify(wrapped):
        def json_wrapper(request):
            result = wrapped(request)
            dumped = json.dumps(result)
            return dumped
        json_wrapper.original_function = wrapped
        return json_wrapper

The line ``json_wrapper.original_function = wrapped`` is the
interesting one above.  It means that the application developer has a
chance to grab a reference to his original function:

.. code-block:: python
   :linenos:

    from myapp import logged_in
    result = logged_in.original_func(None)
    self.assertEqual(result['result'], 'Logged in')

That works.  But it's just a little weird.  Since the ``jsonify``
decorator function has been imported by the developer from a module in
your framework, the developer probably shouldn't really need to know
how it works.  If he needs to read its code, or understand
documentation about how the decorator functions for testing purposes,
your framework *might* be less valuable to him on some level.  This is
arguable, really.  If you use some consistent pattern like this for
all your decorators, it might be a perfectly reasonable solution.

However, what if the decorators offered by your framework were passive
until activated explicitly?  This is the promise of using Venusian
within your decorator implementations.  You may use Venusian within
your decorators to associate a wrapped function, class, or method with
a callback.  Then you can return the originally wrapped function.
Instead of your decorators being "active", the callback associated
with the decorator is passive until a "scan" is initiated.

Using Venusian
--------------

The most basic use of Venusian within a decorator implementation is
demonstrated below.

.. code-block:: python
   :linenos:

   import venusian

   def jsonify(wrapped):
       def callback(scanner, name, ob):
           print 'jsonified'
       venusian.attach(wrapped, callback)
       return wrapped

As you can see, this decorator actually calls into venusian, but then
simply returns the wrapped object.  Effectively this means that this
decorator is "passive" when the module is imported.

Usage of the decorator:

.. code-block:: python
   :linenos:

   from theframework import jsonify

   @jsonify
   def logged_in(request):
       return {'result':'Logged in'}

Note that when we import and use the function, the fact that it is
decorated with the ``jsonify`` decorator is immaterial.  Our decorator
doesn't actually change its behavior.

.. code-block:: python
   :linenos:

   >>> from theapp import logged_in
   >>> logged_in()
   {'result':'Logged in'}
   >>>

This is the intended result.  During unit testing, the original
function can be imported and tested despite the fact that it has been
wrapped with a decorator.

However, we can cause something to happen when we invoke a :term:`scan`.

.. code-block:: python
   :linenos:

   import venusian
   import theapp

   scanner = venusian.Scanner()
   scanner.scan(theapp)

Above we've imported a module named ``theapp``. The ``logged_in``
function which we decorated with our ``jsonify`` decorator lives in
this module.  We've also imported the :mod:`venusian` module, and
we've created an instance of the :class:`venusian.Scanner` class.
Once we've created the instance of :class:`venusian.Scanner`, we
invoke its :meth:`venusian.Scanner.scan` method, passing the
``theapp`` module as an argument to the method.

Here's what happens as a result of invoking the
:meth:`venusian.Scanner.scan` method:

#. Every object defined at module scope within the ``theapp`` Python
   module will be inspected to see if it has had a Venusian callback
   attached to it.

#. For every object that *does* have a Venusian callback attached to
   it, the callback is called.

We could have also passed the ``scan`` method a Python *package*
instead of a module.  This would recursively import each module in the
package (as well as any modules in subpackages), looking for
callbacks.

.. note:: During scan, the only Python files that are processed are
   Python *source* (``.py``) files.  Compiled Python files (``.pyc``,
   ``.pyo`` files) without a corresponding source file are ignored.

In our case, because the callback we defined within the ``jsonify``
decorator function prints ``jsonified`` when it is invoked, which
means that the word ``jsonified`` will be printed to the console when
we cause :meth:`venusian.Scanner.scan` to be invoked.  How is this
useful?  It's not!  At least not yet.  Let's create a more realistic
example.

Let's change our ``jsonify`` decorator to perform a more useful action
when a scan is invoked by changing the body of its callback.

.. code-block:: python
   :linenos:

   import venusian

   def jsonify(wrapped):
       def callback(scanner, name, ob):
           def jsonified(request):
               result = wrapped(request)
               return json.dumps(result)
           scanner.registry.add(name, jsonified)
       venusian.attach(wrapped, callback)
       return wrapped

Now if we invoke a scan, we'll get an error:

.. code-block:: python
   :linenos:

   import venusian
   import theapp

   scanner = venusian.Scanner()
   scanner.scan(theapp)

   AttributeError: Scanner has no attribute 'registry'.

The :class:`venusian.Scanner` class constructor accepts any key-value
pairs; for each key/value pair passed to the scanner's constructor, an
attribute named after the key which points at the value is added to
the scanner instance.  So when you do:

.. code-block:: python
   :linenos:

   import venusian
   scanner = venusian.Scanner(a=1)

Thereafter, ``scanner.a`` will equal the integer 1.

Any number of key-value pairs can be passed to a scanner.  The purpose
of being able to pass arbitrary key/value pairs to a scanner is to
allow cooperating decorator callbacks to access these values: each
callback is passed the ``scanner`` constructed when a scan is invoked.

Let's fix our example by creating an object named ``registry`` that
we'll pass to our scanner's constructor:

.. code-block:: python
   :linenos:

   import venusian
   import theapp

   class Registry(object):
       def __init__(self):
          self.registered = []

       def add(self, name, ob):
          self.registered.append((name, ob))

   registry = Registry()
   scanner = venusian.Scanner(registry=registry)
   scanner.scan(theapp)

At this point, we have a system which, during a scan, for each object
that is wrapped with a Venusian-aware decorator, a tuple will be
appended to the ``registered`` attribute of a ``Registry`` object.
The first element of the tuple will be the decorated object's name,
the second element of the tuple will be a "truly" decorated object.
In our case, this will be a jsonify-decorated callable.

Our framework can then use the information in the registry to decide
which view function to call when a request comes in.

Venusian callbacks must accept three arguments:

``scanner``

  This will be the instance of the scanner that has had its ``scan``
  method invoked.

``name``

  This is the module-level name of the object being decorated.

``ob``

  This is the object being decorated if it's a function or an
  instance; if the object being decorated is a *method*, however, this
  value will be the *class*.

If you consider that the decorator and the scanner can cooperate, and
can perform arbitrary actions together, you can probably imagine a
system where a registry will be populated that informs some
higher-level system (such as a web framework) about the available
decorated functions.

Scan Categories
---------------

Because an application may use two separate Venusian-using frameworks,
Venusian allows for the concept of "scan categories".

The :func:`venusian.attach` function accepts an additional argument
named ``category``.

For example:

.. code-block:: python
   :linenos:

   import venusian

   def jsonify(wrapped):
       def callback(scanner, name, ob):
           def jsonified(request):
               result = wrapped(request)
               return json.dumps(result)
           scanner.registry.add(name, jsonified)
       venusian.attach(wrapped, callback, category='myframework')
       return wrapped

Note the ``category='myframework'`` argument in the call to
:func:`venusian.attach`.  This tells Venusian to attach the callback
to the wrapped object under the specific scan category
``myframework``.  The default scan category is ``None``.

Later, during :meth:`venusian.Scanner.scan`, a user can choose to
activate all the decorators associated only with a particular set of
scan categories by passing a ``categories`` argument.  For example:

.. code-block:: python
   :linenos:

   import venusian
   scanner = venusian.Scanner(a=1)
   scanner.scan(theapp, categories=('myframework',))

The default ``categories`` argument is ``None``, which means activate
all Venusian callbacks during a scan regardless of their category.

``onerror`` Scan Callback
-------------------------

.. versionadded:: 1.0

By default, when Venusian scans a package, it will propagate all exceptions
raised while attempting to import code.  You can use an ``onerror`` callback
argument to :meth:`venusian.Scanner.scan` to change this behavior.

The ``onerror`` argument should either be ``None`` or a callback function
which behaves the same way as the ``onerror`` callback function described in
http://docs.python.org/library/pkgutil.html#pkgutil.walk_packages .

Here's an example ``onerror`` callback that ignores all :exc:`ImportError`
exceptions:

.. code-block:: python
   :linenos:

     import sys
     def onerror(name):
         if not issubclass(sys.exc_info()[0], ImportError):
             raise # reraise the last exception

Here's how we'd use this callback:

.. code-block:: python
   :linenos:

   import venusian
   scanner = venusian.Scanner()
   scanner.scan(theapp, onerror=onerror)

The ``onerror`` callback should execute ``raise`` at some point if any
exception is to be propagated, otherwise it can simply return.  The ``name``
passed to ``onerror`` is the module or package dotted name that could not be
imported due to an exception.


``ignore`` Scan Argument
------------------------

.. versionadded:: 1.0a3

The ``ignore`` to ``scan`` allows you to ignore certain modules, packages, or
global objects during a scan.  It should be a sequence containing strings
and/or callables that will be used to match against the full dotted name of
each object encountered during the scanning process.  If the ignore value you
provide matches a package name, global objects contained by that package as
well any submodules and subpackages of the package (and any global objects
contained by them) will be ignored.  If the ignore value you provide matches
a module name, any global objects in that module will be ignored.  If the
ignore value you provide matches a global object that lives in a package or
module, only that particular global object will be ignored.

The sequence can contain any of these three types of objects:

- A string representing a full dotted name.  To name an object by dotted
  name, use a string representing the full dotted name.  For example, if you
  want to ignore the ``my.package`` package and any of its subobjects during
  the scan, pass ``ignore=['my.package']``.  If the string matches a global
  object (e.g. ``ignore=['my.package.MyClass']``), only that object will be
  ignored and the rest of the objects in the module or package that contains
  the object will be processed.

- A string representing a relative dotted name.  To name an object relative
  to the ``package`` passed to this method, use a string beginning with a
  dot.  For example, if the ``package`` you've passed is imported as
  ``my.package``, and you pass ``ignore=['.mymodule']``, the
  ``my.package.mymodule`` module and any of its subobjects will be omitted
  during scan processing.  If the string matches a global object
  (e.g. ``ignore=['my.package.MyClass']``), only that object will be ignored
  and the rest of the objects in the module or package that contains the
  object will be processed.

- A callable that accepts a full dotted name string of an object as its
  single positional argument and returns ``True`` or ``False``.  If the
  callable returns ``True`` or anything else truthy, the module, package, or
  global object is ignored, if it returns ``False`` or anything else falsy,
  it is not ignored.  If the callable matches a package name, the package as
  well as any of that package's submodules and subpackages (recursively) will
  be ignored.  If the callable matches a module name, that module and any of
  its contained global objects will be ignored.  If the callable matches a
  global object name, only that object name will be ignored.  For example, if
  you want to skip all packages, modules, and global objects that have a full
  dotted name that ends with the word "tests", you can use
  ``ignore=[re.compile('tests$').search]``.

Here's an example of how we might use the ``ignore`` argument to ``scan`` to
ignore an entire package (and any of its submodules and subpackages) by
absolute dotted name:

.. code-block:: python
   :linenos:

   import venusian
   scanner = venusian.Scanner()
   scanner.scan(theapp, ignore=['theapp.package'])

Here's an example of how we might use the ``ignore`` argument to ``scan`` to
ignore an entire package (and any of its submodules and subpackages) by
relative dotted name (``theapp.package``):

.. code-block:: python
   :linenos:

   import venusian
   scanner = venusian.Scanner()
   scanner.scan(theapp, ignore=['.package'])

Here's an example of how we might use the ``ignore`` argument to ``scan`` to
ignore a particular class object:

.. code-block:: python
   :linenos:

   import venusian
   scanner = venusian.Scanner()
   scanner.scan(theapp, ignore=['theapp.package.MyClass'])

Here's an example of how we might use the ``ignore`` argument to ``scan`` to
ignore any module, package, or global object that has a name which ends
with the string ``tests``:

.. code-block:: python
   :linenos:

   import re
   import venusian
   scanner = venusian.Scanner()
   scanner.scan(theapp, ignore=[re.compile('tests$').search])

You can mix and match the three types in the list.  For example,
``scanner.scan(my, ignore=['my.package', '.someothermodule',
re.compile('tests$').search])`` would cause ``my.package`` (and all its
submodules and subobjects) to be ignored, ``my.someothermodule`` to be
ignored, and any modules, packages, or global objects found during the scan
that have a full dotted path that ends with the word ``tests`` to be ignored
beneath the ``my`` package.

Packages and modules matched by any ignore in the list will not be imported,
and their top-level code will not be run as a result.


Limitations and Audience
------------------------

Venusian is not really a tool that is maximally useful to an
application developer.  It would be a little silly to use it every
time you needed a decorator.  Instead, it's most useful for framework
authors, in order to be able to say to their users "the frobozz
decorator doesn't change the output of your function at all" in
documentation.  This is a lot easier than telling them how to test
methods/functions/classes decorated by each individual decorator
offered by your frameworks.

API Documentation / Glossary
----------------------------

.. toctree::
   :maxdepth: 2

   api.rst
   glossary.rst

Indices and tables
------------------

* :ref:`glossary`
* :ref:`modindex`
* :ref:`search`
venusian-1.2.0/docs/api.rst0000664000175000017500000000045313155075417016755 0ustar  tseavertseaver00000000000000API Documentation for Venusian
==============================

.. automodule:: venusian

  .. autoclass:: Scanner

     .. automethod:: scan

  .. autoclass:: AttachInfo

  .. autofunction:: attach(wrapped, callback, category=None, name=None)

  .. autoclass:: lift

  .. autoclass:: onlyliftedfrom
venusian-1.2.0/PKG-INFO0000664000175000017500000002513613415173730015620 0ustar  tseavertseaver00000000000000Metadata-Version: 2.1
Name: venusian
Version: 1.2.0
Summary: A library for deferring decorator actions
Home-page: https://pylonsproject.org
Author: Chris McDonough, Agendaless Consulting
Author-email: pylons-devel@googlegroups.com
License: BSD-derived (http://www.repoze.org/LICENSE.txt)
Description: venusian
        ========
        
        .. image:: https://travis-ci.org/Pylons/venusian.png?branch=master
                :target: https://travis-ci.org/Pylons/venusian
        
        .. image:: https://readthedocs.org/projects/venusian/badge/?version=latest
                :target: https://docs.pylonsproject.org/projects/venusian/en/latest/
                :alt: Documentation Status
        
        Venusian is a library which allows framework authors to defer
        decorator actions.  Instead of taking actions when a function (or
        class) decorator is executed at import time, you can defer the action
        usually taken by the decorator until a separate "scan" phase.
        
        See the "docs" directory of the package or the online documentation at
        https://docs.pylonsproject.org/projects/venusian/en/latest/
        
        
        1.2.0 (2019-01-08)
        ------------------
        
        - Add support for Python 3.7.
        
        - Drop support for Python 3.3.
        
        1.1.0 (2017-04-24)
        ------------------
        
        - Updated to using py.test instead of nosetest, and added support for Python
          3.4 -> 3.6
        
        - Make scanning more resilient of metaclasses that return proxies for any
          attribute access.
        
        - Fix bug where using the same venusian decorator on both a class and its
          methods would cause the method decorations to be ignored. See
          https://github.com/Pylons/venusian/issues/40
        
        - Drop support for Python 2.6.
        
        - Drop support for Python 3.2:  it is no longer supported by current
          packaging / CI tools.
        
        - Support loaders that require the module name as argument to their
          ``get_filename()`` method. This fixes problems with zipped packages
          on Python 3.
        
        - Micro-optimization when ignores are used (see
          https://github.com/Pylons/venusian/pull/20).
        
        - A tox run now combines coverage between Py2 and Py3.
        
        1.0 (2014-06-30)
        ----------------
        
        - Fix an issue under PyPy > 2.0 where attached decorators may not be found.
        
        - Drop support of Python 2.4 / 2.5 / Jython.
        
        - Add ``lift`` and ``onlyliftedfrom`` class decorators to allow for inheritance
          of venusian decorators attached to superclass methods.  See the API
          documentation for more information.
        
        - Fix bug where otherwise undecorated subclass of a superclass that had
          venusian decorators on it would inherit its superclass' decorations.
          Venusian decorators should have never been inherited implicitly.  See
          https://github.com/Pylons/venusian/issues/11#issuecomment-4977352
        
        1.0a8 (2013-04-15)
        ------------------
        
        - Pass ``ignore`` argument along recursively to ``walk_packages`` so custom
          ignore functions will ignore things recursively.  See
          https://github.com/Pylons/venusian/pull/16
        
        - Don't run tox tests under Python 2.4 anymore (tox no longer supports 2.4).
        
        1.0a7 (2012-08-25)
        ------------------
        
        - Venusian now works on Python 3.3b2+ (importlib-based).
        
        - Use nose-exclude instead of relying on fragile module-scope code to ensure
          we don't get errors resulting from import of fixture code during
          "nosetests".
        
        - Bug fix: no longer suppress ``ImportError`` while scanning by default.  If
          you want to suppress ``ImportError`` while scanning, you'll now need use an
          ``onerror`` callback as described in the documentation.
        
        1.0a6 (2012-04-23)
        ------------------
        
        - Don't ignore decorated objects within their original locations if they
          happen to be imported into another module (remove ``seen`` set from invoke
          in venusian scanning).  See https://github.com/Pylons/venusian/pull/13 .
        
        1.0a5 (2012-04-21)
        ------------------
        
        - Slightly less sucky way to ignore objects during scanning that are only
          imported into a module but not actually defined there.  See 1.0a4 change
          notes for rationale.  Now instead of checking whether the module of the
          *scanned object* matches the module being scanned, we check whether the
          module of the *Venusian attachment* matches the module being scanned.  This
          allows some genuine uses of imported objects as Venusian scan targets while
          preventing inappropriate double-scanning of objects that have a venusian
          attachment which just happen to be imported into other scanned modules.
        
        - Add ``dev`` and ``docs`` setup.py commands (ala Pyramid).
        
        1.0a4 (2012-04-16)
        ------------------
        
        - Attempt to ignore objects during scanning that are only imported into a
          module but not actually defined there.  This is a semantics change, but
          it's the right thing to do, because I found myself facing a situation like
          this::
        
            # in a module named "one"
        
            from two import anotheradecoratedthing
            @adecorator
            def adecoratedthing(): pass
        
            # and scanning both modules
            scan('one')
            scan('two')
        
          In this case you'd wind up with two repeated registrations for
          "anotherdecoratedthing", which isn't what anyone expects.
        
        1.0a3 (2012-02-08)
        ------------------
        
        - Add an ``ignore`` argument to the ``scan`` method of a ``Scanner``.  This
          argument allows a user to ignore packages, modules, and global objects by
          name during a ``scan``.  See the "ignore Scan Argument" in the narrative
          documentation for more details.
        
        1.0a2 (2011-09-02)
        ------------------
        
        - Close ImpLoader file handle to avoid resource warnings on Python 3.
        
        1.0a1 (2011-08-27)
        ------------------
        
        - Python 3 compatibility.
        
        - Allow an ``onerror`` callback to be passed to ``Scanner.scan()``.
        
        0.9 (2011-06-18)
        ----------------
        
        - Prevent corner case scan-time exception when trying to introspect insane
          module-scope objects.  See https://github.com/Pylons/venusian/issues/5 .
        
        0.8 (2011-04-30)
        ----------------
        
        - Normal "setup.py test" can't support running the venusian tests under py
          2.4 or 2.5; when it scans the 'classdecorators' fixture, it barfs.  To get
          around this, we used to depend on ``nose`` in ``setup_requires`` and tell
          "setup.py test" to use nose by setting test_suite to "nose.collector" but
          we can't anymore because folks use Venusian in systems which install from
          pip bundles; pip bundles do not support setup_requires.  So, sorry, we're
          painted into a corner; at this point you just have to know to install nose
          and run "setup.py nosetests" rather than "setup.py test".  Or just run
          "tox" which tests it under all Pythons.
        
        0.7 (2011-03-16)
        ----------------
        
        - Use Pylons theme in documentation.
        
        - Fix orphaned pyc test on pypy.
        
        - Fix GitHub Issue #1: subclasses of decorated classes that do not
          have any decorations should not inherit the decorations of their
          parent classes. 
        
        - Fix GitHub Issue #2: scans should only "find" each object once per
          scan, regardless of how many modules that object is imported into.
        
        0.6 (2011-01-09)
        ----------------
        
        - Some metaclasses (Elixir's) don't raise an AttributeError when asked for a
          nonexistent attribute during a scan.  We now catch all exceptions when
          interrogating an object for ``__venusian_callbacks__`` rather than just
          AttributeError.
        
        0.5 (2010-12-19)
        ----------------
        
        - Make ``codeinfo`` attribute available as an attribute of the AttachInfo
          object. It will be a tuple in the form ``(filename, lineno, function,
          sourceline)`` representing the context of the venusian decorator.  Eg.
          ``('/home/chrism/projects/venusian/tests/test_advice.py', 81,
          'testCallInfo', 'add_handler(foo, bar)')``
        
        0.4 (2010-09-03)
        ----------------
        
        - Bug fix: when a venusian decorator used as a class decorator was
          used against both a class *and* a subclass of that class, the
          superclass and subclass would effectively share the same set of
          callbacks.  This was not the intent: each class declaration should
          have its own local set of callbacks; callbacks added via decorations
          should not be inherited, and a superclass should not receive its
          subclass' decorations.
        
        - Arrange test fixtures into a single directory.
        
        0.3 (2010-06-24)
        ----------------
        
        - Ignore orphaned modules (``.pyc`` or ``.pyo`` files without a
          corresponding ``.py`` file) during a scan.
        
        0.2 (2010-04-18)
        ----------------
        
        - Add the concept of scan categories (see the "Scan Categories"
          section of the documentation) to allow an application to make use of
          more than one Venusian-using framework simultaneously.
        
        0.1 (2010-02-15)
        ----------------
        
        - Initial release.
        
Keywords: web wsgi zope
Platform: UNKNOWN
Classifier: Development Status :: 6 - Mature
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Provides-Extra: docs
Provides-Extra: testing
venusian-1.2.0/tox.ini0000664000175000017500000000226213415166305016031 0ustar  tseavertseaver00000000000000[tox]
envlist =
    py27,py34,py35,py36,py37,pypy,
    docs,
    {py2,py3}-cover,coverage,
skip_missing_interpreters = True

[testenv]
# Most of these are defaults but if you specify any you can't fall back
# to defaults for others.
basepython =
    py27: python2.7
    py34: python3.4
    py35: python3.5
    py36: python3.6
    py37: python3.7
    pypy: pypy
    py2: python2.7
    py3: python3.5

commands =
    pip install -q venusian[testing]
    py.test --junitxml=pytest-{envname}.xml {posargs:}

[testenv:docs]
basepython = python3.5
whitelist_externals = make
commands =
    pip install venusian[docs]
    make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E"

[py-cover]
commands =
    pip install venusian[testing]
    py.test --cov-report term-missing --cov=venusian

[testenv:py2-cover]
commands =
    {[py-cover]commands}
setenv =
    COVERAGE_FILE=.coverage.py2

[testenv:py3-cover]
commands =
    {[py-cover]commands}
setenv =
    COVERAGE_FILE=.coverage.py3

[testenv:coverage]
basepython = python3.5
commands =
    coverage erase
    coverage combine
    coverage xml
    coverage report --show-missing --fail-under=100
deps =
    coverage
setenv =
    COVERAGE_FILE=.coverage

venusian-1.2.0/venusian/0000775000175000017500000000000013415173730016344 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/__init__.py0000664000175000017500000005473013155075417020472 0ustar  tseavertseaver00000000000000import imp
from inspect import getmembers, getmro, isclass
from pkgutil import iter_modules
import sys

from venusian.compat import (
    is_nonstr_iter,
    INT_TYPES,
    )

from venusian.advice import getFrameInfo

ATTACH_ATTR = '__venusian_callbacks__'
LIFTONLY_ATTR = '__venusian_liftonly_callbacks__'

class Scanner(object):
    def __init__(self, **kw):
        self.__dict__.update(kw)

    def scan(self, package, categories=None, onerror=None, ignore=None):
        """ Scan a Python package and any of its subpackages.  All
        top-level objects will be considered; those marked with
        venusian callback attributes related to ``category`` will be
        processed.

        The ``package`` argument should be a reference to a Python
        package or module object.

        The ``categories`` argument should be sequence of Venusian
        callback categories (each category usually a string) or the
        special value ``None`` which means all Venusian callback
        categories.  The default is ``None``.

        The ``onerror`` argument should either be ``None`` or a callback
        function which behaves the same way as the ``onerror`` callback
        function described in
        http://docs.python.org/library/pkgutil.html#pkgutil.walk_packages .
        By default, during a scan, Venusian will propagate all errors that
        happen during its code importing process, including
        :exc:`ImportError`.  If you use a custom ``onerror`` callback, you
        can change this behavior.
        
        Here's an example ``onerror`` callback that ignores
        :exc:`ImportError`::

            import sys
            def onerror(name):
                if not issubclass(sys.exc_info()[0], ImportError):
                    raise # reraise the last exception

        The ``name`` passed to ``onerror`` is the module or package dotted
        name that could not be imported due to an exception.

        .. versionadded:: 1.0
           the ``onerror`` callback

        The ``ignore`` argument allows you to ignore certain modules,
        packages, or global objects during a scan.  It should be a sequence
        containing strings and/or callables that will be used to match
        against the full dotted name of each object encountered during a
        scan.  The sequence can contain any of these three types of objects:

        - A string representing a full dotted name.  To name an object by
          dotted name, use a string representing the full dotted name.  For
          example, if you want to ignore the ``my.package`` package *and any
          of its subobjects or subpackages* during the scan, pass
          ``ignore=['my.package']``.

        - A string representing a relative dotted name.  To name an object
          relative to the ``package`` passed to this method, use a string
          beginning with a dot.  For example, if the ``package`` you've
          passed is imported as ``my.package``, and you pass
          ``ignore=['.mymodule']``, the ``my.package.mymodule`` mymodule *and
          any of its subobjects or subpackages* will be omitted during scan
          processing.

        - A callable that accepts a full dotted name string of an object as
          its single positional argument and returns ``True`` or ``False``.
          For example, if you want to skip all packages, modules, and global
          objects with a full dotted path that ends with the word "tests", you
          can use ``ignore=[re.compile('tests$').search]``.  If the callable
          returns ``True`` (or anything else truthy), the object is ignored,
          if it returns ``False`` (or anything else falsy) the object is not
          ignored.  *Note that unlike string matches, ignores that use a
          callable don't cause submodules and subobjects of a module or
          package represented by a dotted name to also be ignored, they match
          individual objects found during a scan, including packages,
          modules, and global objects*.

        You can mix and match the three types of strings in the list.  For
        example, if the package being scanned is ``my``,
        ``ignore=['my.package', '.someothermodule',
        re.compile('tests$').search]`` would cause ``my.package`` (and all
        its submodules and subobjects) to be ignored, ``my.someothermodule``
        to be ignored, and any modules, packages, or global objects found
        during the scan that have a full dotted name that ends with the word
        ``tests`` to be ignored.

        Note that packages and modules matched by any ignore in the list will
        not be imported, and their top-level code will not be run as a result.

        A string or callable alone can also be passed as ``ignore`` without a
        surrounding list.
        
        .. versionadded:: 1.0a3
           the ``ignore`` argument
        """

        pkg_name = package.__name__

        if ignore is not None and not is_nonstr_iter(ignore):
            ignore = [ignore]
        elif ignore is None:
            ignore = []

        # non-leading-dotted name absolute object name
        str_ignores = [ign for ign in ignore if isinstance(ign, str)]
        # leading dotted name relative to scanned package
        rel_ignores = [ign for ign in str_ignores if ign.startswith('.')]
        # non-leading dotted names
        abs_ignores = [ign for ign in str_ignores if not ign.startswith('.')]
        # functions, e.g. re.compile('pattern').search
        callable_ignores = [ign for ign in ignore if callable(ign)]
        
        def _ignore(fullname):
            for ign in rel_ignores:
                if fullname.startswith(pkg_name + ign):
                    return True
            for ign in abs_ignores:
                # non-leading-dotted name absolute object name
                if fullname.startswith(ign):
                    return True
            for ign in callable_ignores:
                if ign(fullname):
                    return True
            return False


        def invoke(mod_name, name, ob):

            fullname = mod_name + '.' + name

            if _ignore(fullname):
                return

            category_keys = categories
            try:
                # Some metaclasses do insane things when asked for an
                # ``ATTACH_ATTR``, like not raising an AttributeError but
                # some other arbitary exception.  Some even shittier
                # introspected code lets us access ``ATTACH_ATTR`` far but
                # barfs on a second attribute access for ``attached_to``
                # (still not raising an AttributeError, but some other
                # arbitrary exception).  Finally, the shittiest code of all
                # allows the attribute access of the ``ATTACH_ATTR`` *and*
                # ``attached_to``, (say, both ``ob.__getattr__`` and
                # ``attached_categories.__getattr__`` returning a proxy for
                # any attribute access), which either a) isn't callable or b)
                # is callable, but, when called, shits its pants in an
                # potentially arbitrary way (although for b, only TypeError
                # has been seen in the wild, from PyMongo).  Thus the
                # catchall except: return here, which in any other case would
                # be high treason.
                attached_categories = getattr(ob, ATTACH_ATTR)
                if not attached_categories.attached_to(mod_name, name, ob):
                    return
            except:
                return
            if category_keys is None:
                category_keys = list(attached_categories.keys())
                try:
                    # When metaclasses return proxies for any attribute access
                    # the list may contain keys of different types which might
                    # not be sortable.  In that case we can just return,
                    # because we're not dealing with a proper venusian
                    # callback.
                    category_keys.sort()
                except TypeError:  # pragma: no cover
                    return
            for category in category_keys:
                callbacks = attached_categories.get(category, [])
                try:
                    # Metaclasses might trick us by reaching this far and then
                    # fail with too little values to unpack.
                    for callback, cb_mod_name, liftid, scope in callbacks:
                        if cb_mod_name != mod_name:
                            # avoid processing objects that were imported into
                            # this module but were not actually defined there
                            continue
                        callback(self, name, ob)
                except ValueError:
                    continue

        for name, ob in getmembers(package):
            # whether it's a module or a package, we need to scan its
            # members; walk_packages only iterates over submodules and
            # subpackages
            invoke(pkg_name, name, ob)

        if hasattr(package, '__path__'): # package, not module
            results = walk_packages(package.__path__, package.__name__+'.',
                                    onerror=onerror, ignore=_ignore)

            for importer, modname, ispkg in results:
                loader = importer.find_module(modname)
                if loader is not None: # happens on pypy with orphaned pyc
                    try:
                        if hasattr(loader, 'etc'):
                            # python < py3.3
                            module_type = loader.etc[2]
                        else: # pragma: no cover
                            # py3.3b2+ (importlib-using)
                            module_type = imp.PY_SOURCE
                            get_filename = getattr(loader, 'get_filename', None)
                            if get_filename is None:
                                get_filename = loader._get_filename
                            try:
                                fn = get_filename(modname)
                            except TypeError:
                                fn = get_filename()
                            if fn.endswith(('.pyc', '.pyo', '$py.class')):
                                module_type = imp.PY_COMPILED
                        # only scrape members from non-orphaned source files
                        # and package directories
                        if module_type in (imp.PY_SOURCE, imp.PKG_DIRECTORY):
                            # NB: use __import__(modname) rather than
                            # loader.load_module(modname) to prevent
                            # inappropriate double-execution of module code
                            try:
                                __import__(modname)
                            except Exception:
                                if onerror is not None:
                                    onerror(modname)
                                else:
                                    raise
                            module = sys.modules.get(modname)
                            if module is not None:
                                for name, ob in getmembers(module, None):
                                    invoke(modname, name, ob)
                    finally:
                        if  ( hasattr(loader, 'file') and
                              hasattr(loader.file,'close') ):
                            loader.file.close()

class AttachInfo(object):
    """
    An instance of this class is returned by the
    :func:`venusian.attach` function.  It has the following
    attributes:

    ``scope``

      One of ``exec``, ``module``, ``class``, ``function call`` or
      ``unknown`` (each a string).  This is the scope detected while
      executing the decorator which runs the attach function.

    ``module``

      The module in which the decorated function was defined.

    ``locals``

      A dictionary containing decorator frame's f_locals.

    ``globals``

      A dictionary containing decorator frame's f_globals.

    ``category``

      The ``category`` argument passed to ``attach`` (or ``None``, the
      default).

    ``codeinfo``

      A tuple in the form ``(filename, lineno, function, sourceline)``
      representing the context of the venusian decorator used.  Eg.
      ``('/home/chrism/projects/venusian/tests/test_advice.py', 81,
      'testCallInfo', 'add_handler(foo, bar)')``
      
    """
    def __init__(self, **kw):
        self.__dict__.update(kw)

class Categories(dict):
    def __init__(self, attached_to):
        super(dict, self).__init__()
        if isinstance(attached_to, tuple):
            self.attached_id = attached_to
        else:
            self.attached_id = id(attached_to)
        self.lifted = False

    def attached_to(self, mod_name, name, obj):
        if isinstance(self.attached_id, INT_TYPES):
            return self.attached_id == id(obj)
        return self.attached_id == (mod_name, name)

def attach(wrapped, callback, category=None, depth=1, name=None):
    """ Attach a callback to the wrapped object.  It will be found
    later during a scan.  This function returns an instance of the
    :class:`venusian.AttachInfo` class.

    ``category`` should be ``None`` or a string representing a decorator
    category name.

    ``name`` should be ``None`` or a string representing a subcategory within
    the category.  This will be used by the ``lift`` class decorator to
    determine if decorations of a method should be inherited or overridden.
    """

    frame = sys._getframe(depth+1)
    scope, module, f_locals, f_globals, codeinfo = getFrameInfo(frame)
    module_name = getattr(module, '__name__', None)
    wrapped_name = getattr(wrapped, '__name__', None)
    class_name = codeinfo[2]

    liftid = '%s %s' % (wrapped_name, name)
    
    if scope == 'class':
        # we're in the midst of a class statement
        categories = f_locals.get(ATTACH_ATTR, None)
        if categories is None or not categories.attached_to(
            module_name, class_name, None
            ):
            categories = Categories((module_name, class_name))
            f_locals[ATTACH_ATTR] = categories
        callbacks = categories.setdefault(category, [])
    else:
        categories = getattr(wrapped, ATTACH_ATTR, None)
        if categories is None or not categories.attached_to(
            module_name, wrapped_name, wrapped
            ):
            # if there aren't any attached categories, or we've retrieved
            # some by inheritance, we need to create new ones
            categories = Categories(wrapped)
            setattr(wrapped, ATTACH_ATTR, categories)
        callbacks = categories.setdefault(category, [])

    callbacks.append((callback, module_name, liftid, scope))

    return AttachInfo(
        scope=scope,
        module=module,
        locals=f_locals,
        globals=f_globals,
        category=category,
        codeinfo=codeinfo,
        )

def walk_packages(path=None, prefix='', onerror=None, ignore=None):
    """Yields (module_loader, name, ispkg) for all modules recursively
    on path, or, if path is None, all accessible modules.

    'path' should be either None or a list of paths to look for
    modules in.

    'prefix' is a string to output on the front of every module name
    on output.

    Note that this function must import all *packages* (NOT all
    modules!) on the given path, in order to access the __path__
    attribute to find submodules.

    'onerror' is a function which gets called with one argument (the name of
    the package which was being imported) if any exception occurs while
    trying to import a package.  If no onerror function is supplied, any
    exception is exceptions propagated, terminating the search.

    'ignore' is a function fed a fullly dotted name; if it returns True, the
    object is skipped and not returned in results (and if it's a package it's
    not imported).

    Examples:

    # list all modules python can access
    walk_packages()

    # list all submodules of ctypes
    walk_packages(ctypes.__path__, ctypes.__name__+'.')

    # NB: we can't just use pkgutils.walk_packages because we need to ignore
    # things
    """

    def seen(p, m={}):
        if p in m: # pragma: no cover
            return True
        m[p] = True

    # iter_modules is nonrecursive
    for importer, name, ispkg in iter_modules(path, prefix):

        if ignore is not None and ignore(name):
            # if name is a package, ignoring here will cause
            # all subpackages and submodules to be ignored too
            continue

        # do any onerror handling before yielding

        if ispkg:
            try:
                __import__(name)
            except Exception:
                if onerror is not None:
                    onerror(name)
                else:
                    raise
            else:
                yield importer, name, ispkg
                path = getattr(sys.modules[name], '__path__', None) or []

                # don't traverse path items we've seen before
                path = [p for p in path if not seen(p)]

                for item in walk_packages(path, name+'.', onerror, ignore):
                    yield item
        else:
            yield importer, name, ispkg


class lift(object):
    """
    A class decorator which 'lifts' superclass venusian configuration
    decorations into subclasses.  For example::

      from venusian import lift
      from somepackage import venusian_decorator

      class Super(object):
          @venusian_decorator()
          def boo(self): pass

          @venusian_decorator()
          def hiss(self): pass

          @venusian_decorator()
          def jump(self): pass
              
      @lift()
      class Sub(Super):
          def boo(self): pass

          def hiss(self): pass
          
          @venusian_decorator()
          def smack(self): pass

    The above configuration will cause the callbacks of seven venusian
    decorators.  The ones attached to Super.boo, Super.hiss, and Super.jump
    *plus* ones attached to Sub.boo, Sub.hiss, Sub.hump and Sub.smack.

    If a subclass overrides a decorator on a method, its superclass decorators
    will be ignored for the subclass.  That means that in this configuration::

      from venusian import lift
      from somepackage import venusian_decorator

      class Super(object):
          @venusian_decorator()
          def boo(self): pass

          @venusian_decorator()
          def hiss(self): pass

      @lift()
      class Sub(Super):

          def boo(self): pass
          
          @venusian_decorator()
          def hiss(self): pass

    Only four, not five decorator callbacks will be run: the ones attached to
    Super.boo and Super.hiss, the inherited one of Sub.boo and the
    non-inherited one of Sub.hiss.  The inherited decorator on Super.hiss will
    be ignored for the subclass.

    The ``lift`` decorator takes a single argument named 'categories'.  If
    supplied, it should be a tuple of category names.  Only decorators
    in this category will be lifted if it is suppled.
          
    """
    def __init__(self, categories=None):
        self.categories = categories

    def __call__(self, wrapped):
        if not isclass(wrapped):
            raise RuntimeError(
                '"lift" only works as a class decorator; you tried to use '
                'it against %r' % wrapped
                )
        frame = sys._getframe(1)
        scope, module, f_locals, f_globals, codeinfo = getFrameInfo(frame)
        module_name = getattr(module, '__name__', None)
        newcategories = Categories(wrapped)
        newcategories.lifted = True
        for cls in getmro(wrapped):
            attached_categories = cls.__dict__.get(ATTACH_ATTR, None)
            if attached_categories is None:
                attached_categories = cls.__dict__.get(LIFTONLY_ATTR, None)
            if attached_categories is not None:
                for cname, category in attached_categories.items():
                    if cls is not wrapped:
                        if self.categories and not cname in self.categories:
                            continue
                    callbacks = newcategories.get(cname, [])
                    newcallbacks = []
                    for cb, _, liftid, cscope in category:
                        append = True
                        toappend = (cb, module_name, liftid, cscope)
                        if cscope == 'class':
                            for ncb, _, nliftid, nscope in callbacks:
                                if (nscope == 'class' and liftid == nliftid):
                                    append = False
                        if append:
                            newcallbacks.append(toappend)
                    newcategory = list(callbacks) + newcallbacks
                    newcategories[cname] = newcategory
                if attached_categories.lifted:
                    break
        if newcategories: # if it has any keys
            setattr(wrapped, ATTACH_ATTR, newcategories)
        return wrapped
        
class onlyliftedfrom(object):
    """
    A class decorator which marks a class as 'only lifted from'.  Decorations
    made on methods of the class won't have their callbacks called directly,
    but classes which inherit from only-lifted-from classes which also use the
    ``lift`` class decorator will use the superclass decoration callbacks.

    For example::
    
      from venusian import lift, onlyliftedfrom
      from somepackage import venusian_decorator

      @onlyliftedfrom()
      class Super(object):
          @venusian_decorator()
          def boo(self): pass

          @venusian_decorator()
          def hiss(self): pass

      @lift()
      class Sub(Super):

          def boo(self): pass
          
          def hiss(self): pass

    Only two decorator callbacks will be run: the ones attached to Sub.boo and
    Sub.hiss.  The inherited decorators on Super.boo and Super.hiss will be
    not be registered.
    """
    def __call__(self, wrapped):
        if not isclass(wrapped):
            raise RuntimeError(
                '"onlyliftedfrom" only works as a class decorator; you tried '
                'to use it against %r' % wrapped
                )
        cats = getattr(wrapped, ATTACH_ATTR, None)
        class_name = wrapped.__name__
        module_name = wrapped.__module__
        key = (module_name, class_name, wrapped)
        if cats is None or not cats.attached_to(*key):
            # we either have no categories or our categories are defined
            # in a superclass
            return
        delattr(wrapped, ATTACH_ATTR)
        setattr(wrapped, LIFTONLY_ATTR, cats)
        return wrapped
        
venusian-1.2.0/venusian/advice.py0000664000175000017500000000543613155075417020165 0ustar  tseavertseaver00000000000000##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Class advice.

This module was adapted from 'protocols.advice', part of the Python
Enterprise Application Kit (PEAK).  Please notify the PEAK authors
(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
Zope-specific changes are required, so that the PEAK version of this module
can be kept in sync.

PEAK is a Python application framework that interoperates with (but does
not require) Zope 3 and Twisted.  It provides tools for manipulating UML
models, object-relational persistence, aspect-oriented programming, and more.
Visit the PEAK home page at http://peak.telecommunity.com for more information.

$Id: advice.py 25177 2004-06-02 13:17:31Z jim $
"""

import inspect
import sys

def getFrameInfo(frame):
    """Return (kind,module,locals,globals) for a frame

    'kind' is one of "exec", "module", "class", "function call", or "unknown".
    """

    f_locals = frame.f_locals
    f_globals = frame.f_globals

    sameNamespace = f_locals is f_globals
    hasModule = '__module__' in f_locals
    hasName = '__name__' in f_globals

    sameName = hasModule and hasName
    sameName = sameName and f_globals['__name__']==f_locals['__module__']

    module = hasName and sys.modules.get(f_globals['__name__']) or None

    namespaceIsModule = module and module.__dict__ is f_globals

    frameinfo = inspect.getframeinfo(frame)
    try:
        sourceline = frameinfo[3][0].strip()
    except: #pragma NO COVER
        # dont understand circumstance here, 3rdparty code without comment
        sourceline = frameinfo[3]

    codeinfo = frameinfo[0], frameinfo[1], frameinfo[2], sourceline

    if not namespaceIsModule: #pragma no COVER
        # some kind of funky exec
        kind = "exec" # don't know how to repeat this scenario
    elif sameNamespace and not hasModule:
        kind = "module"
    elif sameName and not sameNamespace:
        kind = "class"
    elif not sameNamespace:
        kind = "function call"
    else: #pragma NO COVER
        # How can you have f_locals is f_globals, and have '__module__' set?
        # This is probably module-level code, but with a '__module__' variable.
        kind = "unknown"
    return kind, module, f_locals, f_globals, codeinfo
venusian-1.2.0/venusian/compat.py0000664000175000017500000000054013155075417020204 0ustar  tseavertseaver00000000000000import sys

PY3 = sys.version_info[0] == 3

if PY3: # pragma: no cover
    def is_nonstr_iter(v):
        if isinstance(v, str):
            return False
        return hasattr(v, '__iter__')
else:
    def is_nonstr_iter(v):
        return hasattr(v, '__iter__')
    
if PY3: # pragma: no cover
    INT_TYPES = (int,)
else:
    INT_TYPES = (int, long)
venusian-1.2.0/venusian/tests/0000775000175000017500000000000013415173730017506 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/__init__.py0000664000175000017500000000001213155075417021614 0ustar  tseavertseaver00000000000000# package
venusian-1.2.0/venusian/tests/test_venusian.py0000664000175000017500000012113513155075417022756 0ustar  tseavertseaver00000000000000import unittest
import sys
import re
import os
import contextlib


@contextlib.contextmanager
def with_entry_in_sys_path(entry):
    """Context manager that temporarily puts an entry at head of sys.path"""
    sys.path.insert(0, entry)
    yield
    sys.path.remove(entry)


def zip_file_in_sys_path():
    """Context manager that puts zipped.zip at head of sys.path"""
    zip_pkg_path = os.path.join(os.path.dirname(__file__),
                                'fixtures', 'zipped.zip')
    return with_entry_in_sys_path(zip_pkg_path)


class _Test(object):
    def __init__(self):
        self.registrations = []
    def __call__(self, **kw):
        self.registrations.append(kw)

class TestScanner(unittest.TestCase):
    def _makeOne(self, **kw):
        from venusian import Scanner
        return Scanner(**kw)

    def test_package(self):
        from venusian.tests.fixtures import one
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(one)
        self.assertEqual(len(test.registrations), 6)
        test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module2 import function as func2
        from venusian.tests.fixtures.one.module import inst as inst1
        from venusian.tests.fixtures.one.module2 import inst as inst2
        from venusian.tests.fixtures.one.module import Class as Class1
        from venusian.tests.fixtures.one.module2 import Class as Class2

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], Class1)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'Class')
        self.assertEqual(test.registrations[1]['ob'], Class2)
        self.assertEqual(test.registrations[1]['method'], True)

        self.assertEqual(test.registrations[2]['name'], 'function')
        self.assertEqual(test.registrations[2]['ob'], func1)
        self.assertEqual(test.registrations[2]['function'], True)

        self.assertEqual(test.registrations[3]['name'], 'function')
        self.assertEqual(test.registrations[3]['ob'], func2)
        self.assertEqual(test.registrations[3]['function'], True)
        
        self.assertEqual(test.registrations[4]['name'], 'inst')
        self.assertEqual(test.registrations[4]['ob'], inst1)
        self.assertEqual(test.registrations[4]['instance'], True)

        self.assertEqual(test.registrations[5]['name'], 'inst')
        self.assertEqual(test.registrations[5]['ob'], inst2)
        self.assertEqual(test.registrations[5]['instance'], True)

    def test_module_in_zip(self):
        with zip_file_in_sys_path():
            import moduleinzip
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(moduleinzip)
        self.assertEqual(len(test.registrations), 3)
        test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module2 import function as func2
        from venusian.tests.fixtures.one.module import inst as inst1
        from venusian.tests.fixtures.one.module2 import inst as inst2
        from venusian.tests.fixtures.one.module import Class as Class1
        from venusian.tests.fixtures.one.module2 import Class as Class2

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], moduleinzip.Class)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], moduleinzip.function)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'inst')
        self.assertEqual(test.registrations[2]['ob'], moduleinzip.inst)
        self.assertEqual(test.registrations[2]['instance'], True)

    def test_package_in_zip(self):
        with zip_file_in_sys_path():
            import packageinzip
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(packageinzip)

    def test_package_with_orphaned_pyc_file(self):
        # There is a module2.pyc file in the "pycfixtures" package; it
        # has no corresponding .py source file.  Such orphaned .pyc
        # files should be ignored during scanning.
        from venusian.tests.fixtures import pyc
        test = _Test()
        scanner = self._makeOne(test=test) 
        scanner.scan(pyc)
        self.assertEqual(len(test.registrations), 4)
        test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
        from venusian.tests.fixtures.pyc.module import function as func1
        from venusian.tests.fixtures.pyc.module import inst as inst1
        from venusian.tests.fixtures.pyc.module import Class as Class1
        from venusian.tests.fixtures.pyc import subpackage

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], Class1)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func1)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'inst')
        self.assertEqual(test.registrations[2]['ob'], inst1)
        self.assertEqual(test.registrations[2]['instance'], True)

        self.assertEqual(test.registrations[3]['name'], 'pkgfunction')
        self.assertEqual(test.registrations[3]['ob'], subpackage.pkgfunction)
        self.assertEqual(test.registrations[3]['function'], True)

    def test_module(self):
        from venusian.tests.fixtures.one import module
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(module)
        self.assertEqual(len(test.registrations), 3)
        test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module import inst as inst1
        from venusian.tests.fixtures.one.module import Class as Class1

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], Class1)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func1)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'inst')
        self.assertEqual(test.registrations[2]['ob'], inst1)
        self.assertEqual(test.registrations[2]['instance'], True)

    def test_ignore_imported(self):
        # even though "twofunction" is imported into "one", it should not
        # be registered, because it's only imported in one and not defined
        # there
        from venusian.tests.fixtures.importonly import one
        from venusian.tests.fixtures.importonly import two
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(one)
        self.assertEqual(len(test.registrations), 1)
        scanner.scan(two)
        self.assertEqual(len(test.registrations), 2)

    def test_dont_ignore_legit_decorators(self):
        # make sure venusian picks up other decorated things from
        # imported modules when the whole package is scanned
        from venusian.tests.fixtures import import_and_scan
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(import_and_scan)
        self.assertEqual(len(test.registrations), 2)

    def test_one_category(self):
        from venusian.tests.fixtures import category
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(category, categories=('mycategory',))
        self.assertEqual(len(test.registrations), 1)
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], category.function)
        self.assertEqual(test.registrations[0]['function'], True)

    def test_all_categories_implicit(self):
        from venusian.tests.fixtures import category
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(category)
        self.assertEqual(len(test.registrations), 2)
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], category.function)
        self.assertEqual(test.registrations[0]['function'], True)
        self.assertEqual(test.registrations[1]['name'], 'function2')
        self.assertEqual(test.registrations[1]['ob'], category.function2)
        self.assertEqual(test.registrations[1]['function'], True)

    def test_all_categories_explicit(self):
        from venusian.tests.fixtures import category
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(category, categories=('mycategory', 'mycategory2'))
        self.assertEqual(len(test.registrations), 2)
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], category.function)
        self.assertEqual(test.registrations[0]['function'], True)
        self.assertEqual(test.registrations[1]['name'], 'function2')
        self.assertEqual(test.registrations[1]['ob'], category.function2)
        self.assertEqual(test.registrations[1]['function'], True)

    def test_decorations_arent_inherited(self):
        from venusian.tests.fixtures import inheritance
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(inheritance)
        self.assertEqual(test.registrations, [
            dict(name='Parent',
                    ob=inheritance.Parent),
            ])

    def test_classdecorator(self):
        from venusian.tests.fixtures import classdecorator
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(classdecorator)
        test.registrations.sort(key=lambda x: (x['name'], x['ob'].__module__))
        self.assertEqual(len(test.registrations), 2)
        self.assertEqual(test.registrations[0]['name'], 'SubClass')
        self.assertEqual(test.registrations[0]['ob'],
                            classdecorator.SubClass)
        self.assertEqual(test.registrations[0]['subclass'], True)
        self.assertEqual(test.registrations[1]['name'], 'SuperClass')
        self.assertEqual(test.registrations[1]['ob'],
                            classdecorator.SuperClass)
        self.assertEqual(test.registrations[1]['superclass'], True)

    def test_class_and_method_decorator(self):
        from venusian.tests.fixtures import class_and_method
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(class_and_method)
        self.assertEqual(len(test.registrations), 2)
        self.assertEqual(test.registrations[0]['name'], 'ClassWithMethod')
        self.assertEqual(test.registrations[0]['ob'],
                         class_and_method.ClassWithMethod)
        self.assertEqual(test.registrations[0]['method'], True)
        self.assertEqual(test.registrations[1]['name'], 'ClassWithMethod')
        self.assertEqual(test.registrations[1]['ob'],
                         class_and_method.ClassWithMethod)
        self.assertEqual(test.registrations[1]['class_'], True)

    def test_scan_only_finds_classdecoration_once(self):
        from venusian.tests.fixtures import two
        from venusian.tests.fixtures.two.mod1 import Class
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(two)
        self.assertEqual(test.registrations, [
            dict(name='Class',
                    ob=Class),
            ])
            
    def test_importerror_during_scan_default_onerror(self):
        from venusian.tests.fixtures import importerror
        test = _Test()
        scanner = self._makeOne(test=test)
        # without a custom onerror, scan will propagate the importerror from
        # will_cause_import_error
        self.assertRaises(ImportError, scanner.scan, importerror)

    def test_importerror_during_scan_default_onerror_with_ignore(self):
        from venusian.tests.fixtures import importerror
        test = _Test()
        scanner = self._makeOne(test=test)
        # scan will ignore the errors from will_cause_import_error due
        # to us choosing to ignore that package
        scanner.scan(
            importerror,
            ignore='venusian.tests.fixtures.importerror.will_cause_import_error')

    def test_importerror_during_scan_custom_onerror(self):
        from venusian.tests.fixtures import importerror
        test = _Test()
        scanner = self._makeOne(test=test)
        # with this custom onerror, scan will not propagate the importerror
        # from will_raise_importerror
        def onerror(name):
            if not issubclass(sys.exc_info()[0], ImportError): raise
        scanner.scan(importerror, onerror=onerror)
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.importerror import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

    def test_importerror_in_package_during_scan_custom_onerror(self):
        from venusian.tests.fixtures import importerror_package
        md('venusian.tests.fixtures.importerror_package.will_cause_import_error')
        test = _Test()
        scanner = self._makeOne(test=test)
        # with this custom onerror, scan will not propagate the importerror
        # from will_raise_importerror
        def onerror(name):
            raise ValueError
        self.assertRaises(ValueError, scanner.scan, importerror_package,
                          onerror=onerror)
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.importerror_package import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

    def test_attrerror_during_scan_custom_onerror(self):
        from venusian.tests.fixtures import attrerror
        test = _Test()
        scanner = self._makeOne(test=test)
        # with this custom onerror, scan will not propagate the importerror
        # from will_raise_importerror
        def onerror(name):
            if not issubclass(sys.exc_info()[0], ImportError): raise
        self.assertRaises(AttributeError, scanner.scan, attrerror,
                          onerror=onerror)
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.attrerror import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

    def test_attrerror_in_package_during_scan_custom_onerror(self):
        from venusian.tests.fixtures import attrerror_package
        md('venusian.tests.fixtures.attrerror_package.will_cause_import_error')
        test = _Test()
        scanner = self._makeOne(test=test)
        # with this custom onerror, scan will not propagate the importerror
        # from will_raise_importerror
        def onerror(name):
            if not issubclass(sys.exc_info()[0], ImportError): raise
        self.assertRaises(AttributeError, scanner.scan, attrerror_package,
                          onerror=onerror)
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.attrerror_package import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)
        
    def test_attrerror_in_package_during_scan_no_custom_onerror(self):
        from venusian.tests.fixtures import attrerror_package
        md('venusian.tests.fixtures.attrerror_package.will_cause_import_error')
        test = _Test()
        scanner = self._makeOne(test=test)
        self.assertRaises(AttributeError, scanner.scan, attrerror_package)
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.attrerror_package import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)
        
    def test_onerror_used_to_swallow_all_exceptions(self):
        from venusian.tests.fixtures import subpackages
        test = _Test()
        scanner = self._makeOne(test=test)
        # onerror can also be used to skip errors while scanning submodules
        # e.g.: test modules under a given library
        swallowed = []
        def ignore_child(name):
            swallowed.append(name)
        scanner.scan(subpackages, onerror=ignore_child)
        self.assertEqual(swallowed,
          ['venusian.tests.fixtures.subpackages.childpackage.will_cause_import_error',
           'venusian.tests.fixtures.subpackages.mod2'])
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.subpackages import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

    def test_ignore_by_full_dotted_name(self):
        from venusian.tests.fixtures import one
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            one, 
            ignore=['venusian.tests.fixtures.one.module2']
            )
        self.assertEqual(len(test.registrations), 3)
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module import inst as inst1
        from venusian.tests.fixtures.one.module import Class as Class1

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], Class1)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func1)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'inst')
        self.assertEqual(test.registrations[2]['ob'], inst1)
        self.assertEqual(test.registrations[2]['instance'], True)

    def test_ignore_by_full_dotted_name2(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=['venusian.tests.fixtures.nested.sub1']
            )
        self.assertEqual(len(test.registrations), 3)
        from venusian.tests.fixtures.nested import function as func1
        from venusian.tests.fixtures.nested.sub2 import function as func2
        from venusian.tests.fixtures.nested.sub2.subsub2 import function as func3

        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func2)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'function')
        self.assertEqual(test.registrations[2]['ob'], func3)
        self.assertEqual(test.registrations[2]['function'], True)

    def test_ignore_by_full_dotted_name3(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=['venusian.tests.fixtures.nested.sub1',
                    'venusian.tests.fixtures.nested.sub2']
            )
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.nested import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

    def test_ignore_by_full_dotted_name4(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=['venusian.tests.fixtures.nested.sub1',
                    'venusian.tests.fixtures.nested.function']
            )
        self.assertEqual(len(test.registrations), 2)
        from venusian.tests.fixtures.nested.sub2 import function as func2
        from venusian.tests.fixtures.nested.sub2.subsub2 import function as func3

        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func2)
        self.assertEqual(test.registrations[0]['function'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func3)
        self.assertEqual(test.registrations[1]['function'], True)

    def test_ignore_by_relative_dotted_name(self):
        from venusian.tests.fixtures import one
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(one,  ignore=['.module2'])
        self.assertEqual(len(test.registrations), 3)
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module import inst as inst1
        from venusian.tests.fixtures.one.module import Class as Class1

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], Class1)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func1)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'inst')
        self.assertEqual(test.registrations[2]['ob'], inst1)
        self.assertEqual(test.registrations[2]['instance'], True)
        
    def test_ignore_by_relative_dotted_name2(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=['.sub1']
            )
        self.assertEqual(len(test.registrations), 3)
        from venusian.tests.fixtures.nested import function as func1
        from venusian.tests.fixtures.nested.sub2 import function as func2
        from venusian.tests.fixtures.nested.sub2.subsub2 import function as func3

        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func2)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'function')
        self.assertEqual(test.registrations[2]['ob'], func3)
        self.assertEqual(test.registrations[2]['function'], True)

    def test_ignore_by_relative_dotted_name3(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=['.sub1', '.sub2']
            )
        self.assertEqual(len(test.registrations), 1)
        from venusian.tests.fixtures.nested import function as func1
        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

    def test_ignore_by_relative_dotted_name4(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=['.sub1', '.function']
            )
        self.assertEqual(len(test.registrations), 2)
        from venusian.tests.fixtures.nested.sub2 import function as func2
        from venusian.tests.fixtures.nested.sub2.subsub2 import function as func3

        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func2)
        self.assertEqual(test.registrations[0]['function'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func3)
        self.assertEqual(test.registrations[1]['function'], True)

    def test_ignore_by_function(self):
        from venusian.tests.fixtures import one
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(one, ignore=[re.compile('Class').search, 
                                  re.compile('inst').search])
        self.assertEqual(len(test.registrations), 2)
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module2 import function as func2

        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func1)
        self.assertEqual(test.registrations[0]['function'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func2)
        self.assertEqual(test.registrations[1]['function'], True)

    def test_ignore_by_function_nested(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=[re.compile('.function$').search]
            )
        self.assertEqual(len(test.registrations), 0)

    def test_ignore_by_function_nested2(self):
        from venusian.tests.fixtures import nested
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(
            nested, 
            ignore=[re.compile('sub2$').search,
                    re.compile('nested.function$').search]
            )
        self.assertEqual(len(test.registrations), 2)

        from venusian.tests.fixtures.nested.sub1 import function as func2
        from venusian.tests.fixtures.nested.sub1.subsub1 import function as func3

        self.assertEqual(test.registrations[0]['name'], 'function')
        self.assertEqual(test.registrations[0]['ob'], func2)
        self.assertEqual(test.registrations[0]['function'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func3)
        self.assertEqual(test.registrations[1]['function'], True)
        
        
    def test_ignore_as_string(self):
        from venusian.tests.fixtures import one
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(one, ignore='venusian.tests.fixtures.one.module2')
        self.assertEqual(len(test.registrations), 3)
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module import inst as inst1
        from venusian.tests.fixtures.one.module import Class as Class1

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], Class1)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func1)
        self.assertEqual(test.registrations[1]['function'], True)

        self.assertEqual(test.registrations[2]['name'], 'inst')
        self.assertEqual(test.registrations[2]['ob'], inst1)
        self.assertEqual(test.registrations[2]['instance'], True)
        
    def test_ignore_mixed_string_and_func(self):
        import re
        from venusian.tests.fixtures import one
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(one, ignore=['venusian.tests.fixtures.one.module2',
                                  re.compile('inst').search])
        self.assertEqual(len(test.registrations), 2)
        from venusian.tests.fixtures.one.module import function as func1
        from venusian.tests.fixtures.one.module import Class as Class1

        self.assertEqual(test.registrations[0]['name'], 'Class')
        self.assertEqual(test.registrations[0]['ob'], Class1)
        self.assertEqual(test.registrations[0]['method'], True)

        self.assertEqual(test.registrations[1]['name'], 'function')
        self.assertEqual(test.registrations[1]['ob'], func1)
        self.assertEqual(test.registrations[1]['function'], True)

    def test_ignore_mixed_string_abs_rel_and_func(self):
        import re
        from venusian.tests.fixtures import one
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(one, ignore=['venusian.tests.fixtures.one.module2',
                                  '.module',
                                  re.compile('inst').search])
        self.assertEqual(len(test.registrations), 0)

    def test_lifting1(self):
        from venusian.tests.fixtures import lifting1
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(lifting1)
        test.registrations.sort(
            key=lambda x: (x['name'], x['attr'], x['ob'].__module__)
            )
        self.assertEqual(len(test.registrations), 11)

        self.assertEqual(test.registrations[0]['attr'], 'boo')
        self.assertEqual(test.registrations[0]['name'], 'Sub')
        self.assertEqual(test.registrations[0]['ob'], lifting1.Sub)

        self.assertEqual(test.registrations[1]['attr'], 'classname')
        self.assertEqual(test.registrations[1]['name'], 'Sub')
        self.assertEqual(test.registrations[1]['ob'], lifting1.Sub)

        self.assertEqual(test.registrations[2]['attr'], 'hiss')
        self.assertEqual(test.registrations[2]['name'], 'Sub')
        self.assertEqual(test.registrations[2]['ob'], lifting1.Sub)

        self.assertEqual(test.registrations[3]['attr'], 'jump')
        self.assertEqual(test.registrations[3]['name'], 'Sub')
        self.assertEqual(test.registrations[3]['ob'], lifting1.Sub)

        self.assertEqual(test.registrations[4]['attr'], 'ram')
        self.assertEqual(test.registrations[4]['name'], 'Sub')
        self.assertEqual(test.registrations[4]['ob'], lifting1.Sub)

        self.assertEqual(test.registrations[5]['attr'], 'smack')
        self.assertEqual(test.registrations[5]['name'], 'Sub')
        self.assertEqual(test.registrations[5]['ob'], lifting1.Sub)

        self.assertEqual(test.registrations[6]['attr'], 'boo')
        self.assertEqual(test.registrations[6]['name'], 'Super1')
        self.assertEqual(test.registrations[6]['ob'], lifting1.Super1)

        self.assertEqual(test.registrations[7]['attr'], 'classname')
        self.assertEqual(test.registrations[7]['name'], 'Super1')
        self.assertEqual(test.registrations[7]['ob'], lifting1.Super1)

        self.assertEqual(test.registrations[8]['attr'], 'ram')
        self.assertEqual(test.registrations[8]['name'], 'Super1')
        self.assertEqual(test.registrations[8]['ob'], lifting1.Super1)

        self.assertEqual(test.registrations[9]['attr'], 'hiss')
        self.assertEqual(test.registrations[9]['name'], 'Super2')
        self.assertEqual(test.registrations[9]['ob'], lifting1.Super2)

        self.assertEqual(test.registrations[10]['attr'], 'jump')
        self.assertEqual(test.registrations[10]['name'], 'Super2')
        self.assertEqual(test.registrations[10]['ob'], lifting1.Super2)

    def test_lifting2(self):
        from venusian.tests.fixtures import lifting2
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(lifting2)
        test.registrations.sort(
            key=lambda x: (x['name'], x['attr'], x['ob'].__module__)
            )
        self.assertEqual(len(test.registrations), 6)

        self.assertEqual(test.registrations[0]['attr'], 'boo')
        self.assertEqual(test.registrations[0]['name'], 'Sub')
        self.assertEqual(test.registrations[0]['ob'], lifting2.Sub)

        self.assertEqual(test.registrations[1]['attr'], 'classname')
        self.assertEqual(test.registrations[1]['name'], 'Sub')
        self.assertEqual(test.registrations[1]['ob'], lifting2.Sub)

        self.assertEqual(test.registrations[2]['attr'], 'hiss')
        self.assertEqual(test.registrations[2]['name'], 'Sub')
        self.assertEqual(test.registrations[2]['ob'], lifting2.Sub)

        self.assertEqual(test.registrations[3]['attr'], 'jump')
        self.assertEqual(test.registrations[3]['name'], 'Sub')
        self.assertEqual(test.registrations[3]['ob'], lifting2.Sub)

        self.assertEqual(test.registrations[4]['attr'], 'ram')
        self.assertEqual(test.registrations[4]['name'], 'Sub')
        self.assertEqual(test.registrations[4]['ob'], lifting2.Sub)

        self.assertEqual(test.registrations[5]['attr'], 'smack')
        self.assertEqual(test.registrations[5]['name'], 'Sub')
        self.assertEqual(test.registrations[5]['ob'], lifting2.Sub)

    def test_lifting3(self):
        from venusian.tests.fixtures import lifting3
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(lifting3)
        test.registrations.sort(
            key=lambda x: (x['name'], x['attr'], x['ob'].__module__)
            )
        self.assertEqual(len(test.registrations), 8)

        self.assertEqual(test.registrations[0]['attr'], 'boo')
        self.assertEqual(test.registrations[0]['name'], 'Sub')
        self.assertEqual(test.registrations[0]['ob'], lifting3.Sub)

        self.assertEqual(test.registrations[1]['attr'], 'classname')
        self.assertEqual(test.registrations[1]['name'], 'Sub')
        self.assertEqual(test.registrations[1]['ob'], lifting3.Sub)

        self.assertEqual(test.registrations[2]['attr'], 'hiss')
        self.assertEqual(test.registrations[2]['name'], 'Sub')
        self.assertEqual(test.registrations[2]['ob'], lifting3.Sub)

        self.assertEqual(test.registrations[3]['attr'], 'jump')
        self.assertEqual(test.registrations[3]['name'], 'Sub')
        self.assertEqual(test.registrations[3]['ob'], lifting3.Sub)

        self.assertEqual(test.registrations[4]['attr'], 'ram')
        self.assertEqual(test.registrations[4]['name'], 'Sub')
        self.assertEqual(test.registrations[4]['ob'], lifting3.Sub)

        self.assertEqual(test.registrations[5]['attr'], 'smack')
        self.assertEqual(test.registrations[5]['name'], 'Sub')
        self.assertEqual(test.registrations[5]['ob'], lifting3.Sub)

        self.assertEqual(test.registrations[6]['attr'], 'hiss')
        self.assertEqual(test.registrations[6]['name'], 'Super2')
        self.assertEqual(test.registrations[6]['ob'], lifting3.Super2)

        self.assertEqual(test.registrations[7]['attr'], 'jump')
        self.assertEqual(test.registrations[7]['name'], 'Super2')
        self.assertEqual(test.registrations[7]['ob'], lifting3.Super2)

    def test_lifting4(self):
        from venusian.tests.fixtures import lifting4
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(lifting4)
        test.registrations.sort(
            key=lambda x: (x['name'], x['attr'], x['ob'].__module__)
            )
        self.assertEqual(len(test.registrations), 2)

        self.assertEqual(test.registrations[0]['attr'], 'hiss')
        self.assertEqual(test.registrations[0]['name'], 'Sub')
        self.assertEqual(test.registrations[0]['ob'], lifting4.Sub)

        self.assertEqual(test.registrations[1]['attr'], 'smack')
        self.assertEqual(test.registrations[1]['name'], 'Sub')
        self.assertEqual(test.registrations[1]['ob'], lifting4.Sub)

    def test_lifting5(self):
        from venusian.tests.fixtures import lifting5
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(lifting5)
        test.registrations.sort(
            key=lambda x: (x['name'], x['attr'], x['ob'].__module__)
            )
        self.assertEqual(len(test.registrations), 15)

        self.assertEqual(test.registrations[0]['attr'], 'boo')
        self.assertEqual(test.registrations[0]['name'], 'Sub')
        self.assertEqual(test.registrations[0]['ob'], lifting5.Sub)

        self.assertEqual(test.registrations[1]['attr'], 'classname')
        self.assertEqual(test.registrations[1]['name'], 'Sub')
        self.assertEqual(test.registrations[1]['ob'], lifting5.Sub)

        self.assertEqual(test.registrations[2]['attr'], 'hiss')
        self.assertEqual(test.registrations[2]['name'], 'Sub')
        self.assertEqual(test.registrations[2]['ob'], lifting5.Sub)

        self.assertEqual(test.registrations[3]['attr'], 'jump')
        self.assertEqual(test.registrations[3]['name'], 'Sub')
        self.assertEqual(test.registrations[3]['ob'], lifting5.Sub)

        self.assertEqual(test.registrations[4]['attr'], 'ram')
        self.assertEqual(test.registrations[4]['name'], 'Sub')
        self.assertEqual(test.registrations[4]['ob'], lifting5.Sub)

        self.assertEqual(test.registrations[5]['attr'], 'smack')
        self.assertEqual(test.registrations[5]['name'], 'Sub')
        self.assertEqual(test.registrations[5]['ob'], lifting5.Sub)

        self.assertEqual(test.registrations[6]['attr'], 'boo')
        self.assertEqual(test.registrations[6]['name'], 'Super1')
        self.assertEqual(test.registrations[6]['ob'], lifting5.Super1)

        self.assertEqual(test.registrations[7]['attr'], 'classname')
        self.assertEqual(test.registrations[7]['name'], 'Super1')
        self.assertEqual(test.registrations[7]['ob'], lifting5.Super1)

        self.assertEqual(test.registrations[8]['attr'], 'jump')
        self.assertEqual(test.registrations[8]['name'], 'Super1')
        self.assertEqual(test.registrations[8]['ob'], lifting5.Super1)
        
        self.assertEqual(test.registrations[9]['attr'], 'ram')
        self.assertEqual(test.registrations[9]['name'], 'Super1')
        self.assertEqual(test.registrations[9]['ob'], lifting5.Super1)

        self.assertEqual(test.registrations[10]['attr'], 'boo')
        self.assertEqual(test.registrations[10]['name'], 'Super2')
        self.assertEqual(test.registrations[10]['ob'], lifting5.Super2)

        self.assertEqual(test.registrations[11]['attr'], 'classname')
        self.assertEqual(test.registrations[11]['name'], 'Super2')
        self.assertEqual(test.registrations[11]['ob'], lifting5.Super2)

        self.assertEqual(test.registrations[12]['attr'], 'hiss')
        self.assertEqual(test.registrations[12]['name'], 'Super2')
        self.assertEqual(test.registrations[12]['ob'], lifting5.Super2)

        self.assertEqual(test.registrations[13]['attr'], 'jump')
        self.assertEqual(test.registrations[13]['name'], 'Super2')
        self.assertEqual(test.registrations[13]['ob'], lifting5.Super2)

        self.assertEqual(test.registrations[14]['attr'], 'ram')
        self.assertEqual(test.registrations[14]['name'], 'Super2')
        self.assertEqual(test.registrations[14]['ob'], lifting5.Super2)

    def test_subclassing(self):
        from venusian.tests.fixtures import subclassing
        test = _Test()
        scanner = self._makeOne(test=test)
        scanner.scan(subclassing)
        test.registrations.sort(
            key=lambda x: (x['name'], x['attr'], x['ob'].__module__)
            )
        self.assertEqual(len(test.registrations), 2)
        
        self.assertEqual(test.registrations[0]['attr'], 'boo')
        self.assertEqual(test.registrations[0]['name'], 'Super')
        self.assertEqual(test.registrations[0]['ob'], subclassing.Super)

        self.assertEqual(test.registrations[1]['attr'], 'classname')
        self.assertEqual(test.registrations[1]['name'], 'Super')
        self.assertEqual(test.registrations[1]['ob'], subclassing.Super)
        
class Test_lift(unittest.TestCase):
    def _makeOne(self, categories=None):
        from venusian import lift
        return lift(categories)

    def test_not_class(self):
        inst = self._makeOne()
        self.assertRaises(RuntimeError, inst, None)

class Test_onlyliftedfrom(unittest.TestCase):
    def _makeOne(self):
        from venusian import onlyliftedfrom
        return onlyliftedfrom()

    def test_not_class(self):
        inst = self._makeOne()
        self.assertRaises(RuntimeError, inst, None)
        
def md(name): # pragma: no cover
    if name in sys.modules:
        del sys.modules[name]
    
venusian-1.2.0/venusian/tests/test_advice.py0000664000175000017500000000613713415167143022362 0ustar  tseavertseaver00000000000000##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests for advice

This module was adapted from 'protocols.tests.advice', part of the Python
Enterprise Application Kit (PEAK).  Please notify the PEAK authors
(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
Zope-specific changes are required, so that the PEAK version of this module
can be kept in sync.

PEAK is a Python application framework that interoperates with (but does
not require) Zope 3 and Twisted.  It provides tools for manipulating UML
models, object-relational persistence, aspect-oriented programming, and more.
Visit the PEAK home page at http://peak.telecommunity.com for more information.

$Id: test_advice.py 40836 2005-12-16 22:40:51Z benji_york $
"""

import unittest
import sys
from venusian import advice

PY3 = sys.version_info[0] >= 3

if not PY3:
    class ClassicClass:
        classLevelFrameInfo = advice.getFrameInfo(sys._getframe())

class NewStyleClass(object):
    classLevelFrameInfo = advice.getFrameInfo(sys._getframe())

moduleLevelFrameInfo = advice.getFrameInfo(sys._getframe())

class FrameInfoTest(unittest.TestCase):

    classLevelFrameInfo = advice.getFrameInfo(sys._getframe())

    def testModuleInfo(self):
        kind, module, f_locals, f_globals, codeinfo = moduleLevelFrameInfo
        self.assertEqual(kind, "module")
        for d in module.__dict__, f_locals, f_globals:
            self.assertTrue(d is globals())
        self.assertEqual(len(codeinfo), 4)

    if not PY3:
        def testClassicClassInfo(self):
            (kind, module, f_locals, f_globals,
             codeinfo) = ClassicClass.classLevelFrameInfo
            self.assertEqual(kind, "class")

            self.assertTrue(f_locals is ClassicClass.__dict__)  # ???
            for d in module.__dict__, f_globals:
                self.assertTrue(d is globals())
            self.assertEqual(len(codeinfo), 4)

    def testNewStyleClassInfo(self):
        (kind, module, f_locals,
         f_globals, codeinfo) = NewStyleClass.classLevelFrameInfo
        self.assertEqual(kind, "class")

        for d in module.__dict__, f_globals:
            self.assertTrue(d is globals())
        self.assertEqual(len(codeinfo), 4)

    def testCallInfo(self):
        (kind, module, f_locals, f_globals,
         codeinfo) = advice.getFrameInfo(sys._getframe())
        self.assertEqual(kind, "function call")
        self.assertTrue(f_locals is locals()) # ???
        for d in module.__dict__, f_globals:
            self.assertTrue(d is globals())
        self.assertEqual(len(codeinfo), 4)
venusian-1.2.0/venusian/tests/fixtures/0000775000175000017500000000000013415173730021357 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/lifting4.py0000664000175000017500000000073413155075417023461 0ustar  tseavertseaver00000000000000from venusian import lift, onlyliftedfrom
from venusian.tests.fixtures import (
    categorydecorator,
    categorydecorator2,
    )

@onlyliftedfrom()
class Super(object): # pragma: no cover
    @categorydecorator()
    def hiss(self): pass

    @categorydecorator2()
    def jump(self): pass
        
@lift(('mycategory',))
class Sub(Super): # pragma: no cover
    def hiss(self): pass
    
    def jump(self): pass

    @categorydecorator2()
    def smack(self): pass
    
venusian-1.2.0/venusian/tests/fixtures/__init__.py0000664000175000017500000000150613155075417023476 0ustar  tseavertseaver00000000000000import venusian

class decorator(object):
    category = None
    call_count = 0

    def __init__(self, **kw):
        self.__dict__.update(kw)

    def __call__(self, wrapped):
        view_config = self.__dict__.copy()
        def callback(context, name, ob):
            if hasattr(context, 'test'):
                context.test(ob=ob, name=name, **view_config)
            self.__class__.call_count += 1
        info = venusian.attach(wrapped, callback, category=self.category)
        if info.scope == 'class':
            # we're in the midst of a class statement
            if view_config.get('attr') is None:
                view_config['attr'] = wrapped.__name__
        return wrapped

class categorydecorator(decorator):
    category = 'mycategory'
    
class categorydecorator2(decorator):
    category = 'mycategory2'
    
venusian-1.2.0/venusian/tests/fixtures/importerror_package/0000775000175000017500000000000013415173730025416 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/importerror_package/__init__.py0000664000175000017500000000020713155075417027532 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

venusian-1.2.0/venusian/tests/fixtures/importerror_package/will_cause_import_error/0000775000175000017500000000000013415173730032350 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/importerror_package/will_cause_import_error/__init__.py0000664000175000017500000000002413155075417034461 0ustar  tseavertseaver00000000000000import doesnt.exist
venusian-1.2.0/venusian/tests/fixtures/inheritance.py0000664000175000017500000000017213155075417024226 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator()
class Parent(object):
    pass

class Child(Parent):
    pass
venusian-1.2.0/venusian/tests/fixtures/lifting2.py0000664000175000017500000000120113155075417023445 0ustar  tseavertseaver00000000000000from venusian import lift, onlyliftedfrom
from venusian.tests.fixtures import decorator

@onlyliftedfrom()
class Super1(object): # pragma: no cover
    @decorator()
    def classname(self): pass

    @decorator()
    def boo(self): pass

    @decorator()
    def ram(self): pass

    def jump(self): pass

@onlyliftedfrom()
class Super2(object): # pragma: no cover
    def boo(self): pass

    @decorator()
    def hiss(self): pass

    @decorator()
    def jump(self): pass
        
@lift()
class Sub(Super1, Super2): # pragma: no cover
    def boo(self): pass

    def hiss(self): pass
    
    @decorator()
    def smack(self): pass
    
venusian-1.2.0/venusian/tests/fixtures/attrerror/0000775000175000017500000000000013415173730023403 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/attrerror/__init__.py0000664000175000017500000000020713155075417025517 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

venusian-1.2.0/venusian/tests/fixtures/attrerror/will_cause_import_error.py0000664000175000017500000000002613155075417030711 0ustar  tseavertseaver00000000000000raise AttributeError

venusian-1.2.0/venusian/tests/fixtures/classdecorator.py0000664000175000017500000000025713155075417024751 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(superclass=True)
class SuperClass(object):
    pass

@decorator(subclass=True)
class SubClass(SuperClass):
    pass

venusian-1.2.0/venusian/tests/fixtures/zipped.zip0000664000175000017500000000160213155075417023401 0ustar  tseavertseaver00000000000000PKSF9
>moduleinzip.pyUT	TTux
0y
'GRlm]!$|#+q}Ň34h|N\e$Vzğ/,2m6mRC[jӏPK
jBVF
packageinzip/UT	TTuxPK
AVFpackageinzip/__init__.pyUT	TTuxPK
jBVF$packageinzip/moduleinpackageinzip.pyUT	TTuxPKSF9
>moduleinzip.pyUTTuxPK
jBVF
Apackageinzip/UTTuxPK
AVFMpackageinzip/__init__.pyUTTuxPK
jBVF$packageinzip/moduleinpackageinzip.pyUTTuxPKovenusian-1.2.0/venusian/tests/fixtures/category.py0000664000175000017500000000045713155075417023560 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import categorydecorator
from venusian.tests.fixtures import categorydecorator2

@categorydecorator(function=True)
def function(request): # pragma: no cover
    return request

@categorydecorator2(function=True)
def function2(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/nested/0000775000175000017500000000000013415173730022641 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/nested/__init__.py0000664000175000017500000000020713155075417024755 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

venusian-1.2.0/venusian/tests/fixtures/nested/sub2/0000775000175000017500000000000013415173730023514 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/nested/sub2/__init__.py0000664000175000017500000000020613155075417025627 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/nested/sub2/subsub2/0000775000175000017500000000000013415173730025101 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/nested/sub2/subsub2/__init__.py0000664000175000017500000000020613155075417027214 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/nested/sub1/0000775000175000017500000000000013415173730023513 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/nested/sub1/__init__.py0000664000175000017500000000020613155075417025626 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/nested/sub1/subsub1/0000775000175000017500000000000013415173730025077 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/nested/sub1/subsub1/__init__.py0000664000175000017500000000020613155075417027212 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/lifting3.py0000664000175000017500000000125013155075417023452 0ustar  tseavertseaver00000000000000from venusian import lift, onlyliftedfrom
from venusian.tests.fixtures import decorator

@onlyliftedfrom()
class NoDefinitions(object):
    pass

@onlyliftedfrom()
class Super1(object): # pragma: no cover
    @decorator()
    def classname(self): pass

    @decorator()
    def boo(self): pass

    @decorator()
    def ram(self): pass

    def jump(self): pass

class Super2(object): # pragma: no cover
    def boo(self): pass

    @decorator()
    def hiss(self): pass

    @decorator()
    def jump(self): pass
        
@lift()
class Sub(Super1, Super2): # pragma: no cover
    def boo(self): pass

    def hiss(self): pass
    
    @decorator()
    def smack(self): pass
    
venusian-1.2.0/venusian/tests/fixtures/lifting1.py0000664000175000017500000000111513155075417023450 0ustar  tseavertseaver00000000000000from venusian import lift
from venusian.tests.fixtures import decorator

class Super1(object): # pragma: no cover
    @decorator()
    def classname(self): pass

    @decorator()
    def boo(self): pass

    @decorator()
    def ram(self): pass

    def jump(self): pass

class Super2(object): # pragma: no cover
    def boo(self): pass

    @decorator()
    def hiss(self): pass

    @decorator()
    def jump(self): pass
        
@lift()
class Sub(Super1, Super2): # pragma: no cover
    def boo(self): pass

    def hiss(self): pass
    
    @decorator()
    def smack(self): pass
    
venusian-1.2.0/venusian/tests/fixtures/one/0000775000175000017500000000000013415173730022140 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/one/__init__.py0000664000175000017500000000001213155075417024246 0ustar  tseavertseaver00000000000000# package
venusian-1.2.0/venusian/tests/fixtures/one/module2.py0000664000175000017500000000064313155075417024070 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

class Class(object):
    @decorator(method=True)
    def method(self, request): # pragma: no cover
        return request
    
class Instance(object):
    def __call__(self, request): # pragma: no cover
        return request

inst = Instance()
inst = decorator(instance=True)(inst)

venusian-1.2.0/venusian/tests/fixtures/one/module.py0000664000175000017500000000064313155075417024006 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

class Class(object):
    @decorator(method=True)
    def method(self, request): # pragma: no cover
        return request
    
class Instance(object):
    def __call__(self, request): # pragma: no cover
        return request

inst = Instance()
inst = decorator(instance=True)(inst)

venusian-1.2.0/venusian/tests/fixtures/importerror/0000775000175000017500000000000013415173730023743 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/importerror/__init__.py0000664000175000017500000000020713155075417026057 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

venusian-1.2.0/venusian/tests/fixtures/importerror/will_cause_import_error.py0000664000175000017500000000002413155075417031247 0ustar  tseavertseaver00000000000000import doesnt.exist
venusian-1.2.0/venusian/tests/fixtures/attrerror_package/0000775000175000017500000000000013415173730025056 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/attrerror_package/__init__.py0000664000175000017500000000020713155075417027172 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

venusian-1.2.0/venusian/tests/fixtures/attrerror_package/will_cause_import_error/0000775000175000017500000000000013415173730032010 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/attrerror_package/will_cause_import_error/__init__.py0000664000175000017500000000002613155075417034123 0ustar  tseavertseaver00000000000000raise AttributeError

venusian-1.2.0/venusian/tests/fixtures/two/0000775000175000017500000000000013415173730022170 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/two/__init__.py0000664000175000017500000000000013155075417024273 0ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/two/mod1.py0000664000175000017500000000013213155075417023402 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator()
class Class(object):
    pass
venusian-1.2.0/venusian/tests/fixtures/two/mod2.py0000664000175000017500000000003013155075417023400 0ustar  tseavertseaver00000000000000from .mod1 import Class
venusian-1.2.0/venusian/tests/fixtures/pyc/0000775000175000017500000000000013415173730022152 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/pyc/__init__.py0000664000175000017500000000000713155075417024264 0ustar  tseavertseaver00000000000000# pkg

venusian-1.2.0/venusian/tests/fixtures/pyc/subpackage/0000775000175000017500000000000013415173730024257 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/pyc/subpackage/__init__.py0000664000175000017500000000021213155075417026367 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def pkgfunction(request): # pragma: no cover
    return request

venusian-1.2.0/venusian/tests/fixtures/pyc/module.py0000664000175000017500000000064313155075417024020 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

class Class(object):
    @decorator(method=True)
    def method(self, request): # pragma: no cover
        return request
    
class Instance(object):
    def __call__(self, request): # pragma: no cover
        return request

inst = Instance()
inst = decorator(instance=True)(inst)

venusian-1.2.0/venusian/tests/fixtures/importonly/0000775000175000017500000000000013415173730023573 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/importonly/__init__.py0000664000175000017500000000001213155075417025701 0ustar  tseavertseaver00000000000000# package
venusian-1.2.0/venusian/tests/fixtures/importonly/two.py0000664000175000017500000000021113155075417024754 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def twofunction(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/importonly/one.py0000664000175000017500000000034113155075417024730 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

from venusian.tests.fixtures.importonly.two import twofunction # should not be scanned

@decorator(function=True)
def onefunction(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/lifting5.py0000664000175000017500000000122113155075417023452 0ustar  tseavertseaver00000000000000from venusian import lift
from venusian.tests.fixtures import decorator

class Super1(object): # pragma: no cover
    @decorator()
    def classname(self): pass

    @decorator()
    def boo(self): pass

    @decorator()
    def ram(self): pass

    @decorator()
    def jump(self): pass

@lift()
class Super2(Super1): # pragma: no cover
    @decorator()
    def boo(self): pass

    @decorator()
    def hiss(self): pass

    @decorator()
    def jump(self): pass
        
@lift()
class Sub(Super2): # pragma: no cover
    @decorator()
    def boo(self): pass

    @decorator()
    def hiss(self): pass
    
    @decorator()
    def smack(self): pass
    
venusian-1.2.0/venusian/tests/fixtures/subclassing.py0000664000175000017500000000046713155075417024261 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

class Super(object): # pragma: no cover
    @decorator()
    def classname(self): pass

    @decorator()
    def boo(self): pass


# the Sub class must not inherit the decorations of its superclass when scanned

class Sub(Super): # pragma: no cover
     pass
    
venusian-1.2.0/venusian/tests/fixtures/import_and_scan/0000775000175000017500000000000013415173730024517 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/import_and_scan/__init__.py0000664000175000017500000000001213155075417026625 0ustar  tseavertseaver00000000000000# package
venusian-1.2.0/venusian/tests/fixtures/import_and_scan/mock.py0000664000175000017500000000137013155075417026027 0ustar  tseavertseaver00000000000000class _Call(tuple):  # pragma: no cover
    def __new__(cls, value=(), name=None):
        name = ''
        args = ()
        kwargs = {}
        _len = len(value)
        if _len == 3:
            name, args, kwargs = value
        return tuple.__new__(cls, (name, args, kwargs))

    def __init__(self, value=(), name=None):
        self.name = name

    def __call__(self, *args, **kwargs):
        if self.name is None:
            return _Call(('', args, kwargs), name='()')
        else:
            return _Call((self.name, args, kwargs), name=self.name + '()')

    def __getattr__(self, attr):
        if self.name is None:
            return _Call(name=attr)
        else:
            return _Call(name='%s.%s' % (self.name, attr))


call = _Call()
venusian-1.2.0/venusian/tests/fixtures/import_and_scan/two.py0000664000175000017500000000021113155075417025700 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def twofunction(request): # pragma: no cover
    return request
venusian-1.2.0/venusian/tests/fixtures/import_and_scan/one.py0000664000175000017500000000037613155075417025664 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator
from venusian.tests.fixtures.import_and_scan.two import twofunction # should not be scanned

@decorator(function=True)
def onefunction(request): # pragma: no cover
    twofunction(request)
    return request
venusian-1.2.0/venusian/tests/fixtures/subpackages/0000775000175000017500000000000013415173730023647 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/subpackages/__init__.py0000664000175000017500000000020713155075417025763 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator

@decorator(function=True)
def function(request): # pragma: no cover
    return request

venusian-1.2.0/venusian/tests/fixtures/subpackages/childpackage/0000775000175000017500000000000013415173730026246 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/subpackages/childpackage/__init__.py0000664000175000017500000000000013155075417030351 0ustar  tseavertseaver00000000000000venusian-1.2.0/venusian/tests/fixtures/subpackages/childpackage/will_cause_import_error.py0000664000175000017500000000002413155075417033552 0ustar  tseavertseaver00000000000000import doesnt.exist
venusian-1.2.0/venusian/tests/fixtures/subpackages/mod2.py0000664000175000017500000000002613155075417025064 0ustar  tseavertseaver00000000000000raise AttributeError

venusian-1.2.0/venusian/tests/fixtures/class_and_method.py0000664000175000017500000000030313155075417025220 0ustar  tseavertseaver00000000000000from venusian.tests.fixtures import decorator


@decorator(class_=True)
class ClassWithMethod(object):

    @decorator(method=True)
    def method_on_class(self): # pragma: no cover
        pass
venusian-1.2.0/.travis.yml0000664000175000017500000000132513415166722016631 0ustar  tseavertseaver00000000000000# Wire up travis
language: python
sudo: false

matrix:
    include:
        - python: 2.7
          env: TOXENV=py27
        - python: 3.4
          env: TOXENV=py34
        - python: 3.5
          env: TOXENV=py35
        - python: pypy
          env: TOXENV=pypy
        - python: 3.5
          env: TOXENV=py2-cover,py3-cover,coverage
        - python: 3.5
          env: TOXENV=docs
        - python: 3.6
          env: TOXENV=py36
        - python: 3.7
          env: TOXENV=py37
          dist: xenial
          sudo: true

install:
  - travis_retry pip install tox

script:
  - travis_retry tox

notifications:
  email:
    - pyramid-checkins@lists.repoze.org
  irc:
    channels:
      - "chat.freenode.net#pyramid"

venusian-1.2.0/venusian.egg-info/0000775000175000017500000000000013415173730020036 5ustar  tseavertseaver00000000000000venusian-1.2.0/venusian.egg-info/dependency_links.txt0000664000175000017500000000000113415173727024112 0ustar  tseavertseaver00000000000000
venusian-1.2.0/venusian.egg-info/entry_points.txt0000664000175000017500000000000613415173727023336 0ustar  tseavertseaver00000000000000      venusian-1.2.0/venusian.egg-info/PKG-INFO0000664000175000017500000002513613415173727021150 0ustar  tseavertseaver00000000000000Metadata-Version: 2.1
Name: venusian
Version: 1.2.0
Summary: A library for deferring decorator actions
Home-page: https://pylonsproject.org
Author: Chris McDonough, Agendaless Consulting
Author-email: pylons-devel@googlegroups.com
License: BSD-derived (http://www.repoze.org/LICENSE.txt)
Description: venusian
        ========
        
        .. image:: https://travis-ci.org/Pylons/venusian.png?branch=master
                :target: https://travis-ci.org/Pylons/venusian
        
        .. image:: https://readthedocs.org/projects/venusian/badge/?version=latest
                :target: https://docs.pylonsproject.org/projects/venusian/en/latest/
                :alt: Documentation Status
        
        Venusian is a library which allows framework authors to defer
        decorator actions.  Instead of taking actions when a function (or
        class) decorator is executed at import time, you can defer the action
        usually taken by the decorator until a separate "scan" phase.
        
        See the "docs" directory of the package or the online documentation at
        https://docs.pylonsproject.org/projects/venusian/en/latest/
        
        
        1.2.0 (2019-01-08)
        ------------------
        
        - Add support for Python 3.7.
        
        - Drop support for Python 3.3.
        
        1.1.0 (2017-04-24)
        ------------------
        
        - Updated to using py.test instead of nosetest, and added support for Python
          3.4 -> 3.6
        
        - Make scanning more resilient of metaclasses that return proxies for any
          attribute access.
        
        - Fix bug where using the same venusian decorator on both a class and its
          methods would cause the method decorations to be ignored. See
          https://github.com/Pylons/venusian/issues/40
        
        - Drop support for Python 2.6.
        
        - Drop support for Python 3.2:  it is no longer supported by current
          packaging / CI tools.
        
        - Support loaders that require the module name as argument to their
          ``get_filename()`` method. This fixes problems with zipped packages
          on Python 3.
        
        - Micro-optimization when ignores are used (see
          https://github.com/Pylons/venusian/pull/20).
        
        - A tox run now combines coverage between Py2 and Py3.
        
        1.0 (2014-06-30)
        ----------------
        
        - Fix an issue under PyPy > 2.0 where attached decorators may not be found.
        
        - Drop support of Python 2.4 / 2.5 / Jython.
        
        - Add ``lift`` and ``onlyliftedfrom`` class decorators to allow for inheritance
          of venusian decorators attached to superclass methods.  See the API
          documentation for more information.
        
        - Fix bug where otherwise undecorated subclass of a superclass that had
          venusian decorators on it would inherit its superclass' decorations.
          Venusian decorators should have never been inherited implicitly.  See
          https://github.com/Pylons/venusian/issues/11#issuecomment-4977352
        
        1.0a8 (2013-04-15)
        ------------------
        
        - Pass ``ignore`` argument along recursively to ``walk_packages`` so custom
          ignore functions will ignore things recursively.  See
          https://github.com/Pylons/venusian/pull/16
        
        - Don't run tox tests under Python 2.4 anymore (tox no longer supports 2.4).
        
        1.0a7 (2012-08-25)
        ------------------
        
        - Venusian now works on Python 3.3b2+ (importlib-based).
        
        - Use nose-exclude instead of relying on fragile module-scope code to ensure
          we don't get errors resulting from import of fixture code during
          "nosetests".
        
        - Bug fix: no longer suppress ``ImportError`` while scanning by default.  If
          you want to suppress ``ImportError`` while scanning, you'll now need use an
          ``onerror`` callback as described in the documentation.
        
        1.0a6 (2012-04-23)
        ------------------
        
        - Don't ignore decorated objects within their original locations if they
          happen to be imported into another module (remove ``seen`` set from invoke
          in venusian scanning).  See https://github.com/Pylons/venusian/pull/13 .
        
        1.0a5 (2012-04-21)
        ------------------
        
        - Slightly less sucky way to ignore objects during scanning that are only
          imported into a module but not actually defined there.  See 1.0a4 change
          notes for rationale.  Now instead of checking whether the module of the
          *scanned object* matches the module being scanned, we check whether the
          module of the *Venusian attachment* matches the module being scanned.  This
          allows some genuine uses of imported objects as Venusian scan targets while
          preventing inappropriate double-scanning of objects that have a venusian
          attachment which just happen to be imported into other scanned modules.
        
        - Add ``dev`` and ``docs`` setup.py commands (ala Pyramid).
        
        1.0a4 (2012-04-16)
        ------------------
        
        - Attempt to ignore objects during scanning that are only imported into a
          module but not actually defined there.  This is a semantics change, but
          it's the right thing to do, because I found myself facing a situation like
          this::
        
            # in a module named "one"
        
            from two import anotheradecoratedthing
            @adecorator
            def adecoratedthing(): pass
        
            # and scanning both modules
            scan('one')
            scan('two')
        
          In this case you'd wind up with two repeated registrations for
          "anotherdecoratedthing", which isn't what anyone expects.
        
        1.0a3 (2012-02-08)
        ------------------
        
        - Add an ``ignore`` argument to the ``scan`` method of a ``Scanner``.  This
          argument allows a user to ignore packages, modules, and global objects by
          name during a ``scan``.  See the "ignore Scan Argument" in the narrative
          documentation for more details.
        
        1.0a2 (2011-09-02)
        ------------------
        
        - Close ImpLoader file handle to avoid resource warnings on Python 3.
        
        1.0a1 (2011-08-27)
        ------------------
        
        - Python 3 compatibility.
        
        - Allow an ``onerror`` callback to be passed to ``Scanner.scan()``.
        
        0.9 (2011-06-18)
        ----------------
        
        - Prevent corner case scan-time exception when trying to introspect insane
          module-scope objects.  See https://github.com/Pylons/venusian/issues/5 .
        
        0.8 (2011-04-30)
        ----------------
        
        - Normal "setup.py test" can't support running the venusian tests under py
          2.4 or 2.5; when it scans the 'classdecorators' fixture, it barfs.  To get
          around this, we used to depend on ``nose`` in ``setup_requires`` and tell
          "setup.py test" to use nose by setting test_suite to "nose.collector" but
          we can't anymore because folks use Venusian in systems which install from
          pip bundles; pip bundles do not support setup_requires.  So, sorry, we're
          painted into a corner; at this point you just have to know to install nose
          and run "setup.py nosetests" rather than "setup.py test".  Or just run
          "tox" which tests it under all Pythons.
        
        0.7 (2011-03-16)
        ----------------
        
        - Use Pylons theme in documentation.
        
        - Fix orphaned pyc test on pypy.
        
        - Fix GitHub Issue #1: subclasses of decorated classes that do not
          have any decorations should not inherit the decorations of their
          parent classes. 
        
        - Fix GitHub Issue #2: scans should only "find" each object once per
          scan, regardless of how many modules that object is imported into.
        
        0.6 (2011-01-09)
        ----------------
        
        - Some metaclasses (Elixir's) don't raise an AttributeError when asked for a
          nonexistent attribute during a scan.  We now catch all exceptions when
          interrogating an object for ``__venusian_callbacks__`` rather than just
          AttributeError.
        
        0.5 (2010-12-19)
        ----------------
        
        - Make ``codeinfo`` attribute available as an attribute of the AttachInfo
          object. It will be a tuple in the form ``(filename, lineno, function,
          sourceline)`` representing the context of the venusian decorator.  Eg.
          ``('/home/chrism/projects/venusian/tests/test_advice.py', 81,
          'testCallInfo', 'add_handler(foo, bar)')``
        
        0.4 (2010-09-03)
        ----------------
        
        - Bug fix: when a venusian decorator used as a class decorator was
          used against both a class *and* a subclass of that class, the
          superclass and subclass would effectively share the same set of
          callbacks.  This was not the intent: each class declaration should
          have its own local set of callbacks; callbacks added via decorations
          should not be inherited, and a superclass should not receive its
          subclass' decorations.
        
        - Arrange test fixtures into a single directory.
        
        0.3 (2010-06-24)
        ----------------
        
        - Ignore orphaned modules (``.pyc`` or ``.pyo`` files without a
          corresponding ``.py`` file) during a scan.
        
        0.2 (2010-04-18)
        ----------------
        
        - Add the concept of scan categories (see the "Scan Categories"
          section of the documentation) to allow an application to make use of
          more than one Venusian-using framework simultaneously.
        
        0.1 (2010-02-15)
        ----------------
        
        - Initial release.
        
Keywords: web wsgi zope
Platform: UNKNOWN
Classifier: Development Status :: 6 - Mature
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Provides-Extra: docs
Provides-Extra: testing
venusian-1.2.0/venusian.egg-info/top_level.txt0000664000175000017500000000001113415173727022566 0ustar  tseavertseaver00000000000000venusian
venusian-1.2.0/venusian.egg-info/not-zip-safe0000664000175000017500000000000113415166255022270 0ustar  tseavertseaver00000000000000
venusian-1.2.0/venusian.egg-info/requires.txt0000664000175000017500000000012113415173727022436 0ustar  tseavertseaver00000000000000
[docs]
Sphinx
repoze.sphinx.autointerface

[testing]
pytest
coverage
pytest-cov
venusian-1.2.0/venusian.egg-info/SOURCES.txt0000664000175000017500000000523613415173730021730 0ustar  tseavertseaver00000000000000.gitignore
.travis.yml
CHANGES.rst
CONTRIBUTORS.txt
COPYRIGHT.txt
LICENSE.txt
README.rst
rtd.txt
setup.cfg
setup.py
tox.ini
docs/Makefile
docs/api.rst
docs/conf.py
docs/glossary.rst
docs/index.rst
docs/.static/logo_hi.gif
docs/.static/repoze.css
venusian/__init__.py
venusian/advice.py
venusian/compat.py
venusian.egg-info/PKG-INFO
venusian.egg-info/SOURCES.txt
venusian.egg-info/dependency_links.txt
venusian.egg-info/entry_points.txt
venusian.egg-info/not-zip-safe
venusian.egg-info/requires.txt
venusian.egg-info/top_level.txt
venusian/tests/__init__.py
venusian/tests/test_advice.py
venusian/tests/test_venusian.py
venusian/tests/fixtures/__init__.py
venusian/tests/fixtures/category.py
venusian/tests/fixtures/class_and_method.py
venusian/tests/fixtures/classdecorator.py
venusian/tests/fixtures/inheritance.py
venusian/tests/fixtures/lifting1.py
venusian/tests/fixtures/lifting2.py
venusian/tests/fixtures/lifting3.py
venusian/tests/fixtures/lifting4.py
venusian/tests/fixtures/lifting5.py
venusian/tests/fixtures/subclassing.py
venusian/tests/fixtures/zipped.zip
venusian/tests/fixtures/attrerror/__init__.py
venusian/tests/fixtures/attrerror/will_cause_import_error.py
venusian/tests/fixtures/attrerror_package/__init__.py
venusian/tests/fixtures/attrerror_package/will_cause_import_error/__init__.py
venusian/tests/fixtures/import_and_scan/__init__.py
venusian/tests/fixtures/import_and_scan/mock.py
venusian/tests/fixtures/import_and_scan/one.py
venusian/tests/fixtures/import_and_scan/two.py
venusian/tests/fixtures/importerror/__init__.py
venusian/tests/fixtures/importerror/will_cause_import_error.py
venusian/tests/fixtures/importerror_package/__init__.py
venusian/tests/fixtures/importerror_package/will_cause_import_error/__init__.py
venusian/tests/fixtures/importonly/__init__.py
venusian/tests/fixtures/importonly/one.py
venusian/tests/fixtures/importonly/two.py
venusian/tests/fixtures/nested/__init__.py
venusian/tests/fixtures/nested/sub1/__init__.py
venusian/tests/fixtures/nested/sub1/subsub1/__init__.py
venusian/tests/fixtures/nested/sub2/__init__.py
venusian/tests/fixtures/nested/sub2/subsub2/__init__.py
venusian/tests/fixtures/one/__init__.py
venusian/tests/fixtures/one/module.py
venusian/tests/fixtures/one/module2.py
venusian/tests/fixtures/pyc/__init__.py
venusian/tests/fixtures/pyc/module.py
venusian/tests/fixtures/pyc/subpackage/__init__.py
venusian/tests/fixtures/subpackages/__init__.py
venusian/tests/fixtures/subpackages/mod2.py
venusian/tests/fixtures/subpackages/childpackage/__init__.py
venusian/tests/fixtures/subpackages/childpackage/will_cause_import_error.py
venusian/tests/fixtures/two/__init__.py
venusian/tests/fixtures/two/mod1.py
venusian/tests/fixtures/two/mod2.pyvenusian-1.2.0/CHANGES.rst0000664000175000017500000001620213415173432016316 0ustar  tseavertseaver000000000000001.2.0 (2019-01-08)
------------------

- Add support for Python 3.7.

- Drop support for Python 3.3.

1.1.0 (2017-04-24)
------------------

- Updated to using py.test instead of nosetest, and added support for Python
  3.4 -> 3.6

- Make scanning more resilient of metaclasses that return proxies for any
  attribute access.

- Fix bug where using the same venusian decorator on both a class and its
  methods would cause the method decorations to be ignored. See
  https://github.com/Pylons/venusian/issues/40

- Drop support for Python 2.6.

- Drop support for Python 3.2:  it is no longer supported by current
  packaging / CI tools.

- Support loaders that require the module name as argument to their
  ``get_filename()`` method. This fixes problems with zipped packages
  on Python 3.

- Micro-optimization when ignores are used (see
  https://github.com/Pylons/venusian/pull/20).

- A tox run now combines coverage between Py2 and Py3.

1.0 (2014-06-30)
----------------

- Fix an issue under PyPy > 2.0 where attached decorators may not be found.

- Drop support of Python 2.4 / 2.5 / Jython.

- Add ``lift`` and ``onlyliftedfrom`` class decorators to allow for inheritance
  of venusian decorators attached to superclass methods.  See the API
  documentation for more information.

- Fix bug where otherwise undecorated subclass of a superclass that had
  venusian decorators on it would inherit its superclass' decorations.
  Venusian decorators should have never been inherited implicitly.  See
  https://github.com/Pylons/venusian/issues/11#issuecomment-4977352

1.0a8 (2013-04-15)
------------------

- Pass ``ignore`` argument along recursively to ``walk_packages`` so custom
  ignore functions will ignore things recursively.  See
  https://github.com/Pylons/venusian/pull/16

- Don't run tox tests under Python 2.4 anymore (tox no longer supports 2.4).

1.0a7 (2012-08-25)
------------------

- Venusian now works on Python 3.3b2+ (importlib-based).

- Use nose-exclude instead of relying on fragile module-scope code to ensure
  we don't get errors resulting from import of fixture code during
  "nosetests".

- Bug fix: no longer suppress ``ImportError`` while scanning by default.  If
  you want to suppress ``ImportError`` while scanning, you'll now need use an
  ``onerror`` callback as described in the documentation.

1.0a6 (2012-04-23)
------------------

- Don't ignore decorated objects within their original locations if they
  happen to be imported into another module (remove ``seen`` set from invoke
  in venusian scanning).  See https://github.com/Pylons/venusian/pull/13 .

1.0a5 (2012-04-21)
------------------

- Slightly less sucky way to ignore objects during scanning that are only
  imported into a module but not actually defined there.  See 1.0a4 change
  notes for rationale.  Now instead of checking whether the module of the
  *scanned object* matches the module being scanned, we check whether the
  module of the *Venusian attachment* matches the module being scanned.  This
  allows some genuine uses of imported objects as Venusian scan targets while
  preventing inappropriate double-scanning of objects that have a venusian
  attachment which just happen to be imported into other scanned modules.

- Add ``dev`` and ``docs`` setup.py commands (ala Pyramid).

1.0a4 (2012-04-16)
------------------

- Attempt to ignore objects during scanning that are only imported into a
  module but not actually defined there.  This is a semantics change, but
  it's the right thing to do, because I found myself facing a situation like
  this::

    # in a module named "one"

    from two import anotheradecoratedthing
    @adecorator
    def adecoratedthing(): pass

    # and scanning both modules
    scan('one')
    scan('two')

  In this case you'd wind up with two repeated registrations for
  "anotherdecoratedthing", which isn't what anyone expects.

1.0a3 (2012-02-08)
------------------

- Add an ``ignore`` argument to the ``scan`` method of a ``Scanner``.  This
  argument allows a user to ignore packages, modules, and global objects by
  name during a ``scan``.  See the "ignore Scan Argument" in the narrative
  documentation for more details.

1.0a2 (2011-09-02)
------------------

- Close ImpLoader file handle to avoid resource warnings on Python 3.

1.0a1 (2011-08-27)
------------------

- Python 3 compatibility.

- Allow an ``onerror`` callback to be passed to ``Scanner.scan()``.

0.9 (2011-06-18)
----------------

- Prevent corner case scan-time exception when trying to introspect insane
  module-scope objects.  See https://github.com/Pylons/venusian/issues/5 .

0.8 (2011-04-30)
----------------

- Normal "setup.py test" can't support running the venusian tests under py
  2.4 or 2.5; when it scans the 'classdecorators' fixture, it barfs.  To get
  around this, we used to depend on ``nose`` in ``setup_requires`` and tell
  "setup.py test" to use nose by setting test_suite to "nose.collector" but
  we can't anymore because folks use Venusian in systems which install from
  pip bundles; pip bundles do not support setup_requires.  So, sorry, we're
  painted into a corner; at this point you just have to know to install nose
  and run "setup.py nosetests" rather than "setup.py test".  Or just run
  "tox" which tests it under all Pythons.

0.7 (2011-03-16)
----------------

- Use Pylons theme in documentation.

- Fix orphaned pyc test on pypy.

- Fix GitHub Issue #1: subclasses of decorated classes that do not
  have any decorations should not inherit the decorations of their
  parent classes. 

- Fix GitHub Issue #2: scans should only "find" each object once per
  scan, regardless of how many modules that object is imported into.

0.6 (2011-01-09)
----------------

- Some metaclasses (Elixir's) don't raise an AttributeError when asked for a
  nonexistent attribute during a scan.  We now catch all exceptions when
  interrogating an object for ``__venusian_callbacks__`` rather than just
  AttributeError.

0.5 (2010-12-19)
----------------

- Make ``codeinfo`` attribute available as an attribute of the AttachInfo
  object. It will be a tuple in the form ``(filename, lineno, function,
  sourceline)`` representing the context of the venusian decorator.  Eg.
  ``('/home/chrism/projects/venusian/tests/test_advice.py', 81,
  'testCallInfo', 'add_handler(foo, bar)')``

0.4 (2010-09-03)
----------------

- Bug fix: when a venusian decorator used as a class decorator was
  used against both a class *and* a subclass of that class, the
  superclass and subclass would effectively share the same set of
  callbacks.  This was not the intent: each class declaration should
  have its own local set of callbacks; callbacks added via decorations
  should not be inherited, and a superclass should not receive its
  subclass' decorations.

- Arrange test fixtures into a single directory.

0.3 (2010-06-24)
----------------

- Ignore orphaned modules (``.pyc`` or ``.pyo`` files without a
  corresponding ``.py`` file) during a scan.

0.2 (2010-04-18)
----------------

- Add the concept of scan categories (see the "Scan Categories"
  section of the documentation) to allow an application to make use of
  more than one Venusian-using framework simultaneously.

0.1 (2010-02-15)
----------------

- Initial release.
venusian-1.2.0/README.rst0000664000175000017500000000132213155075417016205 0ustar  tseavertseaver00000000000000venusian
========

.. image:: https://travis-ci.org/Pylons/venusian.png?branch=master
        :target: https://travis-ci.org/Pylons/venusian

.. image:: https://readthedocs.org/projects/venusian/badge/?version=latest
        :target: https://docs.pylonsproject.org/projects/venusian/en/latest/
        :alt: Documentation Status

Venusian is a library which allows framework authors to defer
decorator actions.  Instead of taking actions when a function (or
class) decorator is executed at import time, you can defer the action
usually taken by the decorator until a separate "scan" phase.

See the "docs" directory of the package or the online documentation at
https://docs.pylonsproject.org/projects/venusian/en/latest/
venusian-1.2.0/LICENSE.txt0000664000175000017500000000337713155075417016355 0ustar  tseavertseaver00000000000000License

  A copyright notice accompanies this license document that identifies
  the copyright holders.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

  1.  Redistributions in source code must retain the accompanying
      copyright notice, this list of conditions, and the following
      disclaimer.

  2.  Redistributions in binary form must reproduce the accompanying
      copyright notice, this list of conditions, and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.

  3.  Names of the copyright holders must not be used to endorse or
      promote products derived from this software without prior
      written permission from the copyright holders.

  4.  If any files are modified, you must cause the modified files to
      carry prominent notices stating that you changed the files and
      the date of any change.

  Disclaimer

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
    ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.

venusian-1.2.0/CONTRIBUTORS.txt0000664000175000017500000001131613155075417017220 0ustar  tseavertseaver00000000000000Pylons Project Contributor Agreement
====================================

The submitter agrees by adding his or her name within the section below named
"Contributors" and submitting the resulting modified document to the
canonical shared repository location for this software project (whether
directly, as a user with "direct commit access", or via a "pull request"), he
or she is signing a contract electronically.  The submitter becomes a
Contributor after a) he or she signs this document by adding their name
beneath the "Contributors" section below, and b) the resulting document is
accepted into the canonical version control repository.

Treatment of Account
---------------------

Contributor will not allow anyone other than the Contributor to use his or
her username or source repository login to submit code to a Pylons Project
source repository. Should Contributor become aware of any such use,
Contributor will immediately by notifying Agendaless Consulting.
Notification must be performed by sending an email to
webmaster@agendaless.com.  Until such notice is received, Contributor will be
presumed to have taken all actions made through Contributor's account. If the
Contributor has direct commit access, Agendaless Consulting will have
complete control and discretion over capabilities assigned to Contributor's
account, and may disable Contributor's account for any reason at any time.

Legal Effect of Contribution
----------------------------

Upon submitting a change or new work to a Pylons Project source Repository (a
"Contribution"), you agree to assign, and hereby do assign, a one-half
interest of all right, title and interest in and to copyright and other
intellectual property rights with respect to your new and original portions
of the Contribution to Agendaless Consulting. You and Agendaless Consulting
each agree that the other shall be free to exercise any and all exclusive
rights in and to the Contribution, without accounting to one another,
including without limitation, the right to license the Contribution to others
under the Repoze Public License. This agreement shall run with title to the
Contribution. Agendaless Consulting does not convey to you any right, title
or interest in or to the Program or such portions of the Contribution that
were taken from the Program. Your transmission of a submission to the Pylons
Project source Repository and marks of identification concerning the
Contribution itself constitute your intent to contribute and your assignment
of the work in accordance with the provisions of this Agreement.

License Terms
-------------

Code committed to the Pylons Project source repository (Committed Code) must
be governed by the Repoze Public License (http://repoze.org/LICENSE.txt, aka
"the RPL") or another license acceptable to Agendaless Consulting.  Until
Agendaless Consulting declares in writing an acceptable license other than
the RPL, only the RPL shall be used.  A list of exceptions is detailed within
the "Licensing Exceptions" section of this document, if one exists.

Representations, Warranty, and Indemnification
----------------------------------------------

Contributor represents and warrants that the Committed Code does not violate
the rights of any person or entity, and that the Contributor has legal
authority to enter into this Agreement and legal authority over Contributed
Code. Further, Contributor indemnifies Agendaless Consulting against
violations.

Cryptography
------------

Contributor understands that cryptographic code may be subject to government
regulations with which Agendaless Consulting and/or entities using Committed
Code must comply. Any code which contains any of the items listed below must
not be checked-in until Agendaless Consulting staff has been notified and has
approved such contribution in writing.

- Cryptographic capabilities or features

- Calls to cryptographic features

- User interface elements which provide context relating to cryptography

- Code which may, under casual inspection, appear to be cryptographic.

Notices
-------

Contributor confirms that any notices required will be included in any
Committed Code.

Licensing Exceptions
====================

None.

List of Contributors
====================

The below-signed are contributors to a code repository that is part of the
project named "Venusian".  Each below-signed contributor has read, understand
and agrees to the terms above in the section within this document entitled
"Pylons Project Contributor Agreement" as of the date beside his or her name.

Contributors
------------

- Chris McDonough, 2011/02/16
- Chris Withers, 2011/03/14
- Joel Bohman, 2011/07/28
- Olaf Conradi, 2013/09/16
- Wichert Akkerman, 2015/02/23
- Marc Abramowitz, 2015/02/24
- Bert JW Regeer, 2017-04-24
- Steve Piercy, 2017-08-31
venusian-1.2.0/setup.py0000664000175000017500000000463313415173400016226 0ustar  tseavertseaver00000000000000##############################################################################
#
# Copyright (c) 2010 Agendaless Consulting and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the BSD-like license at
# http://www.repoze.org/LICENSE.txt.  A copy of the license should accompany
# this distribution.  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
# FITNESS FOR A PARTICULAR PURPOSE
#
##############################################################################

import os

from setuptools import setup
from setuptools import find_packages

here = os.path.abspath(os.path.dirname(__file__))

try:
    with open(os.path.join(here, 'README.rst')) as f:
        README = f.read()
    with open(os.path.join(here, 'CHANGES.rst')) as f:
        CHANGES = f.read()
except:
    README = ''
    CHANGES = ''

testing_extras = [
    'pytest',
    'coverage',
    'pytest-cov',
]

docs_extras = [
    'Sphinx',
    'repoze.sphinx.autointerface'
]

setup(name='venusian',
      version='1.2.0',
      description='A library for deferring decorator actions',
      long_description=README + '\n\n' + CHANGES,
      classifiers=[
          "Development Status :: 6 - Mature",
          "Intended Audience :: Developers",
          "Programming Language :: Python",
          "Programming Language :: Python :: 2.7",
          "Programming Language :: Python :: 3",
          "Programming Language :: Python :: 3.4",
          "Programming Language :: Python :: 3.5",
          "Programming Language :: Python :: 3.6",
          "Programming Language :: Python :: 3.7",
          "Programming Language :: Python :: Implementation :: CPython",
          "Programming Language :: Python :: Implementation :: PyPy",
      ],
      keywords='web wsgi zope',
      author="Chris McDonough, Agendaless Consulting",
      author_email="pylons-devel@googlegroups.com",
      url="https://pylonsproject.org",
      license="BSD-derived (http://www.repoze.org/LICENSE.txt)",
      packages=find_packages(),
      include_package_data=True,
      zip_safe=False,
      extras_require={
          'testing': testing_extras,
          'docs': docs_extras,
      },
      tests_require=[],
      install_requires=[],
      test_suite='venusian',
      entry_points="""\
      """
      )