def test_logging(): l = Logging("INFO") l.name = "test" l.info("test") l.debug("test") l.warning("test") l.error("test") l.critical("test") for level in ['DEBUG', 'INFO', 'ERROR', 'WARNING', 'CRITICAL']: l.level = level assert l.level == level l.level = True l.level = False for x in [10, 20, 30, 40, 50]: l.level = x try: l.level = "WARN" assert Fales except: assert True # FIXME is this working ??wierd syntax in loggibg_tools. import copy copy.copy(l) copy.deepcopy(l)
class RPackageManager(object): """Implements a R package manager from Python So far you can install a package (from source, or CRAN, or biocLite) :: pm = PackageManager() [(x, pm.installed[x][2]) for x in pm.installed.keys()] You can access to all information within a dataframe called **packages** where indices are the name packages. Some aliases are provided as attributes (e.g., available, installed) """ cran_repos = "http://cran.univ-lyon1.fr/" def __init__(self, verbose=True): self.session = RSession() self.logging = Logging(verbose) self.logging.info('Fetching package information') self.update() def _update(self): # local import ? import numpy import pandas # figure out the installed packages first code = """rvar_packages = as.data.frame(installed.packages())""" self.session.run(code) s = self.session.rvar_packages # FIXME. these 4 lines are needed as a hack related to pyper. try: s = s.replace("\n", "") df = eval(s) except: df = s df.set_index('Package', inplace=True) self._packages = df.copy() # Now, fetch was is possible to install from the default cran repo code = """rvar_status=packageStatus(repos="%s/src/contrib")""" code = code % self.cran_repos self.session.run(code) s = self.session.rvar_status # FIXME. try: s = s.replace("\n", "") res = eval(s) except: res = s res['inst'].set_index('Package', inplace=True) res['avail'].set_index('Package', inplace=True) self._status = res def update(self): """If you install/remove packages yourself elsewhere, you may need to call this function to update the package manager""" try: #self.session.reconnect() self._update() except: self.logging.warning("Could not update the packages. Call update() again") def _compat_version(self, version): return version.replace("-", "a") def _get_installed(self): # we do not buffer because packages may be removed manually or from R of # using remove_packages method, .... #self._package_status() return self._status['inst'] installed = property(_get_installed, "returns list of packages installed as a dataframe") def _get_available(self): # we do not buffer because packages may be removed manually or from R of # using remove_packages method, .... #self._package_status() return self._status['avail'] available = property(_get_available, "returns list of packages available as a dataframe") def _get_packages(self): # do not buffer since it may change in many places return self._packages packages = property(_get_packages) def get_package_latest_version(self, package): """Get latest version available of a package""" return self.available['Version'].ix[package] def get_package_version(self, package): """Get version of an install package""" if package not in self.installed.index: self.logging.error("package {0} not installed".format(package)) return self.installed['Version'].ix[package] def biocLite(self, package=None, suppressUpdates=True, verbose=False): """Installs one or more biocLite packages :param package: a package name (string) or list of package names (list of strings) that will be installed from BioConductor. If package is set to None, all packages already installed will be updated. """ if isinstance(package, str): if package not in self.installed.index: biocLite(package, suppressUpdates, verbose=verbose) elif isinstance(package, list): for pkg in package: self.logging.info("Installing %s" % pkg) if self.is_installed(pkg) is False: biocLite(pkg, suppressUpdates, verbose=verbose) else: # trying other cases (e.g., None updates biocLite itself). biocLite(package, suppressUpdates, verbose=verbose) self.update() def _isLocal(self, pkg): if os.path.exists(pkg): return True else: return False def remove(self, package): """Remove a package (or list) from local repository""" rcode ="""remove.packages("%s")""" if isinstance(package, str): package = [package] for pkg in package: if pkg in self.installed.index: self.session(rcode % pkg) else: self.logging.warning("Package not found. Nothing to remove") self.update() def require(self, pkg, version): "Check if a package with given version is available" if pkg not in self.installed.index: self.logging.info("Package %s not installed" % pkg) return False currentVersion = self.packageVersion(pkg) if self._get_version(currentVersion) >= self._get_version(version): return True else: return False def _install_package(self, packageName, dependencies=True): """Installs one or more CRAN packages .. todo:: check if it is already available to prevent renstallation ? """ repos = self.cran_repos # if this is a source file we want to reset the repo if isinstance(packageName, str): packageName = [packageName] for pkg in packageName: if self.is_installed(pkg) is False: self.logging.info("Package not found. Installing %s..." % pkg) install_package(pkg, dependencies=dependencies, repos=repos) else: self.logging.info("Package %s found. " % pkg) install_package(pkg, dependencies=dependencies, repos=repos) self.update() def install(self, pkg, require=None, update=True, reinstall=False): """install a package automatically scanning CRAN and biocLite repos if require is not set and update is True, when a newest version of a package is available, it is installed """ from easydev import to_list pkgs = to_list(pkg) for pkg in pkgs: self._install(pkg, require=require, update=update, reinstall=reinstall) def _install(self, pkg, require=None, update=update, reinstall=False): # LOCAL file if self._isLocal(pkg): # if a local file, we do not want to jump to biocLite or CRAN. Let # us install it directly. We cannot check version yet so we will # overwrite what is already installed self.logging.warning("Installing from source") self._install_package(pkg) return # From CRAN if self.is_installed(pkg): currentVersion = self.get_package_version(pkg) # if not provided, require should be the latest version if require is None and update is True: try: require = self.get_package_latest_version(pkg) except: # a non-cran package (bioclite maybe) pass if require is None: self.logging.info("%s already installed with version %s" % \ (pkg, currentVersion)) return # if require is not none, is it the required version ? if self._get_version(currentVersion) >= self._get_version(require) and reinstall is False: self.logging.info("%s already installed with required version %s" \ % (pkg, currentVersion)) # if so, nothing to do else: # Try updating self.logging.info("Updating") self._install_package(pkg) if require is None: return currentVersion = self.get_package_version(pkg) if self._get_version(currentVersion) < self._get_version(require): self.logging.warning("%s installed but current version (%s) does not fulfill your requirement" % \ (pkg, currentVersion)) elif pkg in self.available.index: self._install_package(pkg) else: # maybe a biocLite package ? # require is ignored. The latest will be installed self.logging.info("Trying to find the package on bioconductor") self.biocLite(pkg) if require is None: return currentVersion = self.get_package_version(pkg) if self._get_version(currentVersion) >= self._get_version(require): self.logging.warning("%s installed but version is %s too small (even after update)" % \ (pkg, currentVersion, require)) def _get_version(self, version): # some pacakge do not use the correct version convention try: return StrictVersion(version) except: try: return StrictVersion(version.replace("-", "a")) except: # snowfall package example was 1.86-6.1 # This becomes 1.86a61 which is not great but not workaround # for now left, right = version.split("-") version = left + "a" + right.replace('.', '') return StrictVersion(version) def is_installed(self, pkg_name): if pkg_name in self.installed.index: return True else: return False
class RPackageManager(object): """Implements a R package manager from Python So far you can install a package (from source, or CRAN, or biocLite) :: pm = PackageManager() [(x, pm.installed[x][2]) for x in pm.installed.keys()] You can access to all information within a dataframe called **packages** where indices are the name packages. Some aliases are provided as attributes (e.g., available, installed) """ cran_repos = "http://cran.univ-lyon1.fr/" def __init__(self, verbose=True): self.session = RSession() self.logging = Logging(verbose) self.logging.info('Fetching package information') self.update() def _update(self): # local import ? import numpy import pandas # figure out the installed packages first code = """rvar_packages = as.data.frame(installed.packages())""" self.session.run(code) s = self.session.rvar_packages # FIXME. these 4 lines are needed as a hack related to pyper. try: s = s.replace("\n", "") df = eval(s) except: df = s df.set_index('Package', inplace=True) self._packages = df.copy() # Now, fetch was is possible to install from the default cran repo code = """rvar_status=packageStatus(repos="%s/src/contrib")""" code = code % self.cran_repos self.session.run(code) s = self.session.rvar_status # FIXME. try: s = s.replace("\n", "") res = eval(s) except: res = s res['inst'].set_index('Package', inplace=True) res['avail'].set_index('Package', inplace=True) self._status = res def update(self): """If you install/remove packages yourself elsewhere, you may need to call this function to update the package manager""" try: #self.session.reconnect() self._update() except: self.logging.warning("Could not update the packages. Call update() again") def _compat_version(self, version): return version.replace("-", "a") def _get_installed(self): # we do not buffer because packages may be removed manually or from R of # using remove_packages method, .... #self._package_status() return self._status['inst'] installed = property(_get_installed, "returns list of packages installed as a dataframe") def _get_available(self): # we do not buffer because packages may be removed manually or from R of # using remove_packages method, .... #self._package_status() return self._status['avail'] available = property(_get_available, "returns list of packages available as a dataframe") def _get_packages(self): # do not buffer since it may change in many places return self._packages packages = property(_get_packages) def get_package_latest_version(self, package): """Get latest version available of a package""" return self.available['Version'].ix[package] def get_package_version(self, package): """Get version of an install package""" if package not in self.installed.index: self.logging.error("package {0} not installed".format(package)) return self.installed['Version'].ix[package] def biocLite(self, package=None, suppressUpdates=True, verbose=False): """Installs one or more biocLite packages :param package: a package name (string) or list of package names (list of strings) that will be installed from BioConductor. If package is set to None, all packages already installed will be updated. """ if isinstance(package, str): if package not in self.installed.index: biocLite(package, suppressUpdates, verbose=verbose) elif isinstance(package, list): for pkg in package: self.logging.info("Installing %s" % pkg) if self.is_installed(pkg) is False: biocLite(pkg, suppressUpdates, verbose=verbose) else: # trying other cases (e.g., None updates biocLite itself). biocLite(package, suppressUpdates, verbose=verbose) self.update() def _isLocal(self, pkg): if os.path.exists(pkg): return True else: return False def remove(self, package): """Remove a package (or list) from local repository""" rcode ="""remove.packages("%s")""" if isinstance(package, str): package = [package] for pkg in package: if pkg in self.installed.index: self.session(rcode % pkg) else: self.logging.warning("Package not found. Nothing to remove") self.update() def require(self, pkg, version): "Check if a package with given version is available" if pkg not in self.installed.index: self.logging.info("Package %s not installed" % pkg) return False currentVersion = self.packageVersion(pkg) if self._get_version(currentVersion) >= self._get_version(version): return True else: return False def _install_package(self, packageName, dependencies=True): """Installs one or more CRAN packages .. todo:: check if it is already available to prevent renstallation ? """ repos = self.cran_repos # if this is a source file we want to reset the repo if isinstance(packageName, str): packageName = [packageName] for pkg in packageName: if self.is_installed(pkg) is False: self.logging.info("Package not found. Installing %s..." % pkg) install_package(pkg, dependencies=dependencies, repos=repos) else: self.logging.info("Package %s found. " % pkg) install_package(pkg, dependencies=dependencies, repos=repos) self.update() def install(self, pkg, require=None, update=True, reinstall=False): """install a package automatically scanning CRAN and biocLite repos if require is not set and update is True, when a newest version of a package is available, it is installed """ from easydev import to_list pkgs = to_list(pkg) for pkg in pkgs: self._install(pkg, require=require, update=update, reinstall=reinstall) def _install(self, pkg, require=None, update=update, reinstall=False): # LOCAL file if self._isLocal(pkg): # if a local file, we do not want to jump to biocLite or CRAN. Let # us install it directly. We cannot check version yet so we will # overwrite what is already installed self.logging.warning("Installing from source") self._install_package(pkg) return # From CRAN if self.is_installed(pkg): currentVersion = self.get_package_version(pkg) # if not provided, require should be the latest version if require is None and update is True: try: require = self.get_package_latest_version(pkg) except: # a non-cran package (bioclite maybe) pass if require is None: self.logging.info("%s already installed with version %s" % \ (pkg, currentVersion)) return # if require is not none, is it the required version ? if self._get_version(currentVersion) >= self._get_version(require) and reinstall is False: self.logging.info("%s already installed with required version %s" \ % (pkg, currentVersion)) # if so, nothing to do else: # Try updating self.logging.info("Updating") self._install_package(pkg) if require is None: return currentVersion = self.get_package_version(pkg) if self._get_version(currentVersion) < self._get_version(require): self.logging.warning("%s installed but current version (%s) does not fulfill your requirement" % \ (pkg, currentVersion)) elif pkg in self.available.index: self._install_package(pkg) else: # maybe a biocLite package ? # require is ignored. The latest will be installed self.logging.info("Trying to find the package on bioconductor") self.biocLite(pkg) if require is None: return currentVersion = self.get_package_version(pkg) if self._get_version(currentVersion) >= self._get_version(require): self.logging.warning("%s installed but version is %s too small (even after update)" % \ (pkg, currentVersion, require)) def _get_version(self, version): # some pacakge do not use the correct version convention try: return StrictVersion(version) except: try: return StrictVersion(version.replace("-", "a")) except: # snowfall package example was 1.86-6.1 # This becomes 1.86a61 which is not great but not workaround # for now left, right = version.split("-") version = left + "a" + right.replace('.', '') return StrictVersion(version) def is_installed(self, pkg_name): if pkg_name in self.installed.index: return True else: return False
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)
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)