class Complexes(Logging): """Manipulate complexes of Proteins This class uses Intact Complex database to extract information about complexes of proteins. When creating an instance, the default organism is "H**o sapiens". The organism can be set to another one during the instanciation or later:: >>> from biokit.network.complexes import Complexes >>> c = Complexes(organism='H**o sapiens') >>> c.organism = 'Rattus norvegicus' Valid organisms can be found in :attr:`organisms`. When changing the organism, a request to the Intact database is sent, which may take some time to update. Once done, information related to this organism is stored in the :attr:`df` attribute, which is a Pandas dataframe. It contains 4 columns. Here is for example one row:: complexAC EBI-2660609 complexName COP9 signalosome variant 1 description Essential regulator of the ubiquitin (Ubl) con... organismName H**o sapiens; 9606 This is basic information but once a complex accession (e.g., EBI-2660609) is known, you can retrieve detailled information. This is done automatically for all the accession when needed. The first time, it will take a while (20 seconds for 250 accession) but will be cache for this instance. The :attr:`complexes` contains all details about the entries found in :attr:`df`. It is a dictionary where keys are the complex accession. For instance:: >>> c.complexes['EBI-2660609'] In general, one is interested in the participants of the complex, that is the proteins that form the complex. Another attribute is set for you:: >>> c.participants['EBI-2660609'] Finally, you may even want to obtain just the identifier of the participants for each complex. This is stored in the :attr:`identifiers`:: >>> c.identifiers['EBI-2660609'] Note however, that the identifiers are not neceseraly uniprot identifiers. Could be ChEBI or sometimes even set to None. The :meth:`strict_filter` removes the complexes with less than 2 (strictly) uniprot identifiers. Some basic statistics can be printed with :meth:`stats` that indeticates the number of complexes, number of identifiers in those complexes ,and number of unique identifiers. A histogram of number of appearance of each identifier is also shown. The :meth:`hist_participants` shows the number of participants per complex. Finally, the meth:`search_complexes` can be used in the context of logic modelling to infer the AND gates from a list of uniprot identifiers provided by the user. See :meth:`search_complexes` for details. Access to the Intact Complex database is performed using the package BioServices provided in Pypi. """ def __init__(self, organism='H**o sapiens', verbose=True, cache=False): """.. rubric:: Constructor :param str orgamism: the organism to look at. H**o sapiens is the default. Other possible organisms can be found in :attr:`organisms`. :param str verbose: a verbose level in ERROR/DEBUG/INFO/WARNING compatible with those used in BioServices. """ super(Complexes, self).__init__(level=verbose) self.devtools = DevTools() self.webserv = IntactComplex(verbose=verbose, cache=cache) df = self.webserv.search('*', frmt='pandas') self.df = df #: list of valid organisms found in the database self.valid_organisms = list(set(df['organismName'])) self.valid_organisms = [x.split(';')[0] for x in self.valid_organisms] #: list of valid organisms found in the database self.organisms = list(set(df['organismName'])) self._organism = None if organism in self.organisms: self.organism = organism else: print("Organism not set yet. ") # This will populated on request as a cache/buffer self._details = None self._complexes = None def _get_organism(self): return self._organism def _set_organism(self, organism): self.devtools.check_param_in_list(organism, [str(x.split(";")[0]) for x in self.valid_organisms]) self._organism = organism self.df = self.webserv.search('*', frmt='pandas', filters='species_f:("%s")' % self.organism) self._complexes = None organism = property(_get_organism, _set_organism, doc="Getter/Setter of the organism") def hist_participants(self): """Histogram of the number of participants per complexes :return: a dictionary with complex identifiers as keys and number of participants as values :: from biokit.network.complexes import Complexes c = Complexes() c.hist_participants() """ N = [] count = {} for i, identifier in enumerate(self.complexes.keys()): n = len(self.complexes[identifier]['participants']) N.append(n) count[identifier] = n _ = pylab.hist(N, bins=range(0, max(N))) pylab.title('Number of participants per complex') pylab.grid() return count def stats(self): """Prints some stats about the number of complexes and histogram of the number of appearances of each species""" species = [] for k in self.participants.keys(): species.extend([x['identifier'] for x in self.participants[k]]) N = [] for spec in set(species): N.append(species.count(spec)) _ = pylab.hist(N, bins=range(0, max(N))) pylab.title("Number of appaerances of each species") pylab.grid() print("""There are %s complexes involving %s participants with %s unique species. """ % (len(self.complexes), len(species), len(set(species)))) def _get_participants(self): participants = {} for k,v in self.complexes.items(): participants[k] = v['participants'] return participants participants = property(_get_participants, doc="""Getter of the complex participants (full details)""") def _get_identifiers(self): identifiers = {} for k,v in self.participants.items(): identifiers[k] = [x['identifier'] for x in v] return identifiers identifiers = property(_get_identifiers, doc="""Getter of the identifiers of the complex participants""") def _get_complexes(self): if self._complexes is None: self._load_complexes() return self._complexes.copy() complexes = property(_get_complexes, doc="""Getter of the complexes (full details""") def _load_complexes(self, show_progress=True): from easydev import Progress import time pb = Progress(len(self.df.complexAC)) complexes = {} self.logging.info("Loading all details from the IntactComplex database") for i, identifier in enumerate(self.df.complexAC): res = self.webserv.details(identifier) complexes[identifier] = res if show_progress: pb.animate(i+1) self._complexes = complexes def remove_homodimers(self): """Remove identifiers that are None or starts with CHEBI and keep complexes that have at least 2 participants :return: list of complex identifiers that have been removed. """ # None are actually h**o dimers toremove = [] for k,this in self.identifiers.items(): remains = [x for x in this if x is not None] if len(remains)<=1: toremove.append(k) self.logging.info("removing %s homodimers complexes" % len(toremove)) for this in toremove: del self._complexes[this] return toremove def search_complexes(self, user_species, verbose=False): """Given a list of uniprot identifiers, return complexes and possible complexes. :param list user_species: list of uniprot identifiers to be found in the complexes :return: two dictionaries. First one contains the complexes for which all participants have been found in the user_species list. The second one contains complexes for which some participants (not all) have been found in the user_species list. """ level = self.debugLevel[:] if verbose: self.debugLevel = 'INFO' else: self.debugLevel = 'ERROR' and_gates = {} candidates = {} identifiers = self.identifiers.values() for k, identifiers in self.identifiers.items(): # get rid of suffixes such as -1 or -PRO_xxx prefixes = [x.split("-")[0] if x is not None else x for x in identifiers] # You may have a complex with ['P12222', 'P33333-PRO1', # 'P33333-PRO2'], in which case P33333 is found only once and # thereofre the final number of found participants is not the length # of the complexes...so we need to get rid of the duplicates if any prefixes = list(set(prefixes)) N = len(prefixes) found = [spec for spec in user_species if spec in prefixes] if len(found) == N: self.logging.info('Found entire complex %s ' % k) and_gates[k] = identifiers[:] elif len(found) >= 1: self.logging.info('Found partial complex %s with %s participants out of %s' % (k, len(found), len(identifiers))) candidates[k] = {'participants': identifiers, 'found': found} self.debugLevel = level[:] return and_gates, candidates def search(self, name): """Search for a unique identifier (e.g. uniprot) in all complexes :return: list of complex identifiers where the name was found """ found = [] for k, identifiers in self.identifiers.items(): prefixes = [x.split("-")[0] if x is not None else x for x in identifiers ] if name in prefixes: self.logging.info("Found %s in complex %s (%s)" % (name, k, identifiers)) found.append(k) return found def chebi2name(self, name): """Return the ASCII name of a CHEBI identifier""" from bioservices import ChEBI c = ChEBI() name = dict(c.getLiteEntity(name)[0])['chebiAsciiName'] return name def uniprot2genename(self, name): """Return the gene names of a UniProt identifier""" from bioservices import UniProt c = UniProt(cache=True) try: res = pd.read_csv(StringIO(c.search(name, limit=1)), sep='\t') return list(res['Gene names'].values) except: print("Could not find %s" % name) def report(self, species): complete, partial = self.search_complexes(species, verbose=False) res = {'Found':[], 'Participants':[], 'Complete':[], 'Identifier':[], 'Number found':[], 'Number of participants':[], 'Name':[]} for k, v in complete.items(): res['Name'].append(self.complexes[k]['name']) res['Found'].append(";".join(v)) res['Number found'].append(len(v)) res['Participants'].append(";".join(self.identifiers[k])) res['Number of participants'].append(len(self.identifiers[k])) res['Complete'].append(True) res['Identifier'].append(k) for k, v in partial.items(): res['Name'].append(self.complexes[k]['name']) res['Found'].append(";".join(v['found'])) res['Number found'].append(len(v['found'])) res['Participants'].append(";".join(self.identifiers[k])) res['Number of participants'].append(len(self.identifiers[k])) res['Complete'].append(False) res['Identifier'].append(k) res = pd.DataFrame(res, columns=['Found', 'Participants', 'Identifier', 'Name', 'Number found', 'Number of participants', 'Complete']) return res
def scoring(args=None): """This function is used by the standalone application called dreamscoring :: dreamscoring --help """ d = DevTools() if args is None: args = sys.argv[:] user_options = Options(prog="dreamtools") if len(args) == 1: user_options.parse_args(["prog", "--help"]) else: options = user_options.parse_args(args[1:]) if options.version is True: print("%s" % dreamtools.version) sys.exit() # Check on the challenge name if options.challenge is None: print_color('--challenge must be provided', red) sys.exit() else: options.challenge = options.challenge.upper() options.challenge = options.challenge.replace('DOT', 'dot') from dreamtools.admin.download_data import get_challenge_list if options.challenge not in get_challenge_list(): print_color( "This challenge %s is not registered in dreamtools." % options.challenge, red) print("Here is the list of registered challenges: " + ", ".join(get_challenge_list())) sys.exit() # Check that the challenge can be loaded class_inst = get_challenge(options.challenge) try: this = class_inst.import_scoring_class() except NotImplementedError as err: print("\n" + str(err)) sys.exit() else: # User may just request some information about the challenge. if options.info is True: print(this) sys.exit() elif options.onweb is True: this.onweb() sys.exit() # Checks name of the sub-challenges subchallenges = get_subchallenges(options.challenge) if len(subchallenges) and options.sub_challenge is None: txt = "This challenge requires a sub challenge name. " txt += "Please use --sub-challenge followed by one value in %s " % subchallenges print_color(txt, red) sys.exit(0) if options.sub_challenge is not None and len(subchallenges) != 0: try: d.check_param_in_list(options.sub_challenge, subchallenges) except ValueError as err: txt = "DREAMTools error: unknown sub challenge or not implemented" txt += "--->" + str(err) print_color(txt, red) sys.exit() # maybe users just need a template if options.download_template is True: c = Challenge(options.challenge) class_inst = c.import_scoring_class() if options.sub_challenge is None: print(class_inst.download_template()) else: print(class_inst.download_template(options.sub_challenge)) return # similary for the GS if options.download_goldstandard is True: c = Challenge(options.challenge) class_inst = c.import_scoring_class() if options.sub_challenge is None: print(class_inst.download_goldstandard()) else: print(class_inst.download_goldstandard(options.sub_challenge)) return # finally, we need a submission if options.filename is None: txt = "---> filename not provided. You must provide a filename with correct format\n" txt += "You may get a template using --download-template \n" txt += "Alternatively, you can user either --info or --onweb option to get information about the challenge.\n" txt += "https://github.com/dreamtools/dreamtools, or http://dreamchallenges.org\n" print_color(txt, red) sys.exit() # filename # filename in general is a single string but could be a list of filenames # Because on the parser, we must convert the string into a single string # if the list haa a length of 1 for filename in options.filename: if os.path.exists(filename) is False: raise IOError("file %s does not seem to exists" % filename) if len(options.filename) == 1: options.filename = options.filename[0] print_color("DREAMTools scoring", purple, underline=True) print('Challenge %s (sub challenge %s)\n\n' % (options.challenge, options.sub_challenge)) res = generic_scoring(options.challenge, options.filename, subname=options.sub_challenge, goldstandard=options.goldstandard) txt = "Solution for %s in challenge %s" % (options.filename, options.challenge) if options.sub_challenge is not None: txt += " (sub-challenge %s)" % options.sub_challenge txt += " is :\n" for k in sorted(res.keys()): txt += darkgreen(" %s:\n %s\n" % (k, res[k])) print(txt)
class Service(object): """Base class for WSDL and REST classes .. seealso:: :class:`REST`, :class:`WSDLService` """ #: some useful response codes response_codes = { 200: 'OK', 201: 'Created', 400: 'Bad Request. There is a problem with your input', 404: 'Not found. The resource you requests does not exist', 405: 'Method not allowed', 406: "Not Acceptable. Usually headers issue", 410: 'Gone. The resource you requested was removed.', 415: "Unsupported Media Type", 500: 'Internal server error. Most likely a temporary problem', 503: 'Service not available. The server is being updated, try again later' } def __init__(self, name, url=None, verbose=True, requests_per_sec=10): """.. rubric:: Constructor :param str name: a name for this service :param str url: its URL :param bool verbose: prints informative messages if True (default is True) :param requests_per_sec: maximum number of requests per seconds are restricted to 3. You can change that value. If you reach the limit, an error is raise. The reason for this limitation is that some services (e.g.., NCBI) may black list you IP. If you need or can do more (e.g., ChEMBL does not seem to have restrictions), change the value. You can also have several instance but again, if you send too many requests at the same, your future requests may be retricted. Currently implemented for REST only All instances have an attribute called :attr:`~Service.logging` that is an instanceof the :mod:`logging` module. It can be used to print information, warning, error messages:: self.logging.info("informative message") self.logging.warning("warning message") self.logging.error("error message") The attribute :attr:`~Service.debugLevel` can be used to set the behaviour of the logging messages. If the argument verbose is True, the debugLebel is set to INFO. If verbose if False, the debugLevel is set to WARNING. However, you can use the :attr:`debugLevel` attribute to change it to one of DEBUG, INFO, WARNING, ERROR, CRITICAL. debugLevel=WARNING means that only WARNING, ERROR and CRITICAL messages are shown. """ super(Service, self).__init__() self.requests_per_sec = requests_per_sec self.name = name self.logging = Logging("bioservices:%s" % self.name, verbose) self._url = url try: if self.url is not None: urlopen(self.url) except Exception as err: self.logging.warning("The URL (%s) provided cannot be reached." % self.url) self._easyXMLConversion = True # used by HGNC where some XML contains non-utf-8 characters !! # should be able to fix it with requests once HGNC works again #self._fixing_unicode = False #self._fixing_encoding = "utf-8" self.devtools = DevTools() self.settings = BioServicesConfig() self._last_call = 0 def _calls(self): time_lapse = 1. / self.requests_per_sec current_time = time.time() dt = current_time - self._last_call if self._last_call == 0: self._last_call = current_time return else: self._last_call = current_time if dt > time_lapse: return else: time.sleep(time_lapse - dt) def _get_caching(self): return self.settings.params['cache.on'][0] def _set_caching(self, caching): self.devtools.check_param_in_list(caching, [True, False]) self.settings.params['cache.on'][0] = caching # reset the session, which will be automatically created if we # access to the session attribute self._session = None CACHING = property(_get_caching, _set_caching) def _get_url(self): return self._url def _set_url(self, url): # something more clever here to check the URL e.g. starts with http if url is not None: url = url.rstrip("/") self._url = url url = property(_get_url, _set_url, doc="URL of this service") def _get_easyXMLConversion(self): return self._easyXMLConversion def _set_easyXMLConversion(self, value): if isinstance(value, bool) is False: raise TypeError("value must be a boolean value (True/False)") self._easyXMLConversion = value easyXMLConversion = property( _get_easyXMLConversion, _set_easyXMLConversion, doc= """If True, xml output from a request are converted to easyXML object (Default behaviour).""" ) def easyXML(self, res): """Use this method to convert a XML document into an :class:`~bioservices.xmltools.easyXML` object The easyXML object provides utilities to ease access to the XML tag/attributes. Here is a simple example starting from the following XML .. doctest:: >>> from bioservices import * >>> doc = "<xml> <id>1</id> <id>2</id> </xml>" >>> s = Service("name") >>> res = s.easyXML(doc) >>> res.findAll("id") [<id>1</id>, <id>2</id>] """ from bioservices import xmltools return xmltools.easyXML(res) def __str__(self): txt = "This is an instance of %s service" % self.name return txt def pubmed(self, Id): """Open a pubmed Id into a browser tab :param Id: a valid pubmed Id in string or integer format. The URL is a concatenation of the pubmed URL http://www.ncbi.nlm.nih.gov/pubmed/ and the provided Id. """ url = "http://www.ncbi.nlm.nih.gov/pubmed/" import webbrowser webbrowser.open(url + str(Id)) def on_web(self, url): """Open a URL into a browser""" import webbrowser webbrowser.open(url) def save_str_to_image(self, data, filename): """Save string object into a file converting into binary""" with open(filename, 'wb') as f: import binascii try: #python3 newres = binascii.a2b_base64(bytes(data, "utf-8")) except: newres = binascii.a2b_base64(data) f.write(newres)
def scoring(args=None): """This function is used by the standalone application called dreamscoring :: dreamscoring --help """ d = DevTools() if args is None: args = sys.argv[:] user_options = Options(prog="dreamtools") if len(args) == 1: user_options.parse_args(["prog", "--help"]) else: options = user_options.parse_args(args[1:]) if options.version is True: print("%s" % dreamtools.version) sys.exit() # Check on the challenge name if options.challenge is None: print_color('--challenge must be provided', red) sys.exit() else: options.challenge = options.challenge.upper() options.challenge = options.challenge.replace('DOT', 'dot') from dreamtools.admin.download_data import get_challenge_list if options.challenge not in get_challenge_list(): print_color("This challenge %s is not registered in dreamtools." % options.challenge, red) print("Here is the list of registered challenges: " + ", ".join(get_challenge_list())) sys.exit() # Check that the challenge can be loaded class_inst = get_challenge(options.challenge) try: this = class_inst.import_scoring_class() except NotImplementedError as err: print("\n"+str(err)) sys.exit() else: # User may just request some information about the challenge. if options.info is True: print(this) sys.exit() elif options.onweb is True: this.onweb() sys.exit() # Checks name of the sub-challenges subchallenges = get_subchallenges(options.challenge) if len(subchallenges) and options.sub_challenge is None: txt = "This challenge requires a sub challenge name. " txt += "Please use --sub-challenge followed by one value in %s " % subchallenges print_color(txt, red) sys.exit(0) if options.sub_challenge is not None and len(subchallenges) != 0: try: d.check_param_in_list(options.sub_challenge, subchallenges) except ValueError as err: txt = "DREAMTools error: unknown sub challenge or not implemented" txt += "--->" + str(err) print_color(txt, red) sys.exit() # maybe users just need a template if options.download_template is True: c = Challenge(options.challenge) class_inst = c.import_scoring_class() if options.sub_challenge is None: print(class_inst.download_template()) else: print(class_inst.download_template(options.sub_challenge)) return # similary for the GS if options.download_goldstandard is True: c = Challenge(options.challenge) class_inst = c.import_scoring_class() if options.sub_challenge is None: print(class_inst.download_goldstandard()) else: print(class_inst.download_goldstandard(options.sub_challenge)) return # finally, we need a submission if options.filename is None: txt = "---> filename not provided. You must provide a filename with correct format\n" txt += "You may get a template using --download-template \n" txt += "Alternatively, you can user either --info or --onweb option to get information about the challenge.\n" txt += "https://github.com/dreamtools/dreamtools, or http://dreamchallenges.org\n" print_color(txt, red) sys.exit() # filename # filename in general is a single string but could be a list of filenames # Because on the parser, we must convert the string into a single string # if the list haa a length of 1 for filename in options.filename: if os.path.exists(filename) is False: raise IOError("file %s does not seem to exists" % filename) if len(options.filename) == 1: options.filename = options.filename[0] print_color("DREAMTools scoring", purple, underline=True) print('Challenge %s (sub challenge %s)\n\n' % (options.challenge, options.sub_challenge)) res = generic_scoring(options.challenge, options.filename, subname=options.sub_challenge, goldstandard=options.goldstandard) txt = "Solution for %s in challenge %s" % (options.filename, options.challenge) if options.sub_challenge is not None: txt += " (sub-challenge %s)" % options.sub_challenge txt += " is :\n" for k in sorted(res.keys()): txt += darkgreen(" %s:\n %s\n" %(k, res[k])) print(txt)
def scoring(args=None): """This function is used by the standalone application called dreamscoring :: dreamscoring --help """ d = DevTools() if args == None: args = sys.argv[:] user_options = Options(prog="dreamtools") if len(args) == 1: user_options.parse_args(["prog", "--help"]) else: options = user_options.parse_args(args[1:]) # Check on the challenge name if options.challenge is None: print_color("--challenge and --sub-challenge must be provided", red) sys.exit() else: options.challenge = options.challenge.upper() options.challenge = options.challenge.replace("DOT", "dot") # Check that the challenge can be loaded class_inst = get_challenge(options.challenge) try: class_inst.import_scoring_class() except NotImplementedError as err: print("\n" + err.message) sys.exit() # Checks name of the sub-challenges subchallenges = get_subchallenges(options.challenge) if len(subchallenges) and options.sub_challenge is None: txt = "This challenge requires a sub challenge name." txt += "Please provide one amongst %s " % subchallenges print_color(txt, red) sys.exit(0) if options.sub_challenge is not None and len(subchallenges) != 0: try: d.check_param_in_list(options.sub_challenge, subchallenges) except ValueError as err: txt = "DreamTools error: unknown sub challenge or not implemented" txt += "--->" + err.message print_color(txt, red) sys.exit() if options.download_template is True: c = Challenge(options.challenge) class_inst = c.import_scoring_class() if options.sub_challenge is None: print(class_inst.download_template()) else: print(class_inst.download_template(options.sub_challenge)) return # similary for the GS if options.download_goldstandard is True: c = Challenge(options.challenge) class_inst = c.import_scoring_class() if options.sub_challenge is None: print(class_inst.download_goldstandard()) else: print(class_inst.download_goldstandard(options.sub_challenge)) return if options.filename is None: txt = "---> filename not provided. You must provide a filename with correct format\n" txt += "You may get a template using --download-template option\n" txt += "https://github.com/dreamtools/dreamtools, or http://dreamchallenges.org\n" print_color(txt, red) sys.exit() # filename # filename in general is a single string but could be a list of filenames # Because on the parser, we must convert the string into a single string # if the list haa a length of 1 for filename in options.filename: if os.path.exists(filename) is False: raise IOError("file %s does not seem to exists" % filename) if len(options.filename) == 1: options.filename = options.filename[0] print_color("Dreamtools scoring", purple, underline=True) print("Challenge %s (sub challenge %s)\n\n" % (options.challenge, options.sub_challenge)) res = "??" if options.challenge == "D8C1": if options.sub_challenge == "sc1a": res = d8c1_sc1a(options.filename, verbose=options.verbose) elif options.sub_challenge == "sc1b": res = d8c1_sc1b(options.filename, verbose=options.verbose) elif options.sub_challenge == "sc2a": res = d8c1_sc2a(options.filename, verbose=options.verbose) elif options.sub_challenge == "sc2b": res = d8c1_sc2b(options.filename, verbose=options.verbose) else: res = generic_scoring( options.challenge, options.filename, subname=options.sub_challenge, goldstandard=options.goldstandard ) txt = "Solution for %s in challenge %s" % (options.filename, options.challenge) if options.sub_challenge is not None: txt += " (sub-challenge %s)" % options.sub_challenge txt += " is :\n" for k in sorted(res.keys()): txt += darkgreen(" %s:\n %s\n" % (k, res[k])) print(txt)
class Service(object): """Base class for WSDL and REST classes .. seealso:: :class:`REST`, :class:`WSDLService` """ #: some useful response codes response_codes = { 200: 'OK', 201: 'Created', 400: 'Bad Request. There is a problem with your input', 404: 'Not found. The resource you requests does not exist', 405: 'Method not allowed', 406: "Not Acceptable. Usually headers issue", 410: 'Gone. The resource you requested was removed.', 415: "Unsupported Media Type", 500: 'Internal server error. Most likely a temporary problem', 503: 'Service not available. The server is being updated, try again later' } def __init__(self, name, url=None, verbose=True, requests_per_sec=3): """.. rubric:: Constructor :param str name: a name for this service :param str url: its URL :param bool verbose: prints informative messages if True (default is True) :param requests_per_sec: maximum number of requests per seconds are restricted to 3. You can change that value. If you reach the limit, an error is raise. The reason for this limitation is that some services (e.g.., NCBI) may black list you IP. If you need or can do more (e.g., ChEMBL does not seem to have restrictions), change the value. You can also have several instance but again, if you send too many requests at the same, your future requests may be retricted. Currently implemented for REST only All instances have an attribute called :attr:`~Service.logging` that is an instanceof the :mod:`logging` module. It can be used to print information, warning, error messages:: self.logging.info("informative message") self.logging.warning("warning message") self.logging.error("error message") The attribute :attr:`~Service.debugLevel` can be used to set the behaviour of the logging messages. If the argument verbose is True, the debugLebel is set to INFO. If verbose if False, the debugLevel is set to WARNING. However, you can use the :attr:`debugLevel` attribute to change it to one of DEBUG, INFO, WARNING, ERROR, CRITICAL. debugLevel=WARNING means that only WARNING, ERROR and CRITICAL messages are shown. """ super(Service, self).__init__() self.requests_per_sec = requests_per_sec self.name = name self.logging = Logging("bioservices:%s" % self.name, verbose) self._url = url try: if self.url is not None: urlopen(self.url) except Exception as err: self.logging.warning("The URL (%s) provided cannot be reached." % self.url) self._easyXMLConversion = True # used by HGNC where some XML contains non-utf-8 characters !! # should be able to fix it with requests once HGNC works again #self._fixing_unicode = False #self._fixing_encoding = "utf-8" self.devtools = DevTools() self.settings = BioServicesConfig() def _get_caching(self): return self.settings.params['cache.on'][0] def _set_caching(self, caching): self.devtools.check_param_in_list(caching, [True, False]) self.settings.params['cache.on'][0] = caching # reset the session, which will be automatically created if we # access to the session attribute self._session = None CACHING = property(_get_caching, _set_caching) def _get_url(self): return self._url def _set_url(self, url): # something more clever here to check the URL e.g. starts with http if url is not None: url = url.rstrip("/") self._url = url url = property(_get_url, _set_url, doc="URL of this service") def _get_easyXMLConversion(self): return self._easyXMLConversion def _set_easyXMLConversion(self, value): if isinstance(value, bool) is False: raise TypeError("value must be a boolean value (True/False)") self._easyXMLConversion = value easyXMLConversion = property(_get_easyXMLConversion, _set_easyXMLConversion, doc="""If True, xml output from a request are converted to easyXML object (Default behaviour).""") def easyXML(self, res): """Use this method to convert a XML document into an :class:`~bioservices.xmltools.easyXML` object The easyXML object provides utilities to ease access to the XML tag/attributes. Here is a simple example starting from the following XML .. doctest:: >>> from bioservices import * >>> doc = "<xml> <id>1</id> <id>2</id> </xml>" >>> s = Service("name") >>> res = s.easyXML(doc) >>> res.findAll("id") [<id>1</id>, <id>2</id>] """ from bioservices import xmltools return xmltools.easyXML(res) def __str__(self): txt = "This is an instance of %s service" % self.name return txt def pubmed(self, Id): """Open a pubmed Id into a browser tab :param Id: a valid pubmed Id in string or integer format. The URL is a concatenation of the pubmed URL http://www.ncbi.nlm.nih.gov/pubmed/ and the provided Id. """ url = "http://www.ncbi.nlm.nih.gov/pubmed/" import webbrowser webbrowser.open(url + str(Id)) def on_web(self, url): """Open a URL into a browser""" import webbrowser webbrowser.open(url) def save_str_to_image(self, data, filename): """Save string object into a file converting into binary""" with open(filename,'wb') as f: import binascii try: #python3 newres = binascii.a2b_base64(bytes(data, "utf-8")) except: newres = binascii.a2b_base64(data) f.write(newres)