Example #1
0
    def _get_object (options):
        """
        Return Python expression for object that should be passed to various user
        functions.  This is either C{"self"} or C{"self.some_attribute"} if C{options}
        dictionary contains C{"object"} key.  In this case C{"some_attribute"} string is
        evaluated to a private attribute name for the class being defined.

        This is a helper for C{L{_generate_derived_type_dictionary}} only.  It should not
        be called from outside.

        @param options: the same value as passed to C{_generate_derived_type_dictionary}.
        @type  options: C{dict}

        @rtype:         C{str}
        """

        if 'object' in options:
            return 'self.%s' % mangle_identifier (options['new_class_name'], options['object'])
        else:
            return 'self'
Example #2
0
    def _get_object(options):
        """
        Return Python expression for object that should be passed to various user
        functions.  This is either C{"self"} or C{"self.some_attribute"} if C{options}
        dictionary contains C{"object"} key.  In this case C{"some_attribute"} string is
        evaluated to a private attribute name for the class being defined.

        This is a helper for C{L{_generate_derived_type_dictionary}} only.  It should not
        be called from outside.

        @param options: the same value as passed to C{_generate_derived_type_dictionary}.
        @type  options: C{dict}

        @rtype:         C{str}
        """

        if 'object' in options:
            return 'self.%s' % mangle_identifier(options['new_class_name'],
                                                 options['object'])
        else:
            return 'self'
Example #3
0
    def _generate_derived_type_dictionary (cls, options):
        """
        Generate an iterable of pairs in the form (Python identifier, value) for a new
        type created by C{L{derive_type}}.  Exact pairs should be influenced by
        C{options}, which are C{options} as passed to C{derive_type} plus C{cls} (for
        convenience) and C{new_class_name}.

        This method is not meant to be callable from outside, use C{L{derive_type}} for
        that instead.

        Overriden implementations of this method are recommended but not required to be
        generator functions.  They should generally start like this:

            >>> def _generate_derived_type_dictionary (cls, options):
            ...     for attribute in super (..., cls)._generate_derived_type_dictionary (options):
            ...         yield attribute
            ...
            ...     ...

        That is only an approximation and you can, for instance, change or override
        attributes returned by super-method.

        C{options} dictionary is constructed in such a way you should be able to evaluate
        all function-defining statements in it.  For instance, you can write own
        C{_generate_derived_type_dictionary} like this:

            >>> def _generate_derived_type_dictionary (cls, options):
            ...     ...
            ...
            ...     functions = {}
            ...
            ...     if 'foo_value' in options:
            ...         exec 'def foo (self): return foo_value' \
            ...              in { 'foo_value': options['foo_value'] }, functions
            ...
            ...     ...
            ...
            ...     for function in functions.iteritems ():
            ...         yield function

        Returned value for C{__slots__} is treated specially.  While normally values
        associated with the same name override previous values, values for C{__slots__}
        are combined into a tuple instead.

        Note that it is not recommended to use C{options} for execution globals or locals
        dictionary directly.  This way your code may become vulnerable to other option
        addition, e.g. for some derivative of the class.  For instance, you may use
        C{property} built-in, then setting it in C{options} will hide the built-in from
        your code.  Consider using C{L{_filter_options}} utility method.

        @param options:    dictionary of options passed to C{L{derive_type}} method, plus
                           C{cls} and C{new_class_name}.
        @type  options:    C{dict}

        @rtype:            iterable
        @returns:          Pairs of (Python identifier, value) for the new type.

        @raises exception: if there is any error in C{options}.
        """

        functions        = {}
        filtered_options = AbstractValueObject._filter_options (options, 'cls', 'getter', 'setter')


        if 'object' in options:
            object = options['object']
            if not is_valid_identifier (object):
                raise ValueError ("'%s' is not a valid Python identifier" % object)

            yield '__slots__', mangle_identifier (options['new_class_name'], object)

            execute (('def __init__(self, %s):\n'
                      '    cls.__init__(self)\n'
                      '    %s = %s')
                     % (object, AbstractValueObject._get_object (options), object),
                     filtered_options, functions)

            if 'property' in options:
                property = options['property']
                if property == object:
                    raise ValueError ("'property' option cannot be the same as 'object'")

                if not is_valid_identifier (property):
                    raise ValueError ("'%s' is not a valid Python identifier" % property)

                execute ('%s = property (lambda self: %s)'
                         % (mangle_identifier (options['new_class_name'], property),
                            AbstractValueObject._get_object (options)),
                         functions)

        else:
            if 'property' in options:
                raise ValueError ("'property' without 'object' option doesn't make sense")

        if 'dict' in options and options['dict']:
            # Gracefully ignore if this type already has a dict.
            if not _type_has_dictionary (cls):
                yield '__slots__', '__dict__'

        if 'getter' in options:
            if not is_callable (options['getter']):
                raise TypeError ("'getter' must be a callable")

            execute ('def get (self): return getter (%s)'
                     % AbstractValueObject._get_object (options),
                     filtered_options, functions)

        if 'setter' in options:
            if not is_callable (options['setter']):
                raise TypeError ("'setter' must be a callable")

            execute ('def set (self, value): return setter (%s, value)'
                     % AbstractValueObject._get_object (options),
                     filtered_options, functions)

        for function in functions.items ():
            yield function
Example #4
0
    def _generate_derived_type_dictionary(cls, options):
        """
        Generate an iterable of pairs in the form (Python identifier, value) for a new
        type created by C{L{derive_type}}.  Exact pairs should be influenced by
        C{options}, which are C{options} as passed to C{derive_type} plus C{cls} (for
        convenience) and C{new_class_name}.

        This method is not meant to be callable from outside, use C{L{derive_type}} for
        that instead.

        Overriden implementations of this method are recommended but not required to be
        generator functions.  They should generally start like this:

            >>> def _generate_derived_type_dictionary (cls, options):
            ...     for attribute in super (..., cls)._generate_derived_type_dictionary (options):
            ...         yield attribute
            ...
            ...     ...

        That is only an approximation and you can, for instance, change or override
        attributes returned by super-method.

        C{options} dictionary is constructed in such a way you should be able to evaluate
        all function-defining statements in it.  For instance, you can write own
        C{_generate_derived_type_dictionary} like this:

            >>> def _generate_derived_type_dictionary (cls, options):
            ...     ...
            ...
            ...     functions = {}
            ...
            ...     if 'foo_value' in options:
            ...         exec 'def foo (self): return foo_value' \
            ...              in { 'foo_value': options['foo_value'] }, functions
            ...
            ...     ...
            ...
            ...     for function in functions.iteritems ():
            ...         yield function

        Returned value for C{__slots__} is treated specially.  While normally values
        associated with the same name override previous values, values for C{__slots__}
        are combined into a tuple instead.

        Note that it is not recommended to use C{options} for execution globals or locals
        dictionary directly.  This way your code may become vulnerable to other option
        addition, e.g. for some derivative of the class.  For instance, you may use
        C{property} built-in, then setting it in C{options} will hide the built-in from
        your code.  Consider using C{L{_filter_options}} utility method.

        @param options:    dictionary of options passed to C{L{derive_type}} method, plus
                           C{cls} and C{new_class_name}.
        @type  options:    C{dict}

        @rtype:            iterable
        @returns:          Pairs of (Python identifier, value) for the new type.

        @raises exception: if there is any error in C{options}.
        """

        functions = {}
        filtered_options = AbstractValueObject._filter_options(
            options, 'cls', 'getter', 'setter')

        if 'object' in options:
            object = options['object']
            if not is_valid_identifier(object):
                raise ValueError("'%s' is not a valid Python identifier" %
                                 object)

            yield '__slots__', mangle_identifier(options['new_class_name'],
                                                 object)

            execute(('def __init__(self, %s):\n'
                     '    cls.__init__(self)\n'
                     '    %s = %s') %
                    (object, AbstractValueObject._get_object(options), object),
                    filtered_options, functions)

            if 'property' in options:
                property = options['property']
                if property == object:
                    raise ValueError(
                        "'property' option cannot be the same as 'object'")

                if not is_valid_identifier(property):
                    raise ValueError("'%s' is not a valid Python identifier" %
                                     property)

                execute(
                    '%s = property (lambda self: %s)' %
                    (mangle_identifier(options['new_class_name'], property),
                     AbstractValueObject._get_object(options)), functions)

        else:
            if 'property' in options:
                raise ValueError(
                    "'property' without 'object' option doesn't make sense")

        if 'dict' in options and options['dict']:
            # Gracefully ignore if this type already has a dict.
            if not _type_has_dictionary(cls):
                yield '__slots__', '__dict__'

        if 'getter' in options:
            if not is_callable(options['getter']):
                raise TypeError("'getter' must be a callable")

            execute(
                'def get (self): return getter (%s)' %
                AbstractValueObject._get_object(options), filtered_options,
                functions)

        if 'setter' in options:
            if not is_callable(options['setter']):
                raise TypeError("'setter' must be a callable")

            execute(
                'def set (self, value): return setter (%s, value)' %
                AbstractValueObject._get_object(options), filtered_options,
                functions)

        for function in functions.items():
            yield function
Example #5
0
    def test_mangle_identifier (self):
        self.assertEqual (mangle_identifier ('Foo',     'bar'),     'bar')
        self.assertEqual (mangle_identifier ('_Foo',    'bar'),     'bar')
        self.assertEqual (mangle_identifier ('__Foo',   'bar'),     'bar')
        self.assertEqual (mangle_identifier ('__Foo__', 'bar'),     'bar')
        self.assertEqual (mangle_identifier ('Foo',     '_bar'),    '_bar')
        self.assertEqual (mangle_identifier ('_Foo',    '_bar'),    '_bar')
        self.assertEqual (mangle_identifier ('__Foo',   '_bar'),    '_bar')
        self.assertEqual (mangle_identifier ('__Foo__', '_bar'),    '_bar')
        self.assertEqual (mangle_identifier ('Foo',     '__bar'),   '_Foo__bar')
        self.assertEqual (mangle_identifier ('_Foo',    '__bar'),   '_Foo__bar')
        self.assertEqual (mangle_identifier ('__Foo',   '__bar'),   '_Foo__bar')
        self.assertEqual (mangle_identifier ('__Foo__', '__bar'),   '_Foo____bar')
        self.assertEqual (mangle_identifier ('Foo',     '__bar__'), '__bar__')
        self.assertEqual (mangle_identifier ('_Foo',    '__bar__'), '__bar__')
        self.assertEqual (mangle_identifier ('__Foo',   '__bar__'), '__bar__')
        self.assertEqual (mangle_identifier ('__Foo__', '__bar__'), '__bar__')

        # Special cases.
        self.assertEqual (mangle_identifier ('_',     '__bar'),   '__bar')
        self.assertEqual (mangle_identifier ('__',    '__bar'),   '__bar')
        self.assertEqual (mangle_identifier ('___',   '__bar'),   '__bar')
        self.assertEqual (mangle_identifier ('Foo',   '__'),      '__')
        self.assertEqual (mangle_identifier ('___',   '_____'),   '_____')