@@ -383,3 +383,89 @@ def test_automatic_output_string_conversion():
383383 msg = h .get (input_unicode )
384384 assert isinstance (msg , expected_type )
385385 assert msg == expected
386+
387+
388+ def test_wrap_ignore_foreign_cimports ():
389+ """
390+ Test that wrap-ignored classes are not included in foreign cimports.
391+
392+ This test verifies the fix for GitHub issue #194:
393+ When a class has the wrap-ignore annotation, other modules should not
394+ generate cimport statements for it, as wrap-ignored classes don't have
395+ corresponding pxd files.
396+
397+ The test creates a multi-module scenario where one module has a
398+ wrap-ignored class, and verifies that the generated code in the other
399+ module does not attempt to cimport the wrap-ignored class.
400+ """
401+ import tempfile
402+ import shutil
403+ from autowrap .CodeGenerator import CodeGenerator
404+ from autowrap .DeclResolver import ResolvedClass
405+
406+ # Create a temporary directory for generated files
407+ test_dir = tempfile .mkdtemp ()
408+ try :
409+ # Parse the libcpp_test.pxd which has AbstractBaseClass (wrap-ignore)
410+ # and ABS_Impl1, ABS_Impl2 which inherit from it
411+ pxd_files = ["libcpp_test.pxd" ]
412+ full_pxd_files = [os .path .join (test_files , f ) for f in pxd_files ]
413+ decls , instance_map = autowrap .parse (full_pxd_files , test_files )
414+
415+ # Find the wrap-ignored class (AbstractBaseClass)
416+ wrap_ignored_classes = [d for d in decls if isinstance (d , ResolvedClass ) and d .wrap_ignore ]
417+ assert len (wrap_ignored_classes ) > 0 , "Expected at least one wrap-ignored class"
418+
419+ # Set up a multi-module scenario
420+ # Module "module1" contains all the classes
421+ # Module "module2" is our target module that will generate foreign cimports
422+ module1_decls = decls
423+ module2_decls = [] # Empty module that needs to import from module1
424+
425+ master_dict = {
426+ "module1" : {"decls" : module1_decls , "addons" : [], "files" : full_pxd_files },
427+ "module2" : {"decls" : module2_decls , "addons" : [], "files" : []},
428+ }
429+
430+ # Generate code for module2 which would need foreign cimports from module1
431+ target = os .path .join (test_dir , "module2.pyx" )
432+ cg = CodeGenerator (
433+ module2_decls ,
434+ instance_map ,
435+ pyx_target_path = target ,
436+ all_decl = master_dict ,
437+ )
438+
439+ # Call create_foreign_cimports
440+ cg .create_foreign_cimports ()
441+
442+ # Check the generated code for foreign cimports
443+ generated_code = ""
444+ for code_block in cg .top_level_code :
445+ generated_code += code_block .render ()
446+
447+ # Verify that wrap-ignored classes are NOT in the cimports
448+ for ignored_class in wrap_ignored_classes :
449+ # The cimport line would look like: "from .module1 cimport ClassName"
450+ cimport_pattern = f"cimport { ignored_class .name } "
451+ assert cimport_pattern not in generated_code , (
452+ f"Wrap-ignored class '{ ignored_class .name } ' should not be in foreign cimports. "
453+ f"Generated code:\n { generated_code } "
454+ )
455+
456+ # Verify that non-ignored classes ARE in the cimports
457+ non_ignored_classes = [d for d in decls if isinstance (d , ResolvedClass ) and not d .wrap_ignore ]
458+ for normal_class in non_ignored_classes :
459+ # Skip classes with no_pxd_import or wrap-attach
460+ if normal_class .no_pxd_import :
461+ continue
462+ if normal_class .cpp_decl .annotations .get ("wrap-attach" ):
463+ continue
464+ cimport_pattern = f"cimport { normal_class .name } "
465+ assert cimport_pattern in generated_code , (
466+ f"Non-ignored class '{ normal_class .name } ' should be in foreign cimports. "
467+ f"Generated code:\n { generated_code } "
468+ )
469+
470+ finally :
471+ shutil .rmtree (test_dir , ignore_errors = True )
0 commit comments