2
0
mirror of https://github.com/boostorg/python.git synced 2026-01-19 16:32:16 +00:00

- If you export a derived class without exporting its base classes, the derived class will explicitly export the bases's methods and attributes. Before, if you were interested in the bases's methods, you had to export the base classes too.

- Added a new function, no_override. When a member function is specified as "no_override", no virtual wrappers are generated for it, improving performance and letting the code more clean.

- There was a bug in which the policy of virtual member functions was being ignored (patch by Roman Sulzhyk).


[SVN r18814]
This commit is contained in:
Bruno da Silva de Oliveira
2003-06-17 01:34:26 +00:00
parent c821e903f8
commit 7ea2ab1672
19 changed files with 266 additions and 131 deletions

View File

@@ -1,3 +1,21 @@
16 June 2003
Thanks to discussions with David Abrahams and Roman Sulzhyk, some behaviours
have changed:
- If you export a derived class without exporting its base classes, the derived
class will explicitly export the bases's methods and attributes. Before, if
you were interested in the bases's methods, you had to export the base
classes too.
- Added a new function, no_override. When a member function is specified as
"no_override", no virtual wrappers are generated for it, improving
performance and letting the code more clean.
- There was a bug in which the policy of virtual member functions was being
ignored (patch by Roman Sulzhyk).
Thanks again to Roman Sulzhyk for the patches and discussion in the c++-sig.
4 June 2003
Major improvements in memory usage.

View File

@@ -165,8 +165,8 @@ invokes Pyste passing the interface files to it. Pyste then generates a single
cpp file with Boost.Python code, with all the classes and functions exported.
Besides declaring the classes and functions, the user has a number of other
options, like renaming classes and methods, excluding methods and attributes,
and so on.
options, like renaming e excluding classes and member functionis. Those are
explained later on.
[h2 Basics]
@@ -200,7 +200,7 @@ That will expose the class, the free function and the enum found in [^hello.h].
[page:1 Renaming and Excluding]
You can easily rename functions, classes, methods, attributes, etc. Just use the
You can easily rename functions, classes, member functions, attributes, etc. Just use the
function [^rename], like this:
World = Class("World", "hello.h")
@@ -208,7 +208,7 @@ function [^rename], like this:
show = Function("choice", "hello.h")
rename(show, "Show")
You can rename methods and attributes using this syntax:
You can rename member functions and attributes using this syntax:
rename(World.greet, "Greet")
rename(World.set, "Set")
@@ -216,7 +216,7 @@ You can rename methods and attributes using this syntax:
rename(choice.red, "Red")
rename(choice.blue, "Blue")
You can exclude functions, classes, methods, attributes, etc, in the same way,
You can exclude functions, classes, member functions, attributes, etc, in the same way,
with the function [^exclude]:
exclude(World.greet)
@@ -231,12 +231,25 @@ To access the operators of a class, access the member [^operator] like this
The string inside the brackets is the same as the name of the operator in C++.[br]
[h2 Virtual Member Functions]
Pyste automatically generates wrappers for virtual member functions, but you
may want to disable this behaviour (for performance reasons, or to let the code
more clean) if you do not plan to override the functions in Python. To do
this, use the function [^no_override]:
C = Class('C', 'C.h')
no_override(C.foo) # C::foo is a virtual member function
No wrapper code will be generated for the virtual member function C::foo that
way.
[page:1 Policies]
Even thought Pyste can identify various elements in the C++ code, like virtual
methods, attributes, and so on, one thing that it can't do is to guess the
semantics of functions that return pointers or references. In this case, the
user must manually specify the policy. Policies are explained in the
member functions, attributes, and so on, one thing that it can't do is to
guess the semantics of functions that return pointers or references. In this
case, the user must manually specify the policy. Policies are explained in the
[@../../doc/tutorial/doc/call_policies.html tutorial].
The policies in Pyste are named exactly as in Boost.Python, only the syntax is
@@ -248,22 +261,23 @@ becomes in Pyste:
return_internal_reference(1, with_custodian_and_ward(1, 2))
The user can specify policies for functions and methods with the [^set_policy]
function:
The user can specify policies for functions and virtual member functions with
the [^set_policy] function:
set_policy(f, return_internal_reference())
set_policy(C.foo, return_value_policy(manage_new_object))
[blurb
[$theme/note.gif] [*What if a function or method needs a policy and the user
doesn't set one?][br][br] If a function/method needs a policy and one was not
set, Pyste will issue a error. The user should then go in the interface file
and set the policy for it, otherwise the generated cpp won't compile.
[$theme/note.gif] [*What if a function or member function needs a policy and
the user doesn't set one?][br][br] If a function needs a policy and one
was not set, Pyste will issue a error. The user should then go in the
interface file and set the policy for it, otherwise the generated cpp won't
compile.
]
[blurb
[$theme/note.gif]
Note that, for functions/methods that return [^const T&], the policy
Note that, for functions that return [^const T&], the policy
[^return_value_policy<copy_const_reference>()] wil be used by default, because
that's normally what you want. You can change it to something else if you need
to, though.
@@ -371,9 +385,9 @@ You can optionally declare the function in the interface file itself:
names = Function("names", "test.h")
set_wrapper(names, names_wrapper)
The same mechanism can be used with methods too. Just remember that the first
parameter of wrappers for methods is a pointer to the class, like in
Boost.Python:
The same mechanism can be used with member functions too. Just remember that
the first parameter of wrappers for member functions is a pointer to the
class, as in:
struct C
{
@@ -394,7 +408,7 @@ And then in the interface file:
[$theme/note.gif]Even though Boost.Python accepts either a pointer or a
reference to the class in wrappers for member functions as the first parameter,
Pyste expects them to be a [*pointer]. Doing otherwise will prevent your
code to compile when you set a wrapper for a virtual method.
code to compile when you set a wrapper for a virtual member function.
]
[page:1 Exporting An Entire Header]
@@ -479,7 +493,8 @@ functions, and export those.
[page:1 Adding New Methods]
Suppose that you want to add a function to a class, turning it into a method:
Suppose that you want to add a function to a class, turning it into a member
function:
struct World
{
@@ -492,14 +507,14 @@ Suppose that you want to add a function to a class, turning it into a method:
return w.msg;
}
Here, we want to make [^greet] work as a method of the class [^World]. We do
Here, we want to make [^greet] work as a member function of the class [^World]. We do
that using the [^add_method] construct:
W = Class("World", "hello.h")
add_method(W, "greet")
Notice also that then you can rename it, set its policy, just like a regular
method:
member function:
rename(W.greet, 'Greet')

View File

@@ -6,7 +6,7 @@ from policies import *
from SingleCodeUnit import SingleCodeUnit
from EnumExporter import EnumExporter
from utils import makeid, enumerate
from copy import deepcopy
import copy
import exporterutils
import re
@@ -66,20 +66,16 @@ class ClassExporter(Exporter):
self.info.rename = decl.name
else:
self.class_ = decl
self.public_members = \
[x for x in self.class_.members if x.visibility == Scope.public]
self.class_ = copy.deepcopy(self.class_)
def ClassBases(self):
bases = []
def GetBases(class_):
this_bases = [self.GetDeclaration(x.name) for x in class_.bases]
bases.extend(this_bases)
for base in this_bases:
GetBases(base)
GetBases(self.class_)
return bases
all_bases = []
for level in self.class_.hierarchy:
for base in level:
all_bases.append(base)
return [self.GetDeclaration(x.name) for x in all_bases]
def Order(self):
@@ -91,6 +87,8 @@ class ClassExporter(Exporter):
def Export(self, codeunit, exported_names):
self.InheritMethods(exported_names)
self.MakeNonVirtual()
if not self.info.exclude:
self.CheckIsForwardDeclared()
self.CheckForwardDeclarations()
@@ -98,8 +96,8 @@ class ClassExporter(Exporter):
self.ExportBases(exported_names)
self.ExportConstructors()
self.ExportVariables()
self.ExportMethods()
self.ExportVirtualMethods()
self.ExportMethods()
self.ExportOperators()
self.ExportNestedClasses(exported_names)
self.ExportNestedEnums()
@@ -108,10 +106,38 @@ class ClassExporter(Exporter):
self.Write(codeunit)
def InheritMethods(self, exported_names):
'''Go up in the class hierarchy looking for classes that were not
exported yet, and then add their public members to this classes
members, as if they were members of this class. This allows the user to
just export one type and automatically get all the methods from the
base classes.
'''
valid_members = (Method, ClassVariable, NestedClass, ClassOperator,
ConverterOperator, ClassEnumeration)
for level in self.class_.hierarchy:
level_exported = False
for base in level:
base = self.GetDeclaration(base.name)
if base.FullName() not in exported_names:
for member in base.members:
if type(member) in valid_members:
member = copy.deepcopy(member)
#if type(member) not in (ClassVariable,:
# member.class_ = self.class_.FullName()
self.class_.members.append(member)
else:
level_exported = True
if level_exported:
break
self.public_members = \
[x for x in self.class_.members if x.visibility == Scope.public]
def CheckIsForwardDeclared(self):
if self.class_.incomplete:
print "--> Error: Class %s is forward declared! " \
"Please use the header with its complete definition." % self.class_.FullName()
"Please use the header with its complete definition." \
% self.class_.FullName()
print
@@ -191,15 +217,16 @@ class ClassExporter(Exporter):
def ExportBases(self, exported_names):
'Expose the bases of the class into the template section'
bases = self.class_.bases
bases_list = []
for base in bases:
if base.visibility == Scope.public and base.name in exported_names:
bases_list.append(base.name)
if bases_list:
code = namespaces.python + 'bases< %s > ' % \
(', '.join(bases_list))
self.Add('template', code)
hierarchy = self.class_.hierarchy
for level in hierarchy:
exported = []
for base in level:
if base.visibility == Scope.public and base.name in exported_names:
exported.append(base.name)
if exported:
code = namespaces.python + 'bases< %s > ' % (', '.join(exported))
self.Add('template', code)
return
def ExportConstructors(self):
@@ -374,6 +401,14 @@ class ClassExporter(Exporter):
self.Add('declaration', wrapper.code)
def MakeNonVirtual(self):
'''Make all methods that the user indicated to no_override no more virtual, delegating their
export to the ExportMethods routine'''
for member in self.class_.members:
if type(member) == Method and member.virtual:
member.virtual = not self.info[member.name].no_override
def ExportVirtualMethods(self):
# check if this class has any virtual methods
has_virtual_methods = False
@@ -565,7 +600,6 @@ class ClassExporter(Exporter):
nested_info.include = self.info.include
nested_info.name = nested_class.FullName()
exporter = ClassExporter(nested_info)
self.declarations.append(nested_class)
exporter.SetDeclarations(self.declarations)
codeunit = SingleCodeUnit(None, None)
exporter.Export(codeunit, exported_names)
@@ -579,7 +613,6 @@ class ClassExporter(Exporter):
enum_info.include = self.info.include
enum_info.name = enum.FullName()
exporter = EnumExporter(enum_info)
self.declarations.append(enum)
exporter.SetDeclarations(self.declarations)
codeunit = SingleCodeUnit(None, None)
exporter.Export(codeunit, None)
@@ -746,12 +779,17 @@ class _VirtualWrapperGenerator(object):
else:
pointer = method.PointerDeclaration()
# Add policy to overloaded methods also
policy = self.info[method.name].policy or ''
if policy:
policy = ', %s%s()' % (namespaces.python, policy.Code())
# generate the defs
definitions = []
# basic def
definitions.append('.def("%s", %s, %s)' % (rename, pointer, default_pointers[-1]))
definitions.append('.def("%s", %s, %s%s)' % (rename, pointer, default_pointers[-1], policy))
for default_pointer in default_pointers[:-1]:
definitions.append('.def("%s", %s)' % (rename, default_pointer))
definitions.append('.def("%s", %s%s)' % (rename, default_pointer, policy))
return definitions
@@ -766,7 +804,9 @@ class _VirtualWrapperGenerator(object):
This method creates the instance variable self.virtual_methods.
'''
def IsVirtual(m):
return type(m) is Method and m.virtual and m.visibility != Scope.private
return type(m) is Method and \
m.virtual and \
m.visibility != Scope.private
all_methods = [x for x in self.class_.members if IsVirtual(x)]
for base in self.bases:

View File

@@ -64,7 +64,7 @@ class Exporter:
def GetDeclaration(self, fullname):
decls = self.GetDeclarations(fullname)
assert len(decls) == 1
#assert len(decls) == 1
return decls[0]

View File

@@ -2,6 +2,7 @@ from declarations import *
from elementtree.ElementTree import ElementTree
from xml.parsers.expat import ExpatError
from copy import deepcopy
from utils import enumerate
class InvalidXMLError(Exception): pass
@@ -199,14 +200,18 @@ class GCCXMLParser(object):
self.ParseFunction(id, element, Operator)
def GetBases(self, bases):
'Parses the string "bases" from the xml into a list of Base instances.'
def GetHierarchy(self, bases):
'''Parses the string "bases" from the xml into a list of tuples of Base
instances. The first tuple is the most direct inheritance, and then it
goes up in the hierarchy.
'''
if bases is None:
return []
bases = bases.split()
baseobjs = []
for base in bases:
base_names = bases.split()
this_level = []
next_levels = []
for base in base_names:
# get the visibility
split = base.split(':')
if len(split) == 2:
@@ -214,10 +219,21 @@ class GCCXMLParser(object):
base = split[1]
else:
visib = Scope.public
decl = self.GetDecl(base)
baseobj = Base(decl.FullName(), visib)
baseobjs.append(baseobj)
return baseobjs
decl = self.GetDecl(base)
base = Base(decl.FullName(), visib)
this_level.append(base)
# normalize with the other levels
for index, level in enumerate(decl.hierarchy):
if index < len(next_levels):
next_levels[index] = next_levels[index] + level
else:
next_levels.append(level)
hierarchy = []
if this_level:
hierarchy.append(tuple(this_level))
if next_levels:
hierarchy.extend(next_levels)
return hierarchy
def GetMembers(self, members):
@@ -237,20 +253,22 @@ class GCCXMLParser(object):
context = self.GetDecl(element.get('context'))
incomplete = bool(element.get('incomplete', False))
if isinstance(context, str):
class_ = Class(name, context, [], abstract, [])
self.AddDecl(class_)
class_ = Class(name, context, [], abstract)
else:
# a nested class
visib = element.get('access', Scope.public)
class_ = NestedClass(
name, context.FullName(), visib, [], abstract, [])
name, context.FullName(), visib, [], abstract)
self.AddDecl(class_)
# we have to add the declaration of the class before trying
# to parse its members and bases, to avoid recursion.
class_.location = location
class_.incomplete = incomplete
self.Update(id, class_)
# now we can get the members and the bases
class_.bases = self.GetBases(element.get('bases'))
class_.hierarchy = self.GetHierarchy(element.get('bases'))
if class_.hierarchy:
class_.bases = class_.hierarchy[0]
class_.members = self.GetMembers(element.get('members'))
@@ -379,11 +397,10 @@ class GCCXMLParser(object):
context = self.GetDecl(element.get('context'))
if isinstance(context, str):
enum = Enumeration(name, context)
self.AddDecl(enum) # in this case, is a top level decl
else:
visib = element.get('access', Scope.public)
enum = ClassEnumeration(name, context.FullName(), visib)
self.AddDecl(enum)
enum.location = location
for child in element:
if child.tag == 'EnumValue':

View File

@@ -8,8 +8,6 @@ Module declarations
class Declaration(object):
'Represents a basic declaration.'
__slots__ = 'name namespace location incomplete'.split()
def __init__(self, name, namespace):
# the declaration name
self.name = name
@@ -44,15 +42,15 @@ class Declaration(object):
class Class(Declaration):
'The declaration of a class or struct.'
__slots__= 'members abstract bases _members_count'.split()
def __init__(self, name, namespace, members, abstract, bases):
def __init__(self, name, namespace, members, abstract):
Declaration.__init__(self, name, namespace)
# list of members
self.members = members
# whatever the class has any abstract methods
self.abstract = abstract
# instances of Base
self.bases = bases
self.bases = ()
self.hierarchy = ()
self._members_count = {}
@@ -117,10 +115,8 @@ class Class(Declaration):
class NestedClass(Class):
'The declaration of a class/struct inside another class/struct.'
__slots__= 'class_ visibility'.split()
def __init__(self, name, class_, visib, members, abstract, bases):
Class.__init__(self, name, None, members, abstract, bases)
def __init__(self, name, class_, visib, members, abstract):
Class.__init__(self, name, None, members, abstract)
self.class_ = class_
self.visibility = visib
@@ -133,8 +129,6 @@ class NestedClass(Class):
class Base:
'Represents a base class of another class.'
__slots__= 'name visibility'.split()
def __init__(self, name, visibility=None):
# class_ is the full name of the base class
self.name = name
@@ -155,8 +149,6 @@ class Scope:
class Function(Declaration):
'The declaration of a function.'
__slots__= 'result parameters'.split()
def __init__(self, name, namespace, result, params):
Declaration.__init__(self, name, namespace)
# the result type: instance of Type, or None (constructors)
@@ -206,8 +198,6 @@ class Operator(Function):
class Method(Function):
'The declaration of a method.'
__slots__= 'visibility virtual abstract static class_ const'.split()
def __init__(self, name, class_, result, params, visib, virtual, abstract, static, const):
Function.__init__(self, name, None, result, params)
self.visibility = visib
@@ -301,8 +291,6 @@ class ConverterOperator(ClassOperator):
class Type(Declaration):
'Represents a type.'
__slots__= 'const default volatile restricted incomplete'.split()
def __init__(self, name, const=False, default=None, incomplete=False):
Declaration.__init__(self, name, None)
# whatever the type is constant or not
@@ -339,8 +327,6 @@ class Type(Declaration):
class ArrayType(Type):
'Represents an array.'
__slots__= 'min max'.split()
def __init__(self, name, const=False, default=None, incomplete=False):
'min and max can be None.'
Type.__init__(self, name, const)
@@ -358,8 +344,6 @@ class ArrayType(Type):
class ReferenceType(Type):
'A reference type.'
__slots__= 'expand'.split()
def __init__(self, name, const=False, default=None, incomplete=False, expandRef=True):
Type.__init__(self, name, const, default, incomplete)
self.expand = expandRef
@@ -383,8 +367,6 @@ class ReferenceType(Type):
class PointerType(Type):
'A pointer type.'
__slots__= 'expand'.split()
def __init__(self, name, const=False, default=None, incomplete=False, expandPointer=False):
Type.__init__(self, name, const, default, incomplete)
self.expand = expandPointer
@@ -415,8 +397,6 @@ class FundamentalType(Type):
class FunctionType(Type):
'A pointer to a function.'
__slots__= 'result parameters name'.split()
def __init__(self, result, parameters):
Type.__init__(self, '', False)
self.result = result
@@ -438,8 +418,6 @@ class FunctionType(Type):
class MethodType(FunctionType):
'A pointer to a member function of a class.'
__slots__= 'result parameters class_ name'.split()
def __init__(self, result, parameters, class_):
Type.__init__(self, '', False)
self.result = result
@@ -460,8 +438,6 @@ class MethodType(FunctionType):
class Variable(Declaration):
'Represents a global variable.'
__slots__= 'type'.split()
def __init__(self, type, name, namespace):
Declaration.__init__(self, name, namespace)
# instance of Type
@@ -472,8 +448,6 @@ class Variable(Declaration):
class ClassVariable(Variable):
'Represents a class variable.'
__slots__= 'visibility static class_'.split()
def __init__(self, type, name, class_, visib, static):
Variable.__init__(self, type, name, None)
self.visibility = visib
@@ -488,8 +462,6 @@ class ClassVariable(Variable):
class Enumeration(Declaration):
__slots__= 'values'.split()
def __init__(self, name, namespace):
Declaration.__init__(self, name, namespace)
self.values = {} # dict of str => int
@@ -505,8 +477,6 @@ class Enumeration(Declaration):
class ClassEnumeration(Enumeration):
__slots__= 'class_ visibility'.split()
def __init__(self, name, class_, visib):
Enumeration.__init__(self, name, None)
self.class_ = class_
@@ -525,8 +495,6 @@ class ClassEnumeration(Enumeration):
class Typedef(Declaration):
__slots__= 'type visibility'.split()
def __init__(self, type, name, namespace):
Declaration.__init__(self, name, namespace)
self.type = type
@@ -541,8 +509,6 @@ class Union(Declaration):
class ClassUnion(Union):
__slots__= 'class_ visibility'.split()
def __init__(self, name, class_, visib):
Union.__init__(self, name, None)
self.class_ = class_

View File

@@ -215,3 +215,6 @@ def add_method(info, name, rename=None):
info._Attribute('__added__', [(name, rename)])
else:
added.append((name, rename))
def no_override(info):
info._Attribute('no_override', True)

View File

@@ -139,6 +139,7 @@ def CreateContext():
context['use_shared_ptr'] = infos.use_shared_ptr
context['use_auto_ptr'] = infos.use_auto_ptr
context['add_method'] = infos.add_method
context['no_override'] = infos.no_override
# policies
context['return_internal_reference'] = return_internal_reference
context['with_custodian_and_ward'] = with_custodian_and_ward

3
pyste/tests/inherit.cpp Normal file
View File

@@ -0,0 +1,3 @@
#include "inherit.h"
int inherit::C::s = 1;

View File

@@ -1,3 +1,6 @@
namespace inherit {
template<typename T>
class A
{
@@ -16,3 +19,21 @@ class B : public A<int>
public:
int go() { return get(); }
};
struct C : B
{
enum ab { a = 1, b = 2 };
int f1() { return 1; }
int x;
static int s;
};
struct D : C
{
int f2() { return 2; }
int y;
};
struct X {};
struct E: X, D {};
}

View File

@@ -1,8 +1,8 @@
# Doesn't work:
A = Template('A', 'inherit.h')
A_int = A('int')
A = Template('inherit::A', 'inherit.h')
A_int = A('int', 'A_int')
Class('B', 'inherit.h')
# Does work:
#AllFromHeader('inherit.h')
Class('inherit::B', 'inherit.h')
Class('inherit::D', 'inherit.h')
E = Class('inherit::E', 'inherit.h')
exclude(E.s)
exclude(E.ab)

View File

@@ -14,6 +14,15 @@ class InheritExampleTest(unittest.TestCase):
self.assertEqual(b.go(), 1)
self.assertEqual(b.get(), 1)
d = D()
self.assert_(issubclass(D, B))
self.assertEqual(d.x, 0)
self.assertEqual(d.y, 0)
self.assertEqual(d.s, 1)
self.assertEqual(D.s, 1)
self.assertEqual(d.f1(), 1)
self.assertEqual(d.f2(), 2)
if __name__ == '__main__':

7
pyste/tests/vars.cpp Normal file
View File

@@ -0,0 +1,7 @@
#include "vars.h"
const Color black = Color(0, 0, 0);
const Color red = Color(255, 0, 0);
const Color green = Color(0, 255, 0);
const Color blue = Color(0, 0, 255);
Color in_use = black;

View File

@@ -15,6 +15,7 @@ public:
{
return name();
}
virtual int dummy() { return 0; }
protected:
virtual int f_abs() = 0;
@@ -23,6 +24,13 @@ private:
virtual const char* name() { return "C"; }
};
struct D
{
virtual int dummy() { return 0; }
};
inline int call_f(C& c) { return c.f(); }
inline int call_dummy(C* c) { return c->dummy(); }
inline int call_dummy(D* d) { return d->dummy(); }
}

View File

@@ -1,2 +1,6 @@
Class('virtual_::C', 'virtual.h')
C = Class('virtual_::C', 'virtual.h')
no_override(C.dummy)
D = Class('virtual_::D', 'virtual.h')
no_override(D.dummy)
Function('virtual_::call_f', 'virtual.h')
Function('virtual_::call_dummy', 'virtual.h')

View File

@@ -5,12 +5,14 @@ struct A
{
virtual int f() { return 0; }
virtual int f1() { return 10; }
virtual A* make_new() { return new A; }
};
struct B: A
{
virtual int f() { return 1; }
virtual int f2() { return 20; }
virtual A* make_new() { return new B; }
};
inline int call_fs(A*a)

View File

@@ -1,4 +1,6 @@
Class('virtual2::A', 'virtual2.h')
Class('virtual2::B', 'virtual2.h')
A = Class('virtual2::A', 'virtual2.h')
set_policy(A.make_new, return_value_policy(manage_new_object))
B = Class('virtual2::B', 'virtual2.h')
set_policy(B.make_new, return_value_policy(manage_new_object))
Function('virtual2::call_fs', 'virtual2.h')
Function('virtual2::call_f', 'virtual2.h')

View File

@@ -12,7 +12,14 @@ class Virtual2Test(unittest.TestCase):
self.assertEqual(call_fs(b), 30)
self.assertEqual(call_f(a), 0)
self.assertEqual(call_f(b), 1)
nb = b.make_new()
na = a.make_new()
self.assertEqual(na.f1(), 10)
self.assertEqual(nb.f1(), 10)
self.assertEqual(nb.f2(), 20)
self.assertEqual(call_fs(nb), 30)
self.assertEqual(call_f(na), 0)
self.assertEqual(call_f(nb), 1)
class C(B):
def f1(self): return 1
def f2(self): return 2

View File

@@ -5,33 +5,45 @@ class VirtualTest(unittest.TestCase):
def testIt(self):
class D(C):
class E(C):
def f_abs(self):
return 3
def dummy(self):
# override should not work
return 100
class E(C):
class F(C):
def f(self):
return 10
def name(self):
return 'E'
return 'F'
class G(D):
def dummy(self):
# override should not work
return 100
d = D()
e = E()
f = F()
self.assertEqual(d.f(), 3)
self.assertEqual(call_f(d), 3)
self.assertEqual(e.f(), 10)
self.assertEqual(call_f(e), 10)
self.assertEqual(d.get_name(), 'C')
self.assertEqual(e.f(), 3)
self.assertEqual(call_f(e), 3)
self.assertEqual(f.f(), 10)
self.assertEqual(call_f(f), 10)
self.assertEqual(e.get_name(), 'C')
#self.assertEqual(e.get_name(), 'E') check this later
c = C()
def bar(arg):
c.bar(arg)
bar(1) # ok
bar('a') # ok
self.assertRaises(TypeError, bar, 1.0)
c.bar(1) # ok
c.bar('a') # ok
self.assertRaises(TypeError, c.bar, 1.0)
# test no_overrides
d = G()
self.assertEqual(e.dummy(), 100)
self.assertEqual(call_dummy(e), 0)
self.assertEqual(d.dummy(), 100)
self.assertEqual(call_dummy(d), 0)