From 2a12d6634d5bbbb76afb8c340bf9143accf5607b Mon Sep 17 00:00:00 2001 From: Haibao Tang Date: Thu, 28 Sep 2023 02:39:21 -0700 Subject: [PATCH] Initial implementation of a central logger (#159) (#278) * Add get_logger * start simplifying * revert nopen * don't check py version any more * minor * use logger * update annoreader_base * update idtogos_reader * update find_enrichment * update go_enrichment.py * update obo_optional_attributes * minor formatting --- goatools/anno/annoreader_base.py | 18 +-- goatools/anno/idtogos_reader.py | 8 +- goatools/anno/init/reader_idtogos.py | 23 ++-- goatools/base.py | 154 ++++++++++++---------- goatools/cli/find_enrichment.py | 63 +++++---- goatools/go_enrichment.py | 38 ++---- goatools/godag/obo_optional_attributes.py | 13 +- setup.cfg | 1 + 8 files changed, 163 insertions(+), 155 deletions(-) diff --git a/goatools/anno/annoreader_base.py b/goatools/anno/annoreader_base.py index 0ea19bd..b87cf83 100755 --- a/goatools/anno/annoreader_base.py +++ b/goatools/anno/annoreader_base.py @@ -6,10 +6,11 @@ import collections as cx import logging -from goatools.evidence_codes import EvidenceCodes -from goatools.anno.opts import AnnoOptions -from goatools.godag.consts import NAMESPACE2NS -from goatools.gosubdag.go_tasks import get_go2parents_go2obj +from ..anno.opts import AnnoOptions +from ..base import logger +from ..evidence_codes import EvidenceCodes +from ..godag.consts import NAMESPACE2NS +from ..gosubdag.go_tasks import get_go2parents_go2obj __copyright__ = ( "Copyright (C) 2016-present, DV Klopfenstein, H Tang. All rights reserved." @@ -148,13 +149,12 @@ def _get_1ns_assn(self, namespace_usr): if len(self.namespaces) == 1: nspc = next(iter(self.namespaces)) if namespace_usr is not None and nspc != namespace_usr: - print(f"**WARNING: IGNORING {namespace_usr}; ONLY {nspc} WAS LOADED") + logger.warning("IGNORING %s; ONLY %s WAS LOADED", namespace_usr, nspc) return nspc, self.associations if namespace_usr is None: - print( - "**ERROR get_id2gos: GODAG NOT LOADED. USING: {NSs}".format( - NSs=" ".join(sorted(self.namespaces)) - ) + logger.error( + "get_id2gos: GODAG NOT LOADED. USING: %s", + " ".join(sorted(self.namespaces)), ) return namespace_usr, self.associations diff --git a/goatools/anno/idtogos_reader.py b/goatools/anno/idtogos_reader.py index 8069483..beb7cc5 100755 --- a/goatools/anno/idtogos_reader.py +++ b/goatools/anno/idtogos_reader.py @@ -1,8 +1,10 @@ """Reads a Annotation File in text format with data in id2gos line""" import sys -from goatools.anno.annoreader_base import AnnoReaderBase -from goatools.anno.init.reader_idtogos import InitAssc + +from ..base import logger +from .annoreader_base import AnnoReaderBase +from .init.reader_idtogos import InitAssc __copyright__ = "Copyright (C) 2016-2019, DV Klopfenstein, H Tang. All rights reserved." __author__ = "DV Klopfenstein" @@ -35,7 +37,7 @@ def wr_id2gos(fout_txt, id2gos): def prt_summary_anno2ev(self, prt=sys.stdout): """Print a summary of all Evidence Codes seen in annotations""" - prt.write(f"**NOTE: No evidence codes in associations: {self.filename}\n") + logger.info("No evidence codes in associations: %s", self.filename) # pylint: disable=unused-argument def reduce_annotations(self, associations, options): diff --git a/goatools/anno/init/reader_idtogos.py b/goatools/anno/init/reader_idtogos.py index 822c2a7..13cd0ba 100755 --- a/goatools/anno/init/reader_idtogos.py +++ b/goatools/anno/init/reader_idtogos.py @@ -1,10 +1,11 @@ """Reads a Annotation File in text format with data in id2gos line""" -from sys import stdout import timeit import datetime import collections as cx -from goatools.godag.consts import NAMESPACE2NS + +from ...base import logger +from ...godag.consts import NAMESPACE2NS __copyright__ = ( "Copyright (C) 2016-present, DV Klopfenstein, H Tang. All rights reserved." @@ -39,7 +40,7 @@ def init_associations(self, namespaces): if self.godag is None: if namespaces is not None: # pylint: disable=superfluous-parens - print(f"**WARNING: GODAG NOT LOADED. IGNORING namespaces={namespaces}") + logger.warning("GODAG NOT LOADED. IGNORING namespaces=%s", namespaces) return nts if namespaces == {"BP", "MF", "CC"}: return nts @@ -56,7 +57,7 @@ def _init_dflt(self): nts.append(ntobj(DB_ID=itemid, GO_ID=goid)) return nts - def _init_w_godag(self, prt=stdout): + def _init_w_godag(self): """Get a list of namedtuples, one for each annotation.""" nts = [] ntobj = cx.namedtuple("ntanno", self.flds + ["NS"]) @@ -65,12 +66,12 @@ def _init_w_godag(self, prt=stdout): to_add = set() for goid in gos: if goid not in s_godag: - prt.write(f"**WARNING: {goid} NOT FOUND IN DAG\n") + logger.warning("%s NOT FOUND IN DAG", goid) continue goobj = s_godag[goid] if goobj.is_obsolete: if self.obsolete == "keep": - prt.write(f"**WARNING: {goid} obsolete in DAG, kept\n") + logger.warning("%s obsolete in DAG, kept", goid) to_add.add(goid) elif self.obsolete == "replace": to_replace = set() @@ -79,16 +80,14 @@ def _init_w_godag(self, prt=stdout): if "consider" in goobj.__dict__ and goobj.consider: to_replace |= goobj.consider if to_replace: - prt.write( - f"**WARNING: {goid} obsolete in DAG, replaced by {to_replace}\n" + logger.warning( + "%s obsolete in DAG, replaced by %s", goid, to_replace ) else: - prt.write( - f"**WARNING: {goid} obsolete in DAG, no replacement\n" - ) + logger.warning("%s obsolete in DAG, no replacement", goid) to_add |= to_replace elif self.obsolete == "skip": - prt.write(f"**WARNING: {goid} obsolete in DAG, skipped\n") + logger.warning("%s obsolete in DAG, skipped", goid) else: to_add.add(goid) for goid in to_add: diff --git a/goatools/base.py b/goatools/base.py index d7df852..b774905 100644 --- a/goatools/base.py +++ b/goatools/base.py @@ -1,24 +1,37 @@ """Utilities used in Gene Ontology Enrichment Analyses.""" -# Stolen from brentp +import bz2 +import gzip +import io +import logging import os -from os.path import isfile import os.path as op import sys -import bz2 -import gzip -import urllib +import traceback +import zlib + from ftplib import FTP +from os.path import isfile +from subprocess import PIPE, Popen +from urllib.request import urlopen + import requests +from rich.logging import RichHandler + + +def get_logger(name: str): + """Return a logger with a default ColoredFormatter.""" + logger = logging.getLogger(name) + if logger.hasHandlers(): + logger.handlers.clear() + logger.addHandler(RichHandler()) + logger.propagate = False + logger.setLevel(logging.INFO) + return logger -if sys.version_info[0] < 3: - int_types = (int, long) - urlopen = urllib.urlopen -else: - int_types = (int,) - basestring = str - from urllib.request import urlopen + +logger = get_logger("goatools") def nopen(f, mode="r"): @@ -41,26 +54,30 @@ def nopen(f, mode="r"): >>> files = list(nopen("|ls")) >>> assert 'setup.py\n' in files or b'setup.py\n' in files, files """ - if isinstance(f, int_types): + if isinstance(f, int): return nopen(sys.argv[f], mode) - if not isinstance(f, basestring): + if not isinstance(f, str): return f if f.startswith("|"): # using shell explicitly makes things like process substitution work: # http://stackoverflow.com/questions/7407667/python-subprocess-subshells-and-redirection # use sys.stderr so we dont have to worry about checking it... - p = Popen(f[1:], stdout=PIPE, stdin=PIPE, - stderr=sys.stderr if mode == "r" else PIPE, - shell=True, bufsize=-1, # use system default for buffering - preexec_fn=prefunc, - close_fds=False, executable=os.environ.get('SHELL')) - if sys.version_info[0] > 2: - import io - p.stdout = io.TextIOWrapper(p.stdout) - p.stdin = io.TextIOWrapper(p.stdin) - if mode != "r": - p.stderr = io.TextIOWrapper(p.stderr) + p = Popen( + f[1:], + stdout=PIPE, + stdin=PIPE, + stderr=sys.stderr if mode == "r" else PIPE, + shell=True, + bufsize=-1, # use system default for buffering + preexec_fn=prefunc, + close_fds=False, + executable=os.environ.get("SHELL"), + ) + p.stdout = io.TextIOWrapper(p.stdout) + p.stdin = io.TextIOWrapper(p.stdin) + if mode != "r": + p.stderr = io.TextIOWrapper(p.stderr) if mode and mode[0] == "r": return process_iter(p, f[1:]) @@ -70,33 +87,22 @@ def nopen(f, mode="r"): fh = urlopen(f) if f.endswith(".gz"): return ungzipper(fh) - if sys.version_info[0] < 3: - return fh - import io return io.TextIOWrapper(fh) f = op.expanduser(op.expandvars(f)) if f.endswith((".gz", ".Z", ".z")): fh = gzip.open(f, mode) - if sys.version_info[0] < 3: - return fh - import io return io.TextIOWrapper(fh) elif f.endswith((".bz", ".bz2", ".bzip2")): fh = bz2.BZ2File(f, mode) - if sys.version_info[0] < 3: - return fh - import io return io.TextIOWrapper(fh) - return {"r": sys.stdin, "w": sys.stdout}[mode[0]] if f == "-" \ - else open(f, mode) + return {"r": sys.stdin, "w": sys.stdout}[mode[0]] if f == "-" else open(f, mode) def ungzipper(fh, blocksize=16384): """ work-around to get streaming download of http://.../some.gz """ - import zlib uzip = zlib.decompressobj(16 + zlib.MAX_WBITS) data = uzip.decompress(fh.read(blocksize)).split("\n") @@ -116,27 +122,27 @@ def download_go_basic_obo(obo="go-basic.obo", prt=sys.stdout, loading_bar=True): http = "http://purl.obolibrary.org/obo/go" if "slim" in obo: http = "http://www.geneontology.org/ontology/subsets" - # http = 'http://current.geneontology.org/ontology/subsets' - obo_remote = "{HTTP}/{OBO}".format(HTTP=http, OBO=os.path.basename(obo)) + obo_remote = f"{http}/{op.basename(obo)}" dnld_file(obo_remote, obo, prt, loading_bar) else: if prt: prt.write(" EXISTS: {FILE}\n".format(FILE=obo)) return obo + def download_ncbi_associations(gene2go="gene2go", prt=sys.stdout, loading_bar=True): """Download associations from NCBI, if necessary""" # Download: ftp://ftp.ncbi.nlm.nih.gov/gene/DATA/gene2go.gz gzip_file = "{GENE2GO}.gz".format(GENE2GO=gene2go) if not isfile(gene2go): - file_remote = "ftp://ftp.ncbi.nlm.nih.gov/gene/DATA/{GZ}".format( - GZ=os.path.basename(gzip_file)) + file_remote = f"ftp://ftp.ncbi.nlm.nih.gov/gene/DATA/{op.basename(gzip_file)}" dnld_file(file_remote, gene2go, prt, loading_bar) else: if prt is not None: prt.write(" EXISTS: {FILE}\n".format(FILE=gene2go)) return gene2go + def gunzip(gzip_file, file_gunzip=None): """Unzip .gz file. Return filename of unzipped file.""" if file_gunzip is None: @@ -144,16 +150,22 @@ def gunzip(gzip_file, file_gunzip=None): gzip_open_to(gzip_file, file_gunzip) return file_gunzip -def get_godag(fin_obo="go-basic.obo", prt=sys.stdout, loading_bar=True, optional_attrs=None): + +def get_godag( + fin_obo="go-basic.obo", prt=sys.stdout, loading_bar=True, optional_attrs=None +): """Return GODag object. Initialize, if necessary.""" - from goatools.obo_parser import GODag + from .obo_parser import GODag + download_go_basic_obo(fin_obo, prt, loading_bar) return GODag(fin_obo, optional_attrs, load_obsolete=False, prt=prt) + def dnld_gaf(species_txt, prt=sys.stdout, loading_bar=True): """Download GAF file if necessary.""" return dnld_gafs([species_txt], prt, loading_bar)[0] + def dnld_gafs(species_list, prt=sys.stdout, loading_bar=True): """Download GAF files if necessary.""" # Example GAF files in http://current.geneontology.org/annotations/: @@ -164,43 +176,48 @@ def dnld_gafs(species_list, prt=sys.stdout, loading_bar=True): # There are two filename patterns for gene associations on geneontology.org fin_gafs = [] cwd = os.getcwd() - for species_txt in species_list: # e.g., goa_human mgi fb - gaf_base = '{ABC}.gaf'.format(ABC=species_txt) # goa_human.gaf - gaf_cwd = os.path.join(cwd, gaf_base) # {CWD}/goa_human.gaf + for species_txt in species_list: # e.g., goa_human mgi fb + gaf_base = "{ABC}.gaf".format(ABC=species_txt) # goa_human.gaf + gaf_cwd = os.path.join(cwd, gaf_base) # {CWD}/goa_human.gaf remove_filename = "{HTTP}/{GAF}.gz".format(HTTP=http, GAF=gaf_base) dnld_file(remove_filename, gaf_cwd, prt, loading_bar) fin_gafs.append(gaf_cwd) return fin_gafs + def http_get(url, fout=None): """Download a file from http. Save it in a file named by fout""" - print('requests.get({URL}, stream=True)'.format(URL=url)) + print("requests.get({URL}, stream=True)".format(URL=url)) rsp = requests.get(url, stream=True) if rsp.status_code == 200 and fout is not None: - with open(fout, 'wb') as prt: + with open(fout, "wb") as prt: for chunk in rsp: # .iter_content(chunk_size=128): prt.write(chunk) - print(' WROTE: {F}\n'.format(F=fout)) + print(" WROTE: {F}\n".format(F=fout)) else: print(rsp.status_code, rsp.reason, url) print(rsp.content) return rsp + def ftp_get(fin_src, fout): """Download a file from an ftp server""" - assert fin_src[:6] == 'ftp://', fin_src + assert fin_src[:6] == "ftp://", fin_src dir_full, fin_ftp = os.path.split(fin_src[6:]) - pt0 = dir_full.find('/') + pt0 = dir_full.find("/") assert pt0 != -1, pt0 ftphost = dir_full[:pt0] - chg_dir = dir_full[pt0+1:] - print('FTP RETR {HOST} {DIR} {SRC} -> {DST}'.format( - HOST=ftphost, DIR=chg_dir, SRC=fin_ftp, DST=fout)) + chg_dir = dir_full[pt0 + 1 :] + print( + "FTP RETR {HOST} {DIR} {SRC} -> {DST}".format( + HOST=ftphost, DIR=chg_dir, SRC=fin_ftp, DST=fout + ) + ) ftp = FTP(ftphost) # connect to host, default port ftp.ncbi.nlm.nih.gov - ftp.login() # user anonymous, passwd anonymous@ - ftp.cwd(chg_dir) # change into "debian" directory gene/DATA - cmd = 'RETR {F}'.format(F=fin_ftp) # gene2go.gz - ftp.retrbinary(cmd, open(fout, 'wb').write) # /usr/home/gene2go.gz + ftp.login() # user anonymous, passwd anonymous@ + ftp.cwd(chg_dir) # change into "debian" directory gene/DATA + cmd = "RETR {F}".format(F=fin_ftp) # gene2go.gz + ftp.retrbinary(cmd, open(fout, "wb").write) # /usr/home/gene2go.gz ftp.quit() @@ -208,14 +225,14 @@ def dnld_file(src_ftp, dst_file, prt=sys.stdout, loading_bar=True): """Download specified file if necessary.""" if isfile(dst_file): return - do_gunzip = src_ftp[-3:] == '.gz' and dst_file[-3:] != '.gz' + do_gunzip = src_ftp[-3:] == ".gz" and dst_file[-3:] != ".gz" dst_gz = "{DST}.gz".format(DST=dst_file) if do_gunzip else dst_file # Write to stderr, not stdout so this message will be seen when running nosetests cmd_msg = "get({SRC} out={DST})\n".format(SRC=src_ftp, DST=dst_gz) try: - print('$ get {SRC}'.format(SRC=src_ftp)) + print("$ get {SRC}".format(SRC=src_ftp)) #### wget.download(src_ftp, out=dst_gz, bar=loading_bar) - if src_ftp[:4] == 'http': + if src_ftp[:4] == "http": http_get(src_ftp, dst_gz) else: ftp_get(src_ftp, dst_gz) @@ -224,24 +241,23 @@ def dnld_file(src_ftp, dst_file, prt=sys.stdout, loading_bar=True): prt.write("$ gunzip {FILE}\n".format(FILE=dst_gz)) gzip_open_to(dst_gz, dst_file) except IOError as errmsg: - import traceback traceback.print_exc() - sys.stderr.write("**FATAL cmd: {CMD}".format(CMD=cmd_msg)) - sys.stderr.write("**FATAL msg: {ERR}".format(ERR=str(errmsg))) + logger.fatal("cmd: %s", cmd_msg) + logger.fatal("msg: %s", str(errmsg)) sys.exit(1) + def gzip_open_to(fin_gz, fout): """Unzip a file.gz file.""" try: - with gzip.open(fin_gz, 'rb') as zstrm: - with open(fout, 'wb') as ostrm: + with gzip.open(fin_gz, "rb") as zstrm: + with open(fout, "wb") as ostrm: ostrm.write(zstrm.read()) # pylint: disable=broad-except except Exception as errmsg: - print("**ERROR: COULD NOT GUNZIP({G}) TO FILE({F})".format(G=fin_gz, F=fout)) - import traceback + logger.error("COULD NOT GUNZIP(%s) TO FILE(%s)", fin_gz, fout) traceback.print_exc() - sys.stderr.write("**FATAL msg: {ERR}".format(ERR=str(errmsg))) + logger.fatal("msg: %s", str(errmsg)) sys.exit(1) os.remove(fin_gz) diff --git a/goatools/cli/find_enrichment.py b/goatools/cli/find_enrichment.py index a1ead76..eaab821 100644 --- a/goatools/cli/find_enrichment.py +++ b/goatools/cli/find_enrichment.py @@ -23,33 +23,31 @@ import re import argparse -from goatools.evidence_codes import EvidenceCodes - -from goatools.obo_parser import GODag -from goatools.goea.go_enrichment_ns import GOEnrichmentStudyNS -from goatools.multiple_testing import Methods -from goatools.pvalcalc import FisherFactory -from goatools.rpt.goea_nt_xfrm import MgrNtGOEAs -from goatools.rpt.prtfmt import PrtFmt -from goatools.semantic import TermCounts -from goatools.wr_tbl import prt_tsv_sections -from goatools.godag.consts import RELATIONSHIP_SET -from goatools.godag.consts import RELATIONSHIP_LIST -from goatools.godag.consts import chk_relationships -from goatools.godag.prtfncs import GoeaPrintFunctions -from goatools.anno.factory import get_anno_desc -from goatools.anno.factory import get_objanno -from goatools.cli.gos_get import GetGOs - -from goatools.gosubdag.gosubdag import GoSubDag -from goatools.grouper.read_goids import read_sections -from goatools.grouper.grprdflts import GrouperDflts -from goatools.grouper.hdrgos import HdrgosSections -from goatools.grouper.grprobj import Grouper -from goatools.grouper.sorter import Sorter -from goatools.grouper.aart_geneproducts_all import AArtGeneProductSetsAll -from goatools.grouper.wr_sections import WrSectionsTxt -from goatools.grouper.wrxlsx import WrXlsxSortedGos + +from ..anno.factory import get_anno_desc, get_objanno +from ..base import logger +from ..evidence_codes import EvidenceCodes +from ..godag.consts import RELATIONSHIP_LIST, RELATIONSHIP_SET, chk_relationships +from ..godag.prtfncs import GoeaPrintFunctions +from ..goea.go_enrichment_ns import GOEnrichmentStudyNS +from ..gosubdag.gosubdag import GoSubDag +from ..grouper.aart_geneproducts_all import AArtGeneProductSetsAll +from ..grouper.grprdflts import GrouperDflts +from ..grouper.grprobj import Grouper +from ..grouper.hdrgos import HdrgosSections +from ..grouper.read_goids import read_sections +from ..grouper.sorter import Sorter +from ..grouper.wr_sections import WrSectionsTxt +from ..grouper.wrxlsx import WrXlsxSortedGos +from ..multiple_testing import Methods +from ..obo_parser import GODag +from ..pvalcalc import FisherFactory +from ..rpt.goea_nt_xfrm import MgrNtGOEAs +from ..rpt.prtfmt import PrtFmt +from ..semantic import TermCounts +from ..wr_tbl import prt_tsv_sections + +from .gos_get import GetGOs OBJPRTRES = GoeaPrintFunctions() @@ -511,13 +509,14 @@ def chk_genes(self, study, pop, ntsassoc=None): assc_ids = set(nt.DB_ID for nt in ntsassoc) if pop.isdisjoint(assc_ids): if self.objanno.name == "gene2go": - err = ( - "**FATAL: NO POPULATION ITEMS SEEN IN THE NCBI gene2go ANNOTATIONS " - "FOR taxid({T}). TRY: --taxid=" + logger.fatal( + "NO POPULATION ITEMS SEEN IN THE NCBI gene2go ANNOTATIONS " + "FOR taxid(%s). TRY: --taxid=", + next(iter(self.objanno.taxid2asscs.keys())), ) - exit(err.format(T=next(iter(self.objanno.taxid2asscs.keys())))) else: - exit("**FATAL: NO POPULATION ITEMS SEEN IN THE ANNOTATIONS") + logger.fatal("NO POPULATION ITEMS SEEN IN THE ANNOTATIONS") + exit() def get_results_sig(self): """Get significant results.""" diff --git a/goatools/go_enrichment.py b/goatools/go_enrichment.py index 458f385..34ae779 100755 --- a/goatools/go_enrichment.py +++ b/goatools/go_enrichment.py @@ -9,33 +9,22 @@ (including Bonferroni, Holm, Sidak, and false discovery rate) """ -from __future__ import print_function -from __future__ import absolute_import - - __copyright__ = "Copyright (C) 2010-2019, H Tang et al., All rights reserved." __author__ = "various" import sys import collections as cx -from goatools.multiple_testing import Methods -from goatools.multiple_testing import Bonferroni -from goatools.multiple_testing import Sidak -from goatools.multiple_testing import HolmBonferroni -from goatools.multiple_testing import FDR -from goatools.multiple_testing import calc_qval -from goatools.anno.update_association import update_association -from goatools.anno.update_association import remove_assc_goids - -# BROAD from goatools.anno.update_association import get_goids_to_remove -from goatools.ratio import get_terms, count_terms, is_ratio_different import goatools.wr_tbl as RPT -from goatools.pvalcalc import FisherFactory -from goatools.godag.prtfncs import GoeaPrintFunctions -from goatools.rpt.goea_nt_xfrm import MgrNtGOEAs -from goatools.rpt.prtfmt import PrtFmt +from .anno.update_association import remove_assc_goids, update_association +from .base import logger +from .godag.prtfncs import GoeaPrintFunctions +from .multiple_testing import Bonferroni, FDR, HolmBonferroni, Methods, Sidak, calc_qval +from .pvalcalc import FisherFactory +from .ratio import get_terms, count_terms, is_ratio_different +from .rpt.goea_nt_xfrm import MgrNtGOEAs +from .rpt.prtfmt import PrtFmt class GOEnrichmentRecord(object): @@ -323,12 +312,11 @@ def __init__( def _remove_assc_goids(self, assoc, broad_goids): """Remove broad GO IDs""" ret = remove_assc_goids(assoc, broad_goids) - if self.log: - self.log.write( - "**NOTE: {N} of {M} Broad GO IDs remove from association\n".format( - N=len(ret["goids_removed"]), M=len(broad_goids) - ) - ) + logger.info( + "%d of %d Broad GO IDs remove from association", + len(ret["goids_removed"]), + len(broad_goids), + ) return ret["assoc_reduced"] def run_study(self, study, **kws): diff --git a/goatools/godag/obo_optional_attributes.py b/goatools/godag/obo_optional_attributes.py index bc5ba74..b6b53f7 100644 --- a/goatools/godag/obo_optional_attributes.py +++ b/goatools/godag/obo_optional_attributes.py @@ -8,6 +8,8 @@ import re import collections as cx +from ..base import logger + class OboOptionalAttrs: """Manage optional GO-DAG attributes.""" @@ -258,13 +260,14 @@ def get_optional_attrs(optional_attrs, attrs_opt): try: iter(optional_attrs) except TypeError: - pat = ( - "**FATAL: GODag's optional_attrs MUST BE A SET CONTAINING ANY OF: {ATTRS}\n" + logger.fatal( + "GODag's optional_attrs MUST BE A SET CONTAINING ANY OF: %s\n" " " - "**FATAL: BAD GODag optional_attrs({BADVAL})" + "BAD GODag optional_attrs(%s)", + " ".join(attrs_opt), + optional_attrs, ) - msg = pat.format(ATTRS=" ".join(attrs_opt), BADVAL=optional_attrs) - raise TypeError(msg) + exit() getnm = lambda aopt: aopt if aopt != "defn" else "def" opts = None if isinstance(optional_attrs, str) and optional_attrs in attrs_opt: diff --git a/setup.cfg b/setup.cfg index 8eb731d..9efe894 100644 --- a/setup.cfg +++ b/setup.cfg @@ -46,6 +46,7 @@ install_requires = docopt pydot requests + rich include_package_data = True scripts = scripts/wr_sections.py