diff --git a/modules/conop/pymod/export_conop.cc b/modules/conop/pymod/export_conop.cc index 39f006b45f96fc41a83403ab9363c7e5568cbd20..9f72861bd6a038648b02e6d49926615527e0fdbf 100644 --- a/modules/conop/pymod/export_conop.cc +++ b/modules/conop/pymod/export_conop.cc @@ -25,12 +25,26 @@ using namespace boost::python; using namespace ost::conop; +void destroy_compound_lib() { + Conopology::Instance().SetDefaultLib(CompoundLibPtr()); +} + void export_Conop() { class_<Conopology, boost::noncopyable>("Conopology", no_init) - .def("Instance", &Conopology::Instance, return_value_policy<reference_existing_object>()).staticmethod("Instance") + .def("Instance", &Conopology::Instance, + return_value_policy<reference_existing_object>()).staticmethod("Instance") .def("SetDefaultLib", &Conopology::SetDefaultLib) .def("GetDefaultLib", &Conopology::GetDefaultLib) ; + // we need to make sure there are no pending references to Python objects + // tied to the Conopology singleton instance. The destructor of the + // Conopology may be called after Python is shutdown which results in a + // segfault. + scope().attr("__dict__")["atexit"]=handle<>(PyImport_ImportModule("atexit")); + + def("_destroy_compound_lib", &destroy_compound_lib); + object r=scope().attr("_destroy_compound_lib"); + scope().attr("atexit").attr("register")(r); } diff --git a/modules/io/pymod/__init__.py b/modules/io/pymod/__init__.py index 584f03effef442e51d40acdc181e095443240601..85751e6970310949aafbea427ca97733f4934e8f 100644 --- a/modules/io/pymod/__init__.py +++ b/modules/io/pymod/__init__.py @@ -32,7 +32,7 @@ class IOProfiles: def __setitem__(self, key, value): if isinstance(value, str): - value=self[value] + value=self[value].Copy() IOProfileRegistry.Instance().Set(key, value) self._dict[key]=value diff --git a/modules/io/pymod/export_pdb_io.cc b/modules/io/pymod/export_pdb_io.cc index c5062e20d92694a84a029346764bc55cbe4d41fe..6fe425aa7fef010477b6a36e1bbe9db9f92971bf 100644 --- a/modules/io/pymod/export_pdb_io.cc +++ b/modules/io/pymod/export_pdb_io.cc @@ -33,6 +33,10 @@ BOOST_PYTHON_FUNCTION_OVERLOADS(load_PDB_ov, LoadPDB, 1, 2) void (PDBWriter::*write_a)(const mol::EntityHandle&)=&PDBWriter::Write; void (PDBWriter::*write_b)(const mol::EntityView&)=&PDBWriter::Write; +void remove_profiles() { + IOProfileRegistry::Instance().RemoveProfiles(); +} + void export_pdb_io() { class_<IOProfile>("IOProfile", @@ -57,12 +61,13 @@ void export_pdb_io() ; class_<IOProfileRegistry>("IOProfileRegistry", no_init) .def("Get", &IOProfileRegistry::Get, - return_value_policy<reference_existing_object>()) + return_internal_reference<>()) .def("Set", &IOProfileRegistry::Set) .def("Instance", &IOProfileRegistry::Instance, + //return_internal_reference).staticmethod("Instance") return_value_policy<reference_existing_object>()).staticmethod("Instance") .def("GetDefault", &IOProfileRegistry::GetDefault, - return_value_policy<reference_existing_object>()) + return_internal_reference<>()) ; class_<PDBReader, boost::noncopyable>("PDBReader", init<String, const IOProfile&>()) .def("HasNext", &PDBReader::HasNext) @@ -83,4 +88,14 @@ void export_pdb_io() &PDBWriter::SetWriteMultiModel) .def("Write", write_b) ; + + // we need to make sure there are no pending references to Python objects + // tied to the IOProfileRegistry singleton. The destructor of + // IOProfileRegistry may be called after Python is shutdown which results + // in a segfault. + scope().attr("__dict__")["atexit"]=handle<>(PyImport_ImportModule("atexit")); + + def("_remove_profiles", &remove_profiles); + object r=scope().attr("_remove_profiles"); + scope().attr("atexit").attr("register")(r); } diff --git a/modules/io/src/mol/io_profile.hh b/modules/io/src/mol/io_profile.hh index abf37a6b22a2c1e389b9d58d1179e858ca9fe14a..188ed158bf1bddfe87869dc78f766a9233e730d6 100644 --- a/modules/io/src/mol/io_profile.hh +++ b/modules/io/src/mol/io_profile.hh @@ -49,7 +49,6 @@ public: bool no_hetatms; bool calpha_only; conop::ProcessorPtr processor; - IOProfile Copy() { return IOProfile(dialect, quack_mode, fault_tolerant, join_spread_atom_records, @@ -86,6 +85,9 @@ public: } IOProfile& GetDefault() { return profiles_["DEFAULT"]; } + void RemoveProfiles() { + profiles_.clear(); + } private: IOProfileRegistry(); std::map<String, IOProfile> profiles_; diff --git a/modules/io/tests/test_io_pdb.py b/modules/io/tests/test_io_pdb.py index 9beb684d952f60bbbaf35e4b9b2369ddc896395c..f3f3acbdf8593fe6c238b0ef2da1636840291d0b 100644 --- a/modules/io/tests/test_io_pdb.py +++ b/modules/io/tests/test_io_pdb.py @@ -11,6 +11,14 @@ class TestPDB(unittest.TestCase): ch = e.FindChain("A"); self.assertEquals(ch.GetIntProp("mol_id"), 1) + def test_properly_assigns_profile_properties(self): + io.profiles['TEST'] = io.IOProfile() + io.profiles['TEST'].quack_mode = False + self.assertFalse(io.profiles['TEST'].quack_mode) + self.assertFalse(io.profiles['TEST'].Copy().quack_mode) + io.profiles['TEST'].quack_mode = True + self.assertTrue(io.profiles['TEST'].quack_mode) + self.assertTrue(io.profiles['TEST'].Copy().quack_mode) def test_no_bond_feasibility(self): io.profiles['FEAS_CHECK']=io.IOProfile(processor=conop.HeuristicProcessor(check_bond_feasibility=True)) io.profiles['NO_FEAS_CHECK']=io.IOProfile(processor=conop.HeuristicProcessor(check_bond_feasibility=False))