diff --git a/pyste/NEWS b/pyste/NEWS index ab1c5bcb..5ba48563 100644 --- a/pyste/NEWS +++ b/pyste/NEWS @@ -1,3 +1,12 @@ +10 August 2003 +Support for incremental generation of the code has been added. This changes +how --multiple works; documentation of this new feature will follow. Thanks +to Prabhu Ramachandran, that saw the need for this feature and discussed a +solution. + +Automatically convert \ to / in Windows systems before passing the paths to +gccxml. + 7 July 2003 Applied 2 patches by Prabhu Ramachandran: a fix in the new --multiple method, and two new functions "hold_with_shared_ptr" and its counterpart for auto_ptr. diff --git a/pyste/TODO b/pyste/TODO index de588b14..75ae99bd 100644 --- a/pyste/TODO +++ b/pyste/TODO @@ -8,3 +8,5 @@ instance) - Virtual operators + +- Apply Gottfried patches diff --git a/pyste/src/Pyste/ClassExporter.py b/pyste/src/Pyste/ClassExporter.py index fdcf579d..6b8f389c 100644 --- a/pyste/src/Pyste/ClassExporter.py +++ b/pyste/src/Pyste/ClassExporter.py @@ -698,17 +698,25 @@ class _VirtualWrapperGenerator(object): # default implementations (with overloading) def DefaultImpl(method, param_names): 'Return the body of a default implementation wrapper' + indent2 = indent * 2 wrapper = self.info[method.name].wrapper if not wrapper: # return the default implementation of the class - return '%s%s(%s);\n' % \ + if method.abstract: + s = indent2 + 'PyErr_SetString(PyExc_RuntimeError, "abstract function called");\n' +\ + indent2 + 'throw_error_already_set();\n' + if method.result.FullName() != 'void': + s += indent2 + 'return %s();\n' % method.result.FullName() + return s + else: + return indent2 + '%s%s(%s);\n' % \ (return_str, method.FullName(), ', '.join(param_names)) else: # return a call for the wrapper params = ', '.join(['this'] + param_names) - return '%s%s(%s);\n' % (return_str, wrapper.FullName(), params) + return indent2 + '%s%s(%s);\n' % (return_str, wrapper.FullName(), params) - if not method.abstract and method.visibility != Scope.private: + if method.visibility != Scope.private: minArgs = method.minArgs maxArgs = method.maxArgs impl_names = self.DefaultImplementationNames(method) @@ -716,7 +724,7 @@ class _VirtualWrapperGenerator(object): params, param_names, param_types = _ParamsInfo(method, argNum) decl += '\n' decl += indent + '%s %s(%s)%s {\n' % (result, impl_name, params, constantness) - decl += indent*2 + DefaultImpl(method, param_names) + decl += DefaultImpl(method, param_names) decl += indent + '}\n' return decl @@ -823,7 +831,7 @@ class _VirtualWrapperGenerator(object): for method in self.virtual_methods: exclude = self.info[method.name].exclude # generate definitions only for public methods and non-abstract methods - if method.visibility == Scope.public and not method.abstract and not exclude: + if method.visibility == Scope.public and not exclude: defs.extend(self.MethodDefinition(method)) return defs diff --git a/pyste/src/Pyste/CppParser.py b/pyste/src/Pyste/CppParser.py index 04f1ed2c..d07dc118 100644 --- a/pyste/src/Pyste/CppParser.py +++ b/pyste/src/Pyste/CppParser.py @@ -36,6 +36,7 @@ class CppParser: self.delete_cache = False self.cache_dir = cache_dir self.cache_files = [] + self.mem_cache = {} # create the cache dir if cache_dir: try: @@ -142,8 +143,7 @@ class CppParser: declarations = self.GetCache(header, interface, tail) if declarations is None: declarations = self.ParseWithGCCXML(header, tail) - if self.cache_dir is not None: - self.CreateCache(header, interface, tail, declarations) + self.CreateCache(header, interface, tail, declarations) return declarations, header @@ -158,13 +158,19 @@ class CppParser: def GetCache(self, header, interface, tail): if self.cache_dir is None: return None + + key = (header, interface, tail) + # try memory cache first + if key in self.mem_cache: + return self.mem_cache[key] + + # get the cache from the disk header = self.FindHeader(header) cache_file = self.CacheFileName(interface) if os.path.isfile(cache_file): f = file(cache_file, 'rb') try: cache = load(f) - key = (header, interface, tail) if cache.has_key(key): self.cache_files.append(cache_file) return cache[key] @@ -177,6 +183,15 @@ class CppParser: def CreateCache(self, header, interface, tail, declarations): + key = (header, interface, tail) + + # our memory cache only holds one item + self.mem_cache.clear() + self.mem_cache[key] = declarations + + # save the cache in the disk + if self.cache_dir is None: + return header = self.FindHeader(header) cache_file = self.CacheFileName(interface) if os.path.isfile(cache_file): @@ -187,7 +202,6 @@ class CppParser: f.close() else: cache = {} - key = (header, interface, tail) cache[key] = declarations self.cache_files.append(cache_file) f = file(cache_file, 'wb') diff --git a/pyste/src/Pyste/pyste.py b/pyste/src/Pyste/pyste.py index a9db4954..e6ce18c6 100644 --- a/pyste/src/Pyste/pyste.py +++ b/pyste/src/Pyste/pyste.py @@ -41,8 +41,9 @@ import sys from policies import * from CppParser import CppParser, CppParserError import time +from declarations import Typedef -__VERSION__ = '0.9.10' +__VERSION__ = '0.9.11' def RecursiveIncludes(include): 'Return a list containg the include dir and all its subdirectories' @@ -63,6 +64,14 @@ def GetDefaultIncludes(): return [] +def ProcessIncludes(includes): + if sys.platform == 'win32': + index = 0 + for include in includes: + includes[index] = include.replace('\\', '/') + index += 1 + + def ParseArguments(): def Usage(): @@ -147,6 +156,7 @@ def ParseArguments(): Usage() sys.exit(3) + ProcessIncludes(includes) return includes, defines, module, out, files, multiple, cache_dir, create_cache, generate_main @@ -313,6 +323,7 @@ def GenerateCode(parser, module, out, interfaces, multiple): else: declarations = [] parsed_header = None + ExpandTypedefs(declarations, exported_names) export.SetDeclarations(declarations) export.SetParsedHeader(parsed_header) if multiple: @@ -330,6 +341,15 @@ def GenerateCode(parser, module, out, interfaces, multiple): return 0 +def ExpandTypedefs(declarations, exported_names): + '''Check if the names in exported_names are a typedef, and add the real class + name in the dict. + ''' + for name in exported_names.keys(): + for decl in declarations: + if isinstance(decl, Typedef): + exported_names[decl.type.FullName()] = None + def UsePsyco(): 'Tries to use psyco if possible' try: diff --git a/pyste/tests/abstract_test.h b/pyste/tests/abstract_test.h new file mode 100644 index 00000000..4ac2b99b --- /dev/null +++ b/pyste/tests/abstract_test.h @@ -0,0 +1,17 @@ +#include +#include + +namespace abstract { + +struct A { + virtual ~A() {} + virtual std::string f()=0; +}; + +struct B: A { + std::string f() { return "B::f"; } +}; + +std::string call(A* a) { return a->f(); } + +} diff --git a/pyste/tests/abstract_test.pyste b/pyste/tests/abstract_test.pyste new file mode 100644 index 00000000..c65bb3ad --- /dev/null +++ b/pyste/tests/abstract_test.pyste @@ -0,0 +1,3 @@ +Class('abstract::A', 'abstract_test.h') +Class('abstract::B', 'abstract_test.h') +Function('abstract::call', 'abstract_test.h') diff --git a/pyste/tests/abstract_testUT.py b/pyste/tests/abstract_testUT.py new file mode 100644 index 00000000..ace4ba80 --- /dev/null +++ b/pyste/tests/abstract_testUT.py @@ -0,0 +1,22 @@ +import unittest +from _abstract_test import * + +class AbstractTest(unittest.TestCase): + + def testIt(self): + class C(A): + def f(self): + return 'C::f' + + a = A() + b = B() + c = C() + self.assertRaises(RuntimeError, a.f) + self.assertEqual(b.f(), 'B::f') + self.assertEqual(call(b), 'B::f') + self.assertEqual(c.f(), 'C::f') + self.assertEqual(call(c), 'C::f') + + +if __name__ == '__main__': + unittest.main() diff --git a/pyste/tests/test_all.py b/pyste/tests/test_all.py index dbbb7713..259d3753 100644 --- a/pyste/tests/test_all.py +++ b/pyste/tests/test_all.py @@ -11,7 +11,7 @@ import sys if sys.platform == 'win32': includes = '-ID:/programming/libraries/boost-cvs/boost -ID:/Bin/Python/include' - build_pyste_cmd = 'python ../src/Pyste/pyste.py %s ' % includes + build_pyste_cmd = 'python ../src/Pyste/pyste.py --cache-dir=cache %s ' % includes compile_single_cmd = 'icl /nologo /GR /GX -c %s -I. ' % includes link_single_cmd = 'link /nologo /DLL '\ '/libpath:D:/programming/libraries/boost-cvs/lib /libpath:D:/Bin/Python/libs '\ @@ -118,7 +118,7 @@ if __name__ == '__main__': else: module = None try: - #main('--multiple', module) +# main('--multiple', module) main('', module) except RuntimeError, e: print e