''' unit tests for pymol.exporting ''' import os import sys import tempfile import Image import unittest import pymol.exporting from pymol import cmd, testing, stored class TestExporting(testing.PyMOLTestCase): def testCache(self): for action in pymol.exporting.cache_action_dict: cmd.cache(action) def testCopyImage(self): cmd.copy_image self.skipTest("TODO") def testGetFastastr(self): seq, name = 'ACD', 'm1' cmd.fab(seq, name) s = cmd.get_fastastr() lines = s.split() self.assertTrue(lines[0] in ( '>' + name, '>' + name + '_', )) self.assertEqual(lines[1:], [seq]) def testGetPdbstr(self): cmd.pseudoatom() lines = cmd.get_pdbstr().splitlines() self.assertTrue(lines[0].startswith('HETATM')) self.assertTrue(lines[1].startswith('END')) def testGetSession(self): cmd.fragment('ala') x = cmd.count_atoms() s = cmd.get_session() cmd.reinitialize() cmd.set_session(s) self.assertEqual(['ala'], cmd.get_names()) self.assertEqual(x, cmd.count_atoms()) @testing.requires_version('2.5') def testGetSession25(self): cmd.set('pse_binary_dump', 0) cmd.set('pse_export_version', 0) s = cmd.get_session(binary=-1, version=-1) self.assertEqual(cmd.get_setting_int('pse_binary_dump'), 0) self.assertEqual(cmd.get_setting_float('pse_export_version'), 0.0) cmd.set_session(s) self.assertEqual(cmd.get_setting_int('pse_binary_dump'), 0) self.assertEqual(cmd.get_setting_float('pse_export_version'), 0.0) s = cmd.get_session(binary=True, version=1.2) self.assertEqual(cmd.get_setting_int('pse_binary_dump'), 0) self.assertEqual(cmd.get_setting_float('pse_export_version'), 0.0) cmd.set_session(s) self.assertEqual(cmd.get_setting_int('pse_binary_dump'), 1) self.assertEqual(cmd.get_setting_float('pse_export_version'), 1.2) @testing.requires_version('1.8.4') def testMultisave(self): names = ['ala', 'gly', 'his', 'arg'] for name in names: cmd.fragment(name) sym1 = (1.2, 3.4, 5.6, 60.0, 70.0, 80.0) sym2 = (2.3, 4.5, 6.7, 90.0, 90.0, 90.0) sym3 = (3.4, 5.6, 7.8, 70.0, 70.0, 70.0) cmd.set_symmetry("gly", *sym1) cmd.set_symmetry("his", *sym2) cmd.set_symmetry("arg", *sym3) with testing.mktemp('.pdb') as filename: cmd.multisave(filename, names[0]) # new cmd.multisave(filename, names[1]) # new (overwrite) cmd.multisave(filename, ' '.join(names[2:]), append=1) # append cmd.delete('*') cmd.load(filename) self.assertEqual(cmd.get_object_list(), names[1:]) self.assertArrayEqual(cmd.get_symmetry("gly")[:6], sym1, delta=1e-4) self.assertArrayEqual(cmd.get_symmetry("his")[:6], sym2, delta=1e-4) self.assertArrayEqual(cmd.get_symmetry("arg")[:6], sym3, delta=1e-4) @testing.requires_version('2.1') def testMultifilesave(self): import glob for name in ['ala', 'gly', 'his', 'arg']: cmd.fragment(name) # multistate for i in range(2, 11): cmd.create('ala', 'ala', 1, i) for fmt in ['{}-{:02}.cif', '{}-{state}.cif']: with testing.mkdtemp() as dirname: cmd.multifilesave(os.path.join(dirname, fmt), 'ala', 0) filenames = [os.path.basename(p) for p in glob.glob(os.path.join(dirname, '*.cif'))] self.assertEqual(len(filenames), 10) self.assertTrue('ala-03.cif' in filenames) with testing.mkdtemp() as dirname: cmd.multifilesave(os.path.join(dirname, '{}.pdb'), 'a* g*') filenames_full = sorted(glob.glob(os.path.join(dirname, '*.pdb'))) filenames = [os.path.basename(p) for p in filenames_full] self.assertEqual(filenames, ['ala.pdb', 'arg.pdb', 'gly.pdb']) cmd.delete('*') cmd.load(filenames_full[0]) self.assertEqual(cmd.count_atoms(), 10) cmd.delete('*') cmd.load(filenames_full[1]) self.assertEqual(cmd.count_atoms(), 24) @testing.foreach( ('0.5in', '0.25in', 200, (100, 50)), # inch ('2.54cm', '1.27cm', 100, (100, 50)), # centimeter (100, 0, -1, (100, 75)), # px, only width (0, 75, -1, (100, 75)), # px, only height ) @testing.requires('no_edu') # ray @testing.requires_version('1.6.0') def testPng(self, w, h, dpi, size): with testing.mktemp('.png') as filename: cmd.png(filename, w, h, dpi, ray=1) self.assertEqual(size, Image.open(filename).size) @testing.requires_version('2.5') def testPngNoFile(self): self.ambientOnly() cmd.fragment('gly') cmd.show_as('spheres') cmd.color('yellow') cmd.zoom(complete=1) cmd.refresh() # TODO should not be necessary, see also PYMOL-3328 ncol, nrow = 120, 80 buf = cmd.png(None, ncol, nrow) self.assertTrue(buf.startswith(b'\x89PNG')) import io img = self.get_imagearray(Image.open(io.BytesIO(buf))) self.assertEqual(img.shape[:2], (nrow, ncol)) self.assertImageHasColor('yellow', img) # not supported in older versions: xyz (no ref) @testing.foreach('pdb', 'sdf', 'mol', 'mol2') def testSaveRef(self, format): # for rms_cur (not all formats save all identifiers) m = -1 cmd.set('retain_order') cmd.fragment('ala', 'm1') cmd.copy('m2', 'm1') cmd.copy('m3', 'm1') cmd.rotate('y', 90, 'm2') cmd.align('m3', 'm2') # with ref=m3 with testing.mktemp('.' + format) as filename: cmd.save(filename, 'm2', ref='m3') cmd.load(filename, 'm4') self.assertAlmostEqual(cmd.rms_cur('m4', 'm1', matchmaker=m), 0.00, delta=1e-2) self.assertAlmostEqual(cmd.rms_cur('m4', 'm2', matchmaker=m), 1.87, delta=1e-2) # without ref with testing.mktemp('.' + format) as filename: cmd.save(filename, 'm2') cmd.load(filename, 'm5') self.assertAlmostEqual(cmd.rms_cur('m5', 'm2', matchmaker=m), 0.00, delta=1e-2) self.assertAlmostEqual(cmd.rms_cur('m5', 'm1', matchmaker=m), 1.87, delta=1e-2) @testing.foreach( ('pdb', 1.2), ('sdf', 1.7), ('mol2', 1.7), ('xyz', 1.8), ('mol', 1.831), ('mae', 1.831), ('cif', 1.8), ) def testSaveState(self, format, pymol_version): if pymol_version > testing.PYMOL_VERSION[1]: self.skipTest("version %f" % (pymol_version)) # for rms_cur (not all formats save all identifiers) m = -1 cmd.set('retain_order') # create a multistate object cmd.fragment('ala', 'm1') cmd.create('m1', 'm1', 1, 2) cmd.create('m1', 'm1', 1, 3) cmd.translate([5, 0, 0], 'm1', state=2) cmd.translate([0, 5, 0], 'm1', state=3) n_states = cmd.count_states('m1') with testing.mktemp('.' + format) as filename: if format == 'mae' and not pymol.invocation.options.incentive_product: format = 'cms' # explicit for state in range(1, n_states + 1): cmd.delete('m2') cmd.save(filename, 'm1', state=state) cmd.load(filename, 'm2', format=format) rms = cmd.rms_cur('m1', 'm2', state, 1, matchmaker=m) self.assertAlmostEqual(rms, 0.00, delta=1e-2) # current state for state in range(1, n_states + 1): cmd.frame(state) cmd.delete('m2') cmd.save(filename, 'm1') cmd.load(filename, 'm2', 1, format=format) rms = cmd.rms_cur('m1', 'm2', state, 1, matchmaker=m) self.assertAlmostEqual(rms, 0.00, delta=1e-2) if format in ('mol', 'cms'): # no multi support return # all states cmd.delete('m2') cmd.save(filename, 'm1', state=0) cmd.load(filename, 'm2', 1, discrete=1, multiplex=0) self.assertEqual(cmd.count_states('m2'), n_states) for state in range(1, n_states + 1): rms = cmd.rms_cur('m1', 'm2 and state %d' % state, state, state, matchmaker=m) self.assertAlmostEqual(rms, 0.00, delta=1e-2) @testing.foreach( ('pdb', 1.2), ('sdf', 1.7), ('mol2', 1.7), ('xyz', 1.7), ('mol', 1.7), ('mae', 1.831), ('cif', 1.8), ) def testSaveSelection(self, format, pymol_version): if pymol_version > testing.PYMOL_VERSION[1]: self.skipTest("version %f" % (pymol_version)) cmd.fragment('trp', 'm1') cmd.fragment('glu', 'm2') n_O = cmd.count_atoms('elem O') n_N = cmd.count_atoms('elem N') with testing.mktemp('.' + format) as filename: if testing.PYMOL_VERSION_TUPLE < (1, 8): cmd.set('raise_exceptions', 0) # 1.7.6 save xyz doesn't set r=DEFAULT_SUCCESS cmd.save(filename, 'elem O+N') if testing.PYMOL_VERSION_TUPLE < (1, 8): cmd.set('raise_exceptions', 1) if format == 'mae' and not pymol.invocation.options.incentive_product: format = 'cms' cmd.delete('*') cmd.load(filename, 'm2', discrete=1, format=format) # avoid merging of atoms self.assertEqual(n_O, cmd.count_atoms('elem O')) self.assertEqual(n_N, cmd.count_atoms('elem N')) self.assertEqual(n_O + n_N, cmd.count_atoms()) @testing.foreach('pdb', 'cif', 'mmtf') @testing.requires_version('2.5') def testSave_symmetry(self, format): sym1 = (1.2, 3.4, 5.6, 60.0, 70.0, 80.0, "P 3") cmd.fragment("gly", "m1") cmd.set_symmetry("m1", *sym1) with testing.mktemp('.' + format) as filename: cmd.save(filename) cmd.delete('*') cmd.load(filename, "m2") sym = cmd.get_symmetry("m2") self.assertArrayEqual(sym[:6], sym1[:6], delta=1e-4) self.assertEqual(sym[6], sym1[6]) # cmp_atom : compares all fields in Atom (see chempy/__init__.py) # except the id (which is unique to the instance) def cmp_atom(self, selfobj,other): cmp = lambda a, b: a != b # py3k return \ cmp(type(selfobj), type(other)) or \ cmp(selfobj.segi, other.segi) or \ cmp(selfobj.chain, other.chain) or \ cmp(selfobj.resi_number, other.resi_number) or \ cmp(selfobj.resi, other.resi) or \ cmp(selfobj.resn, other.resn) or \ cmp(selfobj.symbol, other.symbol) or \ cmp(selfobj.name, other.name) # cmp_bond : compares all fields in Bond (see chempy/__init__.py) def cmp_bond(self, selfobj,other): cmp = lambda a, b: a != b # py3k return \ cmp(selfobj.order, other.order) or \ cmp(selfobj.index, other.index) def assertModelsAreSame(self, m1, m2): self.assertTrue(len(m1.atom) == len(m2.atom)) idx = 0 for m1atomidx in m1.atom: self.assertTrue(self.cmp_atom(m1atomidx, m2.atom[idx]) == 0) idx = idx + 1 idx = 0 for m1bondidx in m1.bond: self.assertTrue(self.cmp_bond(m1bondidx, m2.bond[idx]) == 0) idx = idx + 1 @testing.requires_version('1.7.6') def testPSEBulkImport(self): cmd.load(self.datafile('1rx1_1766_bulk.pse.gz')) m1 = cmd.get_model() cmd.load(self.datafile('1rx1_176.pse.gz')) m2 = cmd.get_model() self.assertModelsAreSame(m1, m2) @testing.foreach.product((0, 1.7, 1.76, 1.8, 1.82, 1.9), (0, 1)) @testing.requires_version('1.7.6.5') def testPSEBulkExportImport(self, pse_export_version, pse_binary_dump): with testing.mktemp('.pse') as filename: cmd.load(self.datafile("1oky-frag.pdb")) m1 = cmd.get_model() cmd.set("pse_export_version", pse_export_version) cmd.set("pse_binary_dump", pse_binary_dump) cmd.save(filename) cmd.reinitialize() cmd.load(filename) m2 = cmd.get_model() self.assertModelsAreSame(m1, m2) def testGetModelObjectName(self): cmd.load(self.datafile("1oky-frag.pdb")) cmd.load(self.datafile('1rna.cif')) m1 = cmd.get_model() cnt = cmd.count_atoms('%1oky-frag') self.assertEqual(m1.atom[0].model, '1oky-frag') self.assertEqual(m1.atom[-1].model, '1rna') self.assertEqual(m1.atom[cnt].model, '1rna') @testing.requires_version('2.1') def testMMTF(self): '''Styled MMTF export/import''' S = 0b10 # 1 << 1 spheres D = 0b1000000000 # 1 << 9 dots B = 2 # blue R = 4 # red cmd.fragment('gly') cmd.color(B) cmd.color(R, 'elem C') cmd.show_as('spheres') cmd.show_as('dots', 'elem C') with testing.mktemp('.mmtf') as filename: cmd.save(filename) cmd.delete('*') cmd.load(filename) color_list = [] reps_list = [] cmd.iterate('*', 'color_list.append(color)', space=locals()) cmd.iterate('*', 'reps_list.append(reps)', space=locals()) self.assertEqual(color_list, [B, R, R, B, B, B, B]) self.assertEqual(reps_list, [S, D, D, S, S, S, S]) @testing.requires_version('2.1') def testMMTFExportSele(self): cmd.fab('ACDE') with testing.mktemp('.mmtf') as filename: cmd.save(filename, 'resn CYS+ASP') cmd.delete('*') cmd.load(filename) self.assertEqual(cmd.count_atoms(), 23) self.assertEqual(cmd.count_atoms('bound_to ASP/CG'), 3) self.assertEqual(cmd.get_model('ASP/CG ASP/OD1').bond[0].order, 2) self.assertEqual(cmd.get_model('ASP/CG ASP/OD2').bond[0].order, 1) self.assertEqual(cmd.get_model('CYS/C ASP/N').bond[0].order, 1) @testing.requires_version('2.1') def testMMTFExportEmpty(self): with testing.mktemp('.mmtf') as filename: cmd.save(filename) cmd.load(filename, 'm1') self.assertEqual(cmd.count_atoms(), 0) self.assertEqual(cmd.get_names(), ['m1']) @testing.requires_version('2.4') def testglTF(self): '''glTF export''' cmd.fragment('gly') with testing.mktemp('.gltf') as filename: self.assertEqual(cmd.save(filename), 0) cmd.delete('*') def testSaveAln(self): cmd.fab('ACDEFGH', 'm1') cmd.fab('ACDFGH', 'm2') cmd.align('m1', 'm2', cycles=0, object='aln') with testing.mktemp('.aln') as filename: cmd.save(filename) with open(filename) as handle: lines = list(handle) self.assertEqual(lines[0].split(), ['CLUSTAL']) self.assertEqual(lines[1].split(), []) self.assertEqual(lines[2].split(), ['m1', 'ACDEFGH']) self.assertEqual(lines[3].split(), ['m2', 'ACD-FGH']) @testing.requires_version('2.3') def testSaveAlnNucleic(self): cmd.load(self.datafile('1rna.cif')) cmd.create('m1', 'chain A and not resi 6-7') cmd.create('m2', 'chain B') cmd.alter('m1 and resi 1', 'resn = "DT"') # mimic DNA cmd.alter('m2 and resi 20', 'resn = "UNK"') # mimic nonstd residue cmd.align('m1', 'm2', cycles=0, object='aln') with testing.mktemp('.aln') as filename: cmd.save(filename) with open(filename) as handle: lines = list(handle) self.assertEqual(lines[0].split(), ['CLUSTAL']) self.assertEqual(lines[1].split(), []) self.assertEqual(lines[2].split(), ['m1', 'TUAUA--UAUAUAA']) self.assertEqual(lines[3].split(), ['m2', 'UUAUA?AUAUAUAA']) @testing.requires_version('2.0') def testAssingAtomTypes(self): cmd.fragment('his') cmd.alter('all', 'text_type = "none"') mytypes = {} cmd.iterate('all', 'mytypes[name] = text_type', space=locals()) self.assertEqual(mytypes['NE2'], 'none') pymol.exporting.assign_atom_types('all', 'mol2') cmd.iterate('all', 'mytypes[name] = text_type', space=locals()) self.assertEqual(mytypes['NE2'], 'N.pl3') self.assertEqual(mytypes['N'], 'N.3') self.assertEqual(mytypes['CG'], 'C.2')