Source code for diffpy.structure.parsers.p_cif

#!/usr/bin/env python
##############################################################################
#
# diffpy.structure  by DANSE Diffraction group
#                   Simon J. L. Billinge
#                   (c) 2007 trustees of the Michigan State University.
#                   All rights reserved.
#
# File coded by:    Pavol Juhas
#
# See AUTHORS.txt for a list of people who contributed.
# See LICENSE_DANSE.txt for license information.
#
##############################################################################

"""Parser for basic CIF file format.

Attributes
----------
rx_float : re.Pattern
    Constant regular expression for `leading_float()`.
symvec : dict
    Helper dictionary for `getSymOp()`.

Note
----
References: https://www.iucr.org/resources/cif
"""

import io
import re
import sys
from contextlib import contextmanager

import numpy

from diffpy.structure import Atom, Lattice, Structure
from diffpy.structure.parsers import StructureParser
from diffpy.structure.structureerrors import StructureFormatError

# ----------------------------------------------------------------------------


[docs] class P_cif(StructureParser): """Simple parser for CIF structure format. Reads Structure from the first block containing _atom_site_label key. Following blocks, if any, are ignored. Parameters ---------- eps : float, Optional Fractional coordinates cutoff for duplicate positions. When ``None`` use the default for `ExpandAsymmetricUnit`: ``1.0e-5``. Attributes ---------- format : str Structure format name. ciffile : CifFile Instance of `CifFile` from `PyCifRW`. stru : Structure `Structure` instance used for CIF input or output. spacegroup : SpaceGroup Instance of `SpaceGroup` used for symmetry expansion. eps : float Resolution in fractional coordinates for non-equal positions. Used for expansion of asymmetric unit. eau : ExpandAsymmetricUnit Instance of `ExpandAsymmetricUnit` from `SymmetryUtilities`. asymmetric_unit : list List of `Atom` instances for the original asymmetric unit in the CIF file. labelindex : dict Dictionary mapping unique atom label to index of `Atom` in `self.asymmetric_unit`. anisotropy : dict Dictionary mapping unique atom label to displacement anisotropy resolved at that site. cif_sgname : str or None Space group name obtained by looking up the value of `_space_group_name_Hall`, `_symmetry_space_group_name_Hall`, `_space_group_name_H-M_alt`, `_symmetry_space_group_name_H-M` items. ``None`` when neither is defined. """ # static data and methods ------------------------------------------------ # dictionary set of class methods for translating CIF values # to Atom attributes # static data and methods ------------------------------------------------ # dictionary set of class methods for translating CIF values # to Atom attributes _atom_setters = dict.fromkeys( ( "_tr_ignore", "_tr_atom_site_label", "_tr_atom_site_type_symbol", "_tr_atom_site_fract_x", "_tr_atom_site_fract_y", "_tr_atom_site_fract_z", "_tr_atom_site_cartn_x", "_tr_atom_site_cartn_y", "_tr_atom_site_cartn_z", "_tr_atom_site_U_iso_or_equiv", "_tr_atom_site_B_iso_or_equiv", "_tr_atom_site_adp_type", "_tr_atom_site_thermal_displace_type", "_tr_atom_site_occupancy", "_tr_atom_site_aniso_U_11", "_tr_atom_site_aniso_U_22", "_tr_atom_site_aniso_U_33", "_tr_atom_site_aniso_U_12", "_tr_atom_site_aniso_U_13", "_tr_atom_site_aniso_U_23", "_tr_atom_site_aniso_B_11", "_tr_atom_site_aniso_B_22", "_tr_atom_site_aniso_B_33", "_tr_atom_site_aniso_B_12", "_tr_atom_site_aniso_B_13", "_tr_atom_site_aniso_B_23", ) ) # make _atom_setters case insensitive for k in list(_atom_setters.keys()): _atom_setters[k] = _atom_setters[k.lower()] = k del k BtoU = 1.0 / (8 * numpy.pi**2) """float: Conversion factor from B values to U values.""" def _tr_ignore(a, value): return _tr_ignore = staticmethod(_tr_ignore) def _tr_atom_site_label(a, value): a.label = str(value) # set element when not specified by _atom_site_type_symbol if not a.element: P_cif._tr_atom_site_type_symbol(a, value) _tr_atom_site_label = staticmethod(_tr_atom_site_label) # 3 regexp groups for nucleon number, atom symbol, and oxidation state _psymb = re.compile(r"(\d+-)?([a-zA-Z]+)(\d[+-])?") def _tr_atom_site_type_symbol(a, value): rx = P_cif._psymb.match(value) smbl = rx and rx.group(0) or value smbl = str(smbl) a.element = smbl[:1].upper() + smbl[1:].lower() _tr_atom_site_type_symbol = staticmethod(_tr_atom_site_type_symbol) def _tr_atom_site_fract_x(a, value): a.xyz[0] = leading_float(value) _tr_atom_site_fract_x = staticmethod(_tr_atom_site_fract_x) def _tr_atom_site_fract_y(a, value): a.xyz[1] = leading_float(value) _tr_atom_site_fract_y = staticmethod(_tr_atom_site_fract_y) def _tr_atom_site_fract_z(a, value): a.xyz[2] = leading_float(value) _tr_atom_site_fract_z = staticmethod(_tr_atom_site_fract_z) def _tr_atom_site_cartn_x(a, value): a.xyz_cartn[0] = leading_float(value) _tr_atom_site_cartn_x = staticmethod(_tr_atom_site_cartn_x) def _tr_atom_site_cartn_y(a, value): a.xyz_cartn[1] = leading_float(value) _tr_atom_site_cartn_y = staticmethod(_tr_atom_site_cartn_y) def _tr_atom_site_cartn_z(a, value): a.xyz_cartn[2] = leading_float(value) _tr_atom_site_cartn_z = staticmethod(_tr_atom_site_cartn_z) def _tr_atom_site_U_iso_or_equiv(a, value): a.Uisoequiv = leading_float(value) _tr_atom_site_U_iso_or_equiv = staticmethod(_tr_atom_site_U_iso_or_equiv) def _tr_atom_site_B_iso_or_equiv(a, value): a.Uisoequiv = P_cif.BtoU * leading_float(value) _tr_atom_site_B_iso_or_equiv = staticmethod(_tr_atom_site_B_iso_or_equiv) def _tr_atom_site_adp_type(a, value): a.anisotropy = value not in ("Uiso", "Biso") _tr_atom_site_adp_type = staticmethod(_tr_atom_site_adp_type) _tr_atom_site_thermal_displace_type = _tr_atom_site_adp_type def _tr_atom_site_occupancy(a, value): a.occupancy = leading_float(value, 1.0) _tr_atom_site_occupancy = staticmethod(_tr_atom_site_occupancy) def _tr_atom_site_aniso_U_11(a, value): a.U11 = leading_float(value) _tr_atom_site_aniso_U_11 = staticmethod(_tr_atom_site_aniso_U_11) def _tr_atom_site_aniso_U_22(a, value): a.U22 = leading_float(value) _tr_atom_site_aniso_U_22 = staticmethod(_tr_atom_site_aniso_U_22) def _tr_atom_site_aniso_U_33(a, value): a.U33 = leading_float(value) _tr_atom_site_aniso_U_33 = staticmethod(_tr_atom_site_aniso_U_33) def _tr_atom_site_aniso_U_12(a, value): a.U12 = leading_float(value) _tr_atom_site_aniso_U_12 = staticmethod(_tr_atom_site_aniso_U_12) def _tr_atom_site_aniso_U_13(a, value): a.U13 = leading_float(value) _tr_atom_site_aniso_U_13 = staticmethod(_tr_atom_site_aniso_U_13) def _tr_atom_site_aniso_U_23(a, value): a.U23 = leading_float(value) _tr_atom_site_aniso_U_23 = staticmethod(_tr_atom_site_aniso_U_23) def _tr_atom_site_aniso_B_11(a, value): a.U11 = P_cif.BtoU * leading_float(value) _tr_atom_site_aniso_B_11 = staticmethod(_tr_atom_site_aniso_B_11) def _tr_atom_site_aniso_B_22(a, value): a.U22 = P_cif.BtoU * leading_float(value) _tr_atom_site_aniso_B_22 = staticmethod(_tr_atom_site_aniso_B_22) def _tr_atom_site_aniso_B_33(a, value): a.U33 = P_cif.BtoU * leading_float(value) _tr_atom_site_aniso_B_33 = staticmethod(_tr_atom_site_aniso_B_33) def _tr_atom_site_aniso_B_12(a, value): a.U12 = P_cif.BtoU * leading_float(value) _tr_atom_site_aniso_B_12 = staticmethod(_tr_atom_site_aniso_B_12) def _tr_atom_site_aniso_B_13(a, value): a.U13 = P_cif.BtoU * leading_float(value) _tr_atom_site_aniso_B_13 = staticmethod(_tr_atom_site_aniso_B_13) def _tr_atom_site_aniso_B_23(a, value): a.U23 = P_cif.BtoU * leading_float(value) _tr_atom_site_aniso_B_23 = staticmethod(_tr_atom_site_aniso_B_23) def _get_atom_setters(cifloop): """Static method for finding translators of CifLoop items to data in `Atom` instance. Parameters ---------- cifloop : CifLoop Instance of `CifLoop`. Returns ------- list List of setter functions in the order of `cifloop.keys()`. """ rv = [] for p in cifloop.keys(): lcname = "_tr" + p.lower() fncname = P_cif._atom_setters.get(lcname, "_tr_ignore") f = getattr(P_cif, fncname) rv.append(f) return rv _get_atom_setters = staticmethod(_get_atom_setters) # normal methods --------------------------------------------------------- def __init__(self, eps=None): StructureParser.__init__(self) self.format = "cif" self.ciffile = None self.stru = None self.spacegroup = None self.eps = eps self.eau = None self.asymmetric_unit = None self.labelindex = {} self.anisotropy = {} self.cif_sgname = None pass
[docs] def parse(self, s): """Create `Structure` instance from a string in CIF format. Parameters ---------- s : str A string in CIF format. Returns ------- Structure `Structure` instance. Raises ------ StructureFormatError When the data do not constitute a valid CIF format. """ self.ciffile = None self.filename = "" fp = io.StringIO(s) rv = self._parseCifDataSource(fp) return rv
[docs] def parseLines(self, lines): """Parse list of lines in CIF format. Parameters ---------- lines : list List of strings stripped of line terminator. Returns ------- Structure `Structure` instance. Raises ------ StructureFormatError When the data do not constitute a valid CIF format. """ s = "\n".join(lines) + "\n" return self.parse(s)
[docs] def parseFile(self, filename): """Create Structure from an existing CIF file. Parameters ---------- filename : str Path to structure file. Returns ------- Structure `Structure` instance. Raises ------ StructureFormatError When the data do not constitute a valid CIF format. IOError When the file cannot be opened. """ self.ciffile = None self.filename = filename rv = self._parseCifDataSource(filename) # all good here return rv
def _parseCifDataSource(self, datasource): """Open and process CIF data from the specified `datasource`. Parameters ---------- datasource : str or a file-like object This is used as an argument to the `CifFile` class. The `CifFile` instance is stored in `ciffile` attribute of this Parser. Returns ------- Structure The `Structure` object loaded from the specified data source. Raises ------ StructureFormatError When the data do not constitute a valid CIF format. """ from CifFile import CifFile, StarError self.stru = None try: with _suppressCifParserOutput(): # Use `grammar` option to digest values with curly-brackets. # Ref: https://bitbucket.org/jamesrhester/pycifrw/issues/19 self.ciffile = CifFile(datasource, grammar="auto") for blockname in self.ciffile.keys(): self._parseCifBlock(blockname) # stop after reading the first structure if self.stru is not None: break except (StarError, ValueError, IndexError) as err: exc_type, exc_value, exc_traceback = sys.exc_info() emsg = str(err).strip() e = StructureFormatError(emsg) raise e.with_traceback(exc_traceback) return self.stru def _parseCifBlock(self, blockname): """Translate CIF file block, skip blocks without `_atom_site_label`. Updates data members `stru`, `eau`. Parameters ---------- blockname : str Name of top level block in `self.ciffile`. """ block = self.ciffile[blockname] if "_atom_site_label" not in block: return # here block contains structure, initialize output data self.stru = Structure() self.labelindex.clear() self.anisotropy.clear() # execute specialized block parsers self._parse_lattice(block) self._parse_atom_site_label(block) self._parse_atom_site_aniso_label(block) self._parse_space_group_symop_operation_xyz(block) return def _parse_lattice(self, block): """Obtain `lattice` parameters from a `CifBlock`. This method updates `self.stru.lattic`e. Parameters ---------- block : CifBlock Instance of CifBlock. """ if "_cell_length_a" not in block: return # obtain lattice parameters try: latpars = ( leading_float(block["_cell_length_a"]), leading_float(block["_cell_length_b"]), leading_float(block["_cell_length_c"]), leading_float(block["_cell_angle_alpha"]), leading_float(block["_cell_angle_beta"]), leading_float(block["_cell_angle_gamma"]), ) except KeyError as err: exc_type, exc_value, exc_traceback = sys.exc_info() emsg = str(err) e = StructureFormatError(emsg) raise e.with_traceback(exc_traceback) self.stru.lattice = Lattice(*latpars) return def _parse_atom_site_label(self, block): """Obtain atoms in asymmetric unit from a `CifBlock`. This method inserts `Atom` instances to `self.stru` and updates `labelindex` dictionary. Parameters ---------- block : CifBlock Instance of `CifBlock`. """ # process _atom_site_label atom_site_loop = block.GetLoop("_atom_site_label") does_adp_type = ( "_atom_site_adp_type" in atom_site_loop or "_atom_site_thermal_displace_type" in atom_site_loop ) # get a list of setters for atom_site values prop_setters = P_cif._get_atom_setters(atom_site_loop) # index of the _atom_site_label item for the labelindex dictionary ilb = atom_site_loop.keys().index("_atom_site_label") # loop through the values and pass them to the setters sitedatalist = zip(*atom_site_loop.values()) for values in sitedatalist: curlabel = values[ilb] # skip entries that have invalid label if curlabel == "?": continue self.labelindex[curlabel] = len(self.stru) self.stru.addNewAtom() a = self.stru.getLastAtom() for fset, val in zip(prop_setters, values): fset(a, val) if does_adp_type: self.anisotropy[curlabel] = a.anisotropy return def _parse_atom_site_aniso_label(self, block): """Obtain value of anisotropic thermal displacements from a `CifBlock`. This method updates `U` members of `Atom` instances in `self.stru`. The `labelindex` dictionary has to be defined beforehand. Parameters ---------- block : CifBlock Instance of `CifBlock`. """ if "_atom_site_aniso_label" not in block: return # something to do here: adp_loop = block.GetLoop("_atom_site_aniso_label") # index of the _atom_site_label column ilb = adp_loop.keys().index("_atom_site_aniso_label") # get a list of setters for this loop prop_setters = P_cif._get_atom_setters(adp_loop) sitedatalist = zip(*adp_loop.values()) for values in sitedatalist: lb = values[ilb] if lb == "?": break idx = self.labelindex[lb] a = self.stru[idx] if lb not in self.anisotropy: a.anisotropy = True self.anisotropy[lb] = True for fset, val in zip(prop_setters, values): fset(a, val) return def _parse_space_group_symop_operation_xyz(self, block): """Process symmetry operations from a CifBlock. The method updates `spacegroup` and `eau` data according to symmetry operations defined in `_space_group_symop_operation_xyz` or `_symmetry_equiv_pos_as_xyz` items in `CifBlock`. Parameters ---------- block : CifBlock Instance of `CifBlock`. """ from diffpy.structure.spacegroups import FindSpaceGroup, GetSpaceGroup, IsSpaceGroupIdentifier, SpaceGroup self.asymmetric_unit = list(self.stru) sym_synonyms = ("_space_group_symop_operation_xyz", "_symmetry_equiv_pos_as_xyz") sym_loop_name = [n for n in sym_synonyms if n in block] # recover explicit list of symmetry operations symop_list = [] if sym_loop_name: # sym_loop exists here and we know its cif name sym_loop_name = sym_loop_name[0] sym_loop = block.GetLoop(sym_loop_name) for eqxyz in sym_loop[sym_loop_name]: opcif = getSymOp(eqxyz) symop_list.append(opcif) # determine space group number sg_nameHall = block.get("_space_group_name_Hall", "") or block.get("_symmetry_space_group_name_Hall", "") sg_nameHM = ( block.get("_space_group_name_H-M_alt", "") or block.get("_space_group_name_H-M_ref", "") or block.get("_symmetry_space_group_name_H-M", "") ) self.cif_sgname = sg_nameHall or sg_nameHM or None sgid = block.get("_space_group_IT_number", "") or block.get("_symmetry_Int_Tables_number", "") or sg_nameHM self.spacegroup = None # try to reuse existing space group from symmetry operations if symop_list: try: self.spacegroup = FindSpaceGroup(symop_list) except ValueError: pass # otherwise lookup the space group from its identifier if self.spacegroup is None and sgid and IsSpaceGroupIdentifier(sgid): self.spacegroup = GetSpaceGroup(sgid) # define new spacegroup when symmetry operations were listed, but # there is no match to an existing definition if symop_list and self.spacegroup is None: new_short_name = "CIF " + (sg_nameHall or "data") new_crystal_system = ( block.get("_space_group_crystal_system") or block.get("_symmetry_cell_setting") or "TRICLINIC" ).upper() self.spacegroup = SpaceGroup( short_name=new_short_name, crystal_system=new_crystal_system, symop_list=symop_list ) if self.spacegroup is None: emsg = "CIF file has unknown space group identifier {!r}." raise StructureFormatError(emsg.format(sgid)) self._expandAsymmetricUnit(block) return def _expandAsymmetricUnit(self, block): """Perform symmetry expansion of `self.stru` using `self.spacegroup`. This method updates data in `stru` and `eau`. Parameters ---------- block : CifBlock The top-level block containing crystal structure data. """ from diffpy.structure.symmetryutilities import ExpandAsymmetricUnit corepos = [a.xyz for a in self.stru] coreUijs = [a.U for a in self.stru] self.eau = ExpandAsymmetricUnit(self.spacegroup, corepos, coreUijs, eps=self.eps) # setup anisotropy according to symmetry requirements # unless it was already explicitly set for ca, uisotropy in zip(self.stru, self.eau.Uisotropy): if ca.label not in self.anisotropy: ca.anisotropy = not uisotropy self.anisotropy[ca.label] = ca.anisotropy # build a nested list of new atoms: newatoms = [] for i, ca in enumerate(self.stru): eca = [] # expanded core atom for j in range(self.eau.multiplicity[i]): a = Atom(ca) a.xyz = self.eau.expandedpos[i][j] if j > 0: a.label += "_" + str(j + 1) if a.anisotropy: a.U = self.eau.expandedUijs[i][j] eca.append(a) newatoms.append(eca) # insert new atoms where they belong self.stru[:] = sum(newatoms, []) return # conversion to CIF ------------------------------------------------------
[docs] def toLines(self, stru): """Convert `Structure` to a list of lines in basic CIF format. Parameters ---------- stru : Structure The structure to be converted. Returns ------- list List of lines in basic CIF format. """ import time lines = [] # may be replaced with filtered Structure.title # for now, we can add the title as a comment if stru.title.strip() != "": title_lines = stru.title.split("\n") lines.extend(["# " + line.strip() for line in title_lines]) lines.append("") lines.append("data_3D") iso_date = "%04i-%02i-%02i" % time.gmtime()[:3] lines.extend( [ "%-31s %s" % ("_audit_creation_date", iso_date), "%-31s %s" % ("_audit_creation_method", "P_cif.py"), "", "%-31s %s" % ("_symmetry_space_group_name_H-M", "'P1'"), "%-31s %s" % ("_symmetry_Int_Tables_number", "1"), "%-31s %s" % ("_symmetry_cell_setting", "triclinic"), "", ] ) # there should be no need to specify equivalent positions for P1 # _symmetry_equiv_posi_as_xyz x,y,z lines.extend( [ "%-31s %.6g" % ("_cell_length_a", stru.lattice.a), "%-31s %.6g" % ("_cell_length_b", stru.lattice.b), "%-31s %.6g" % ("_cell_length_c", stru.lattice.c), "%-31s %.6g" % ("_cell_angle_alpha", stru.lattice.alpha), "%-31s %.6g" % ("_cell_angle_beta", stru.lattice.beta), "%-31s %.6g" % ("_cell_angle_gamma", stru.lattice.gamma), "", ] ) # build a list of site labels and adp (displacement factor) types element_count = {} a_site_label = [] a_adp_type = [] for a in stru: cnt = element_count[a.element] = element_count.get(a.element, 0) + 1 a_site_label.append("%s%i" % (a.element, cnt)) if numpy.all(a.U == a.U[0, 0] * numpy.identity(3)): a_adp_type.append("Uiso") else: a_adp_type.append("Uani") # list all atoms lines.extend( [ "loop_", " _atom_site_label", " _atom_site_type_symbol", " _atom_site_fract_x", " _atom_site_fract_y", " _atom_site_fract_z", " _atom_site_U_iso_or_equiv", " _atom_site_adp_type", " _atom_site_occupancy", ] ) for i in range(len(stru)): a = stru[i] line = " %-5s %-3s %11.6f %11.6f %11.6f %11.6f %-5s %.4f" % ( a_site_label[i], a.element, a.xyz[0], a.xyz[1], a.xyz[2], a.Uisoequiv, a_adp_type[i], a.occupancy, ) lines.append(line) # find anisotropic atoms idx_aniso = [i for i in range(len(stru)) if a_adp_type[i] != "Uiso"] if idx_aniso != []: lines.extend( [ "loop_", " _atom_site_aniso_label", " _atom_site_aniso_U_11", " _atom_site_aniso_U_22", " _atom_site_aniso_U_33", " _atom_site_aniso_U_12", " _atom_site_aniso_U_13", " _atom_site_aniso_U_23", ] ) for i in idx_aniso: a = stru[i] line = " %-5s %9.6f %9.6f %9.6f %9.6f %9.6f %9.6f" % ( a_site_label[i], a.U[0, 0], a.U[1, 1], a.U[2, 2], a.U[0, 1], a.U[0, 2], a.U[1, 2], ) lines.append(line) return lines
# End of class P_cif # Routines ------------------------------------------------------------------- # constant regular expression for leading_float() rx_float = re.compile(r"[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?")
[docs] def leading_float(s, d=0.0): """Extract the first float from a string and ignore trailing characters. Useful for extracting values from "value(std)" syntax. Parameters ---------- s : str The string to be scanned for floating point value. d : float, Optional The default value when `s` is "." or "?", which in CIF format stands for inapplicable and unknown, respectively. Returns ------- float The extracted floating point value. Raises ------ ValueError When string does not start with a float. """ sbare = s.strip() mx = rx_float.match(sbare) if mx: rv = float(mx.group()) elif sbare == "." or sbare == "?": # CIF files may contain "." or "?" for unknown values rv = d else: rv = float(sbare) return rv
# helper dictionary for getSymOp() symvec = { "x": numpy.array([1, 0, 0], dtype=float), "y": numpy.array([0, 1, 0], dtype=float), "z": numpy.array([0, 0, 1], dtype=float), "-x": numpy.array([-1, 0, 0], dtype=float), "-y": numpy.array([0, -1, 0], dtype=float), "-z": numpy.array([0, 0, -1], dtype=float), } symvec["+x"] = symvec["x"] symvec["+y"] = symvec["y"] symvec["+z"] = symvec["z"]
[docs] def getSymOp(s): """Create `SpaceGroups.SymOp` instance from a string. Parameters ---------- s : str Formula for equivalent coordinates, for example ``'x,1/2-y,1/2+z'``. Returns ------- SymOp Instance of `SymOp`. """ from diffpy.structure.spacegroups import SymOp snoblanks = s.replace(" ", "") eqlist = snoblanks.split(",") R = numpy.zeros((3, 3), dtype=float) t = numpy.zeros(3, dtype=float) for i in (0, 1, 2): eqparts = re.split("(?i)([+-]?[xyz])", eqlist[i]) for Rpart in eqparts[1::2]: R[i, :] += symvec[Rpart.lower()] for tpart in eqparts[::2]: t[i] += eval("1.0*%s+0" % tpart) t -= numpy.floor(t) rv = SymOp(R, t) return rv
[docs] def getParser(eps=None): """Return new `parser` object for CIF format. Parameters ---------- eps : float, Optional fractional coordinates cutoff for duplicate positions. When ``None`` use the default for `ExpandAsymmetricUnit`: ``1.0e-5``. Returns ------- P_cif Instance of `P_cif`. """ return P_cif(eps=eps)
# Local Helpers -------------------------------------------------------------- @contextmanager def _suppressCifParserOutput(): """Context manager which suppresses diagnostic messages from CIF parser.""" from CifFile import yapps3_compiled_rt print_error = yapps3_compiled_rt.print_error # replace the print_error function with no-operation yapps3_compiled_rt.print_error = lambda *a, **kw: None try: yield print_error finally: yapps3_compiled_rt.print_error = print_error pass