rdkit_chem 2025.09.3.2 → 2025.09.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb84e9eb9b23425a697da1935d2a58fde15b5b184f9b0f0605340577f9296f16
4
- data.tar.gz: 3ba3ea75e71cf6f86d82008d297a6409494a09e152b7049077245eb641626eaf
3
+ metadata.gz: 3ba77bf7020186b6859a9d6eff2eda271ca1853b5e022d09a97362b090db3685
4
+ data.tar.gz: fc8426cf986a9c7b38490bde57fd6db7080d8df22d6f7392242e0b263b7feae0
5
5
  SHA512:
6
- metadata.gz: f2001ea207f0a2e4484595d27ce3fe5f74d2e0acbf3b58caf204169a09b928542fccabf544a424653175af1e272b73378eaac81db962b26b8ff1ece82219bd43
7
- data.tar.gz: 5c859291de10e2a2355068b3d57619d7e56e3c248c607b3df413d46f4e9fd29437bfc5cc1914fbb70e2daa319da2889b790b2a205c6087a2ebdaf1faffcb6cdd
6
+ metadata.gz: 3f245fe05a0d011ee1e2a4e80bce87f3cb4b629016671b49db3c83b234b34366af3d04533f501336b4b84dcd92b108c571925d43d3b79023bbcda53d376b563c
7
+ data.tar.gz: 7242f4eb9d2bb13f127277612de7f3ccb0265e7c97a2e3cc383aaf35e0a05d4a49df86780003ffe159b8bb0f180f6453d91f96290ed48a840f4de0c117bdc37a
@@ -44,23 +44,27 @@
44
44
  %newobject RDKit::MolOps::mergeQueryHs;
45
45
  %newobject RDKit::MolOps::adjustQueryProperties;
46
46
 
47
- %ignore RDKit::MolOps::detectChemistryProblems;
48
- // Ignore all native sanitizeMol overloads - we provide a custom wrapper below
47
+ // IMPORTANT: %ignore must come BEFORE %include
48
+ // Ignore all sanitizeMol overloads - we provide custom wrapper below
49
49
  %ignore RDKit::MolOps::sanitizeMol;
50
+ %ignore RDKit::MolOps::detectChemistryProblems;
50
51
  %include <GraphMol/MolOps.h>
51
52
  %template(BoolPair) std::pair<bool, bool>;
52
53
 
53
54
  %inline %{
54
- int sanitizeMol(RDKit::RWMol &mol,int sanitizeOps){
55
+ // Custom sanitizeMol wrapper that returns the operation that failed
56
+ // Only ONE definition needed - default parameter handles both use cases
57
+ int sanitizeMol(RDKit::RWMol &mol, int sanitizeOps=RDKit::MolOps::SANITIZE_ALL){
55
58
  unsigned int opThatFailed;
56
59
  try{
57
- RDKit::MolOps::sanitizeMol(mol,opThatFailed,
60
+ RDKit::MolOps::sanitizeMol(mol, opThatFailed,
58
61
  static_cast<unsigned int>(sanitizeOps));
59
62
  } catch(...) {
60
63
 
61
64
  }
62
65
  return static_cast<int>(opThatFailed);
63
- };
66
+ }
67
+
64
68
  std::vector<boost::shared_ptr<RDKit::MolSanitizeException>> detectChemistryProblems(RDKit::ROMol &mol,int sanitizeOps=RDKit::MolOps::SANITIZE_ALL){
65
69
  std::vector<boost::shared_ptr<RDKit::MolSanitizeException>> res;
66
70
  auto probs = RDKit::MolOps::detectChemistryProblems(mol,sanitizeOps);
@@ -101,12 +101,11 @@
101
101
 
102
102
  /*
103
103
  * Special handling for Conformer objects which should not be GCed until the molecule is destroyed
104
- * We want to modify the behavior of the Conformer coming into the addConformer method without
105
- * impacting Conformer objects that are arguments to other methods. Therefore we define a pattern
106
- * that will trigger special handling of the Conformer input (the addConf method match this pattern).
104
+ * We ignore the original addConformer and provide our own in %extend that copies the conformer
105
+ * to avoid double-free issues with shared_ptr wrappers.
106
+ * Both addConf and addConformer are defined in %extend below (add_conf and add_conformer in Ruby).
107
107
  */
108
- %ignore addConformer(Conformer * conf, bool assignId=false);
109
- %rename(addConformer) RDKit::ROMol::addConf;
108
+ %ignore RDKit::ROMol::addConformer;
110
109
  %include <GraphMol/ROMol.h>
111
110
 
112
111
  %ignore SubstructMatch;
@@ -189,9 +188,18 @@ unsigned int getDefaultPickleProperties();
189
188
  return res;
190
189
  }
191
190
 
192
- /* Used in the addConformer modifications described above */
193
- unsigned int RDKit::ROMol::addConf(RDKit::Conformer * ownedConf, bool assignId=false) {
194
- return self->addConformer(ownedConf, assignId);
191
+ /* Create a copy of the conformer before adding it to the molecule.
192
+ This avoids the double-free issue since Ruby owns the original and C++ owns the copy.
193
+ The original DISOWN approach doesn't work with shared_ptr wrappers. */
194
+ unsigned int addConf(RDKit::Conformer *conf, bool assignId=false) {
195
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
196
+ return self->addConformer(confCopy, assignId);
197
+ }
198
+
199
+ // Alias for addConf - exposes as add_conformer in Ruby
200
+ unsigned int addConformer(RDKit::Conformer *conf, bool assignId=false) {
201
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
202
+ return self->addConformer(confCopy, assignId);
195
203
  }
196
204
 
197
205
  std::string MolToSmiles(bool doIsomericSmiles=true, bool doKekule=false, int rootedAtAtom=-1, bool canonical=true,
@@ -44,6 +44,13 @@
44
44
 
45
45
  %template(RWMol_Vect) std::vector< boost::shared_ptr<RDKit::RWMol> >;
46
46
 
47
+ /*
48
+ * Special handling for Conformer objects which should not be GCed until the molecule is destroyed
49
+ * We ignore the original addConformer and provide our own in %extend that copies the conformer
50
+ * to avoid double-free issues with shared_ptr wrappers.
51
+ */
52
+ %ignore RDKit::RWMol::addConformer;
53
+
47
54
  // ignore the methods that allow the molecule to take ownership of atoms/Bonds
48
55
  // (instead of copying them). This just leads to memory problems with Ruby
49
56
  %ignore RDKit::RWMol::addAtom(Atom *atom,bool updateLabel,bool takeOwnership);
@@ -78,6 +85,20 @@
78
85
  %include <GraphMol/RWMol.h>
79
86
 
80
87
  %extend RDKit::RWMol {
88
+ /* Create a copy of the conformer before adding it to the molecule.
89
+ This avoids the double-free issue since Ruby owns the original and C++ owns the copy.
90
+ The original DISOWN approach doesn't work with shared_ptr wrappers. */
91
+ unsigned int addConf(RDKit::Conformer *conf, bool assignId=false) {
92
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
93
+ return self->addConformer(confCopy, assignId);
94
+ }
95
+
96
+ // Alias for addConf - exposes as add_conformer in Ruby
97
+ unsigned int addConformer(RDKit::Conformer *conf, bool assignId=false) {
98
+ RDKit::Conformer *confCopy = new RDKit::Conformer(*conf);
99
+ return self->addConformer(confCopy, assignId);
100
+ }
101
+
81
102
  static RDKit::RWMOL_SPTR MolFromSmiles(const std::string &smi,int debugParse=0,bool sanitize=1,
82
103
  std::map<std::string,std::string> *replacements=0){
83
104
  return RDKit::RWMOL_SPTR(RDKit::SmilesToMol(smi, debugParse, sanitize,replacements));
@@ -16,13 +16,13 @@ rescue StandardError
16
16
  nr_processors = 1
17
17
  end
18
18
 
19
- FileUtils.mkdir_p rdkit_dir
20
- Dir.chdir main_dir do
21
- FileUtils.rm_rf src_dir
22
- puts 'Downloading RDKit sources'
23
- git = 'git clone https://github.com/rdkit/rdkit.git'
24
- system git
25
- end
19
+ # FileUtils.mkdir_p rdkit_dir
20
+ # Dir.chdir main_dir do
21
+ # FileUtils.rm_rf src_dir
22
+ # puts 'Downloading RDKit sources'
23
+ # git = 'git clone https://github.com/rdkit/rdkit.git'
24
+ # system git
25
+ # end
26
26
 
27
27
  Dir.chdir(src_dir) do
28
28
  checkout = 'git checkout c2e48f41d88ddc15c6e1f818d1c4ced70b7f20d1'
@@ -74,7 +74,7 @@ Dir.chdir build_dir do
74
74
  end
75
75
 
76
76
  # Remove compiled file, free spaces
77
- FileUtils.remove_dir(rdkit_dir)
77
+ # FileUtils.remove_dir(rdkit_dir)
78
78
 
79
79
  # create a fake Makefile
80
80
  File.open(File.join(File.dirname(__FILE__), 'Makefile'), 'w+') do |makefile|
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RDKitChem
2
- VERSION = '2025.09.3'.freeze
3
- GEMVERSION = VERSION + '.2'
4
+ VERSION = "2025.09.3"
5
+ GEMVERSION = "#{VERSION}.5"
4
6
  end
@@ -1,25 +1,25 @@
1
- require 'test/unit'
1
+ require "test/unit"
2
2
 
3
- require 'rdkit_chem'
3
+ require "rdkit_chem"
4
4
 
5
5
  class RDKitTest < Test::Unit::TestCase
6
6
  def test_mol_from_smiles
7
- smiles = 'CCO' # ethanol
7
+ smiles = "CCO" # ethanol
8
8
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
9
9
  assert_not_nil(rw_mol)
10
10
  assert_equal(3, rw_mol.get_num_atoms)
11
11
  end
12
12
 
13
13
  def test_mol_to_mol_block
14
- smiles = 'CCO' # ethanol
14
+ smiles = "CCO" # ethanol
15
15
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
16
16
  mdl = rw_mol.mol_to_mol_block(true, -1, false)
17
17
  assert_not_nil(mdl)
18
- assert(mdl.include?('V2000') || mdl.include?('V3000'))
18
+ assert(mdl.include?("V2000") || mdl.include?("V3000"))
19
19
  end
20
20
 
21
21
  def test_mol_from_mol_block
22
- smiles = 'c1ccccc1' # benzene
22
+ smiles = "c1ccccc1" # benzene
23
23
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
24
24
  mdl = rw_mol.mol_to_mol_block(true, -1, false)
25
25
 
@@ -30,18 +30,70 @@ class RDKitTest < Test::Unit::TestCase
30
30
  end
31
31
 
32
32
  def test_mol_to_smiles
33
- smiles = 'CCO'
33
+ smiles = "CCO"
34
34
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
35
35
  output_smiles = RDKitChem.mol_to_smiles(rw_mol)
36
36
  assert_not_nil(output_smiles)
37
- assert_equal('CCO', output_smiles)
37
+ assert_equal("CCO", output_smiles)
38
+ end
39
+
40
+ def test_sanitize_mol
41
+ smiles = "c1ccccc1" # benzene
42
+ rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
43
+
44
+ # sanitize_mol returns an integer (0 for success usually, or bitmask of operations failed)
45
+ # It might return different things based on SWIG wrapping details observed in MolOps.i
46
+ # The SWIG wrapper returns the operation that failed (0 if all successful).
47
+ result = RDKitChem.sanitize_mol(rw_mol, RDKitChem::SANITIZE_ALL)
48
+ assert_equal(0, result, "sanitize_mol should return 0 (success)")
49
+
50
+ result = RDKitChem.sanitize_mol(rw_mol)
51
+ assert_equal(0, result, "sanitize_mol should return 0 (success)")
38
52
  end
39
53
 
40
54
  def test_cholesterol
41
55
  # Cholesterol without chirality
42
- smiles = 'CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C'
56
+ smiles = "CC(C)CCCC(C)C1CCC2C1(CCC3C2CC=C4C3(CCC(C4)O)C)C"
43
57
  rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
44
58
  assert_not_nil(rw_mol)
45
- assert_equal(28, rw_mol.get_num_atoms) # C27H46O has 28 heavy atoms (27 C + 1 O)
59
+ assert_equal(28, rw_mol.get_num_atoms) # C27H46O has 28 heavy atoms (27 C + 1 O)
60
+ end
61
+
62
+ def test_conformer_ownership
63
+ # FIXME: This test causes a segfault (exit code 139) likely due to GC/double-free issues.
64
+ # Test that conformer ownership is properly transferred to the molecule.
65
+ # This verifies the DISOWN directive works - without it, Ruby GC and C++
66
+ # destructor both try to free the Conformer, causing a double-free segfault.
67
+ smiles = "CCO" # ethanol
68
+ rw_mol = RDKitChem::RWMol.mol_from_smiles(smiles)
69
+
70
+ # Create a conformer with 3D coordinates
71
+ conf = RDKitChem::Conformer.new(rw_mol.get_num_atoms)
72
+ conf.set_atom_pos(0, RDKitChem::Point3D.new(0.0, 0.0, 0.0))
73
+ conf.set_atom_pos(1, RDKitChem::Point3D.new(1.5, 0.0, 0.0))
74
+ conf.set_atom_pos(2, RDKitChem::Point3D.new(2.0, 1.0, 0.0))
75
+
76
+ # Add conformer to molecule (ownership transfers to C++)
77
+ conf_id = rw_mol.add_conf(conf, true)
78
+ assert(conf_id >= 0, "Conformer should be added successfully")
79
+
80
+ # Verify conformer was added
81
+ assert_equal(1, rw_mol.get_num_conformers)
82
+
83
+ # Access the conformer through the molecule
84
+ retrieved_conf = rw_mol.get_conformer(conf_id)
85
+ assert_not_nil(retrieved_conf)
86
+
87
+ # Verify coordinates
88
+ pos = retrieved_conf.get_atom_pos(0)
89
+ assert_in_delta(0.0, pos.x, 0.001)
90
+ assert_in_delta(0.0, pos.y, 0.001)
91
+ assert_in_delta(0.0, pos.z, 0.001)
92
+
93
+ # Force garbage collection to ensure no double-free occurs
94
+ GC.start
95
+
96
+ # The molecule should still be valid after GC
97
+ assert_equal(3, rw_mol.get_num_atoms)
46
98
  end
47
99
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdkit_chem
3
3
  version: !ruby/object:Gem::Version
4
- version: 2025.09.3.2
4
+ version: 2025.09.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Nguyen