"""
Docstring needed

"""

from datetime import datetime, date, time, timedelta
from numbers import Number
from decimal import Decimal
import numpy

ALLOWED_ANNOTATION_TYPES = (int, float, complex,
                            str, bytes,
                            type(None),
                            datetime, date, time, timedelta,
                            Number, Decimal,
                            numpy.number, numpy.complex, numpy.bool)

# handle both Python 2 and Python 3
try:
    ALLOWED_ANNOTATION_TYPES += (long, unicode)
except NameError:
    pass


def _check_annotations(value):
    """
    Recursively check that value is either of a "simple" type (number, string,
    date/time) or is a (possibly nested) dict, list or numpy array containing
    only simple types.
    """
    if isinstance(value, dict):
        for element in value.values():
            _check_annotations(element)
    elif isinstance(value, (list, tuple)):
        for element in value:
            _check_annotations(element)
    elif (not isinstance(value, ALLOWED_ANNOTATION_TYPES) and
          not issubclass(value, ALLOWED_ANNOTATION_TYPES)):
        raise ValueError("Invalid annotation. Annotations of type %s are not \
                         allowed" % type(value))
    elif isinstance(value, numpy.ndarray):
        if ((not isinstance(value.dtype.type, ALLOWED_ANNOTATION_TYPES) and
                not issubclass(value.dtype.type, ALLOWED_ANNOTATION_TYPES)) or
                (isinstance(value.dtype.type, numpy.string_) or
                 issubclass(value.dtype.type, numpy.string_))):
            raise ValueError("Invalid annotation. NumPy arrays with dtype %s \
                             are not allowed" % value.dtype)


class BaseNeo(object):
    """This is the base class from which all Neo objects inherit.

    This class implements support for universally recommended arguments,
    and also sets up the `annotations` dict for additional arguments.

    The following "universal" methods are available:
        __init__ : Grabs the universally recommended arguments `name`,
            `file_origin`, `description` and stores as attributes.

            Also stores every additional argument (that is, every argument
            that is not handled by BaseNeo or the child class), and puts
            in the dict `annotations`.

        annotate(**args) : Updates `annotations` with keyword/value pairs.

    Each child class should:
        0) call BaseNeo.__init__(self, name=name, file_origin=file_origin,
                                 description=description, **annotations)
           with the universal recommended arguments, plus optional annotations
        1) process its required arguments in its __new__ or __init__ method
        2) process its non-universal recommended arguments (in its __new__ or
           __init__ method

    Non-keyword arguments should only be used for required arguments.

    The required and recommended arguments for each child class (Neo object)
    are specified in ../description.py and the documentation for the child.
    """

    def __init__(self, name=None, file_origin=None, description=None,
                 **annotations):
        """This is the base constructor for all Neo objects.

        Stores universally recommended attributes and creates `annotations`
        from additional arguments not processed by BaseNeo or the child class.
        """
        # create `annotations` for additional arguments
        _check_annotations(annotations)
        self.annotations = annotations

        # these attributes are recommended for all objects.
        self.name = name
        self.description = description
        self.file_origin = file_origin

    def annotate(self, **annotations):
        """
        Add annotations (non-standardized metadata) to a Neo object.

        Example:

        >>> obj.annotate(key1=value1, key2=value2)
        >>> obj.key2
        value2
        """
        _check_annotations(annotations)
        self.annotations.update(annotations)

@@ -429,27 +429,66 @@
 mpy.
-complex, numpy.bool
+bool_)%0ADISALLOWED_ANNOTATION_DTYPES = (str, numpy.flexible
 )%0A%0A#
@@ -575,16 +575,64 @@
 nicode)%0A
+    DISALLOWED_ANNOTATION_DTYPES += (unicode, )%0A
 except N
@@ -908,236 +908,66 @@
 ue, 
-dict):%0A        for element in value.values():%0A            _check_annotations(element)%0A    elif isinstance(value, (list, tuple)):%0A        for element in value:%0A            _check_annotations(element)%0A    elif (not isinstance(valu
+numpy.ndarray):%0A        if (not issubclass(value.dtype.typ
 e, A
@@ -983,35 +983,34 @@
 NOTATION_TYPES) 
-and
+or
 %0A          not i
@@ -996,35 +996,37 @@
 S) or%0A          
-not
+     
  issubclass(valu
@@ -1026,18 +1026,32 @@
 ss(value
-, 
+.dtype.type, DIS
 ALLOWED_
@@ -1057,24 +1057,25 @@
 _ANNOTATION_
+D
 TYPES)):%0A   
@@ -1067,24 +1067,28 @@
 N_DTYPES)):%0A
+    
         rais
@@ -1125,41 +1125,41 @@
 on. 
-Annotations of type %25s are not %5C%0A
+NumPy arrays with dtype %25s%22%0A     
     
@@ -1178,16 +1178,24 @@
         
+%22are not
  allowed
@@ -1202,19 +1202,24 @@
 %22 %25 
-type(value)
+value.dtype.type
 )%0A  
@@ -1247,445 +1247,391 @@
 ue, 
-numpy.ndarray):%0A        if ((not isinstance(value.dtype.type, ALLOWED_ANNOTATION_TYPES) and%0A                not issubclass(value.dtype.type, ALLOWED_ANNOTATION_TYPES)) or%0A                (isinstance(value.dtype.type, numpy.string_) or%0A                 issubclass(value.dtype.type, numpy.string_))):%0A            raise ValueError(%22Invalid annotation. NumPy arrays with dtype %25s %5C%0A                             are not allowed%22 %25 value.dtype
+dict):%0A        for element in value.values():%0A            _check_annotations(element)%0A    elif isinstance(value, (list, tuple)):%0A        for element in value:%0A            _check_annotations(element)%0A    elif not isinstance(value, ALLOWED_ANNOTATION_TYPES):%0A        raise ValueError(%22Invalid annotation. Annotations of type %25s are not%22%0A                         %22allowed%22 %25 type(value)
 )%0A%0A%0A

fix some problems found while setting up the unittest