diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 157317d2..6c1a9610 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,3 +90,36 @@ jobs: artifact-name: regression-test-results_musl_gcc # FIXME: We don't run invariance test on alpine yet because of difficulties with RDKit installation. + + + lint: + name: Code Quality + permissions: + # give only read-only access to the contents of the repository + # this is the only permission this job requires, so keep it to the least privilege + # i.e., not to issues, discussions, actions, etc. + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + + - name: Install Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install ruff + + # See documentation on ruff CI integration at + # https://docs.astral.sh/ruff/integrations/#github-actions + - name: Run Ruff format check + run: ruff format --check --output-format=github . + + # Update output format to enable automatic inline annotations. + - name: Run Ruff check + run: ruff check --output-format=github . diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/mol2inchi.py b/INCHI-1-SRC/INCHI_API/demos/python_sample/mol2inchi.py index ac350d7f..e2b1f0e3 100644 --- a/INCHI-1-SRC/INCHI_API/demos/python_sample/mol2inchi.py +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/mol2inchi.py @@ -1,108 +1,166 @@ +import gzip import os import sys -import string -import gzip from optparse import OptionParser + from yapyinchi import * + + # # # ############################################################################## def show_record_results(rst, fout, flog): # - inchi_result, ikey_result, sinchi, slog, smessage, saux, sikey, sxtra1, sxtra2, ierr = rst + ( + inchi_result, + ikey_result, + sinchi, + slog, + smessage, + saux, + sikey, + sxtra1, + sxtra2, + ierr, + ) = rst # if inchi_result != 0: - if (flog!=sys.stdout): - flog.write('\n RECORD %-d\t' % (num) ) - if (inchi_result==1): - flog.write('Warning : ') + if flog != sys.stdout: + flog.write("\n RECORD %-d\t" % (num)) + if inchi_result == 1: + flog.write("Warning : ") else: - flog.write('InChI creation error (%-d) : ' % inchi_result) - flog.write('%-s' % smessage ) - if inchi_result <=1 : + flog.write("InChI creation error (%-d) : " % inchi_result) + flog.write("%-s" % smessage) + if inchi_result <= 1: # no error or just warning - #print('%-s' % sinchi) - fout.write('%-s\n' % sinchi) + # print('%-s' % sinchi) + fout.write("%-s\n" % sinchi) # ... may compute InChIKey - if calc_key==1: - if ikey_result!=0: - if (fl!=sys.stdout): - flog.write('RECORD %-d\t' % (num) ) + if calc_key == 1: + if ikey_result != 0: + if fl != sys.stdout: + flog.write("RECORD %-d\t" % (num)) # special case: reversal of InChI failed # InChIKey is computed, but the warning issued - if (ikey_result==11): - flog.write('Warning: InChI reversal gets mismatching InChI string\n') - fout.write('InChIKey=%-s\n' % sikey) + if ikey_result == 11: + flog.write( + "Warning: InChI reversal gets mismatching InChI string\n" + ) + fout.write("InChIKey=%-s\n" % sikey) else: - flog.write('Key calculation error (%-d)\n' % ikey_result) + flog.write("Key calculation error (%-d)\n" % ikey_result) else: - #print('InChIKey=%-s' % sikey ) - fout.write('InChIKey=%-s\n' % sikey ) - if (calc_xkey==1): - print('XHash1=%-s' % sxtra1 ) - print('XHash1=%-s' % sxtra2 ) - fout.write('XHash1=%-s\n' % sxtra1 ) - fout.write('XHash2=%-s\n' % sxtra2 ) + # print('InChIKey=%-s' % sikey ) + fout.write("InChIKey=%-s\n" % sikey) + if calc_xkey == 1: + print("XHash1=%-s" % sxtra1) + print("XHash1=%-s" % sxtra2) + fout.write("XHash1=%-s\n" % sxtra1) + fout.write("XHash2=%-s\n" % sxtra2) if show_auxinfo: - fout.write('%-s\n' % saux ) + fout.write("%-s\n" % saux) return ############################################################################## def retire(code): - print ("Something went wrong...") - - if ( code==1 ): - print ("Invalid command line") - elif ( code==2 ): + print("Something went wrong...") + + if code == 1: + print("Invalid command line") + elif code == 2: pass - elif( code==3 ): - print ("Unsupported platform") - elif( code==4 ): - print ("Could not access InChI library") - elif( code==5 ): - print ("Could not open input file") - elif( code==6 ): - print ("Could not open output file") - elif( code==7 ): - print ("Could not open log file") + elif code == 3: + print("Unsupported platform") + elif code == 4: + print("Could not access InChI library") + elif code == 5: + print("Could not open input file") + elif code == 6: + print("Could not open output file") + elif code == 7: + print("Could not open log file") else: pass - - sys.exit(code) + sys.exit(code) ############################################################################## def cl_parser(): - parser = OptionParser(usage="""\ + parser = OptionParser( + usage="""\ Usage: %prog [options] - """) - parser.add_option('-i', '--input', - type='string', action='store', - help='name of input SD file (required)') - parser.add_option('-o', '--output', - type='string', action='store', - help='name of output file (default=inchi_out.txt)') - parser.add_option('-l', '--log', - type='string', action='store', - help='name of log file (errors/warnings; default=stdout)') - parser.add_option('-s', '--start_record', - type='int', action='store', - help='starting number of record to be converted') - parser.add_option('-e', '--end_record', - type='int', action='store', - help='number of the last record to be converted') - parser.add_option('-x', '--aux', dest='aux', - help='print auxilary info', default=False, action='store_true') - parser.add_option('-k', '--key', dest='key', - help='calculate InChIKey', default=False, action='store_true') - parser.add_option('-z', '--xtra', dest='xtra', - help='calculate extra hash complementing InChIKey', default=False, action='store_true') - parser.add_option('-p', '--inchi_options', - type='string', action='store', - help='string with InChI options') + """ + ) + parser.add_option( + "-i", + "--input", + type="string", + action="store", + help="name of input SD file (required)", + ) + parser.add_option( + "-o", + "--output", + type="string", + action="store", + help="name of output file (default=inchi_out.txt)", + ) + parser.add_option( + "-l", + "--log", + type="string", + action="store", + help="name of log file (errors/warnings; default=stdout)", + ) + parser.add_option( + "-s", + "--start_record", + type="int", + action="store", + help="starting number of record to be converted", + ) + parser.add_option( + "-e", + "--end_record", + type="int", + action="store", + help="number of the last record to be converted", + ) + parser.add_option( + "-x", + "--aux", + dest="aux", + help="print auxilary info", + default=False, + action="store_true", + ) + parser.add_option( + "-k", + "--key", + dest="key", + help="calculate InChIKey", + default=False, + action="store_true", + ) + parser.add_option( + "-z", + "--xtra", + dest="xtra", + help="calculate extra hash complementing InChIKey", + default=False, + action="store_true", + ) + parser.add_option( + "-p", + "--inchi_options", + type="string", + action="store", + help="string with InChI options", + ) opts, args = parser.parse_args() if not opts.input: parser.print_help() @@ -112,20 +170,20 @@ def cl_parser(): if opts.output: outname = opts.output else: - outname = "inchi_out.txt" + outname = "inchi_out.txt" if opts.log: logname = opts.log else: - logname = "" + logname = "" inchi_options = "" platform = what_platform() - if platform=='Windows': + if platform == "Windows": inchi_options = "/W60" else: - if platform=='Linux': + if platform == "Linux": inchi_options = "-W60" - + if opts.inchi_options: inchi_options = inchi_options + opts.inchi_options if not opts.aux: @@ -148,44 +206,55 @@ def cl_parser(): endr = opts.end_record else: endr = -1 - return [inname, outname, logname, inchi_options, show_auxinfo, calc_key, calc_xkey, startr, endr] + return [ + inname, + outname, + logname, + inchi_options, + show_auxinfo, + calc_key, + calc_xkey, + startr, + endr, + ] + + ############################################################################## -def open_files( inname, outname, logname ): +def open_files(inname, outname, logname): try: - if os.path.splitext(inname)[-1]==".gz": + if os.path.splitext(inname)[-1] == ".gz": f = gzip.open(inname) else: f = open(inname, "rb") except: - return ( None, 5 ) + return (None, 5) # try: - fout = open(outname, 'w') + fout = open(outname, "w") except: - return ( None, 6 ) + return (None, 6) if not fout: - return ( None, 6 ) + return (None, 6) # try: - if ( logname==""): + if logname == "": flog = sys.stderr else: - flog = open(logname, 'w') + flog = open(logname, "w") except: - return ( None, 7 ) + return (None, 7) if not flog: - return ( None, 7 ) - - return ( (f, fout, flog), 0) + return (None, 7) + return ((f, fout, flog), 0) ############################################################################## def banner(): - print ("This Python demo program reads SD file and calls InChI library") - print ("(libinchi.dll/libinchi.so.1) function MakeINCHIFromMolfileText()") - print ("to generate InChI string (also generates InChIKey).") - print ("Note: the example is provided for illustrative purposes only.\n") + print("This Python demo program reads SD file and calls InChI library") + print("(libinchi.dll/libinchi.so.1) function MakeINCHIFromMolfileText()") + print("to generate InChI string (also generates InChIKey).") + print("Note: the example is provided for illustrative purposes only.\n") ############################################################################## @@ -195,17 +264,19 @@ def clean_and_make_list_of_records(text): # Split string by '$$$$' content = text.split("$$$$") # Remove surplus empty record appearing after the last $4 (if any) - if ( len(content[-1])<2 ): # \n + if len(content[-1]) < 2: # \n content = content[:-1] - except: - retire(97) + except: + retire(97) + # Clean up segments: remove surplus head linefeed in records 1-n start # ( it appears as we split at '$$$$' not '$$$$\n' ) def del_head_lf(s): if s: - if s[0]=="\n": + if s[0] == "\n": s = s[1:] return s + content = [content[0]] + [del_head_lf(item) for item in content[1:]] return content @@ -223,47 +294,70 @@ def del_head_lf(s): if not parameters: retire(1) -inname, outname, logname, inchi_options, show_auxinfo, calc_key, calc_xkey, startr, endr = parameters +( + inname, + outname, + logname, + inchi_options, + show_auxinfo, + calc_key, + calc_xkey, + startr, + endr, +) = parameters inchi_api = load_inchi_library("") -if ( not inchi_api ): - retire(4) +if not inchi_api: + retire(4) -print ("\nProcessing " + inname) +print("\nProcessing " + inname) -files, ierr = open_files( inname, outname, logname ) +files, ierr = open_files(inname, outname, logname) if not files: retire(ierr) f, fout, flog = files -print ("\nReading, please wait..") +print("\nReading, please wait..") text = f.read() -print ("..done.") -text = text.decode('utf-8') +print("..done.") +text = text.decode("utf-8") content = clean_and_make_list_of_records(text) num = nerr = 0 for record in content: num = num + 1 - - if (num0): - if (num>endr): + if endr > 0: + if num > endr: break - - flog.write ( "\nRecord # %-ld (%-ld chars)" % (num, len(record)) ) - #print ('{' + record + '}') - - results = inchify_molfile( record, inchi_options, show_auxinfo, calc_key, calc_xkey, inchi_api ) - - show_record_results( results, fout, flog ) - - inchi_result, ikey_result, sinchi, slog, smessage, saux, sikey, sxtra1, sxtra2, ierr = results + + flog.write("\nRecord # %-ld (%-ld chars)" % (num, len(record))) + # print ('{' + record + '}') + + results = inchify_molfile( + record, inchi_options, show_auxinfo, calc_key, calc_xkey, inchi_api + ) + + show_record_results(results, fout, flog) + + ( + inchi_result, + ikey_result, + sinchi, + slog, + smessage, + saux, + sikey, + sxtra1, + sxtra2, + ierr, + ) = results nerr = nerr + ierr -#print ( "\nProcessed %-ld records; had %-ld errors." % (num, nerr)) -flog.write ( "\nCOMPLETED\nProcessed %-ld records with %-ld errors\n" % (num, nerr)) +# print ( "\nProcessed %-ld records; had %-ld errors." % (num, nerr)) +flog.write("\nCOMPLETED\nProcessed %-ld records with %-ld errors\n" % (num, nerr)) diff --git a/INCHI-1-SRC/INCHI_API/demos/python_sample/yapyinchi.py b/INCHI-1-SRC/INCHI_API/demos/python_sample/yapyinchi.py index 472b50b8..f4651b32 100644 --- a/INCHI-1-SRC/INCHI_API/demos/python_sample/yapyinchi.py +++ b/INCHI-1-SRC/INCHI_API/demos/python_sample/yapyinchi.py @@ -1,10 +1,11 @@ -''' +""" Yet another Python (3) to InChI Software Library interface -''' -import os +""" + import sys -import string from ctypes import * + + # # # @@ -15,34 +16,36 @@ # ############################################################################## def what_platform(): - ''' Returns 'Windows' or 'Linux' ''' + """Returns 'Windows' or 'Linux'""" opsys = sys.platform - if (opsys[:3]=='win'): + if opsys[:3] == "win": return "Windows" - elif (opsys[:5]=='linux'): + elif opsys[:5] == "linux": return "Linux" else: return "Unsupported" + + # # # def load_inchi_library(path): # platform = what_platform() - print ("Platform =",platform) + print("Platform =", platform) libname = "" - if (platform=="Windows"): + if platform == "Windows": libname = path + "libinchi.dll" - elif (platform=="Linux"): - libname = path + "libinchi.so.1" #"/usr/lib/libinchi.so.1" + elif platform == "Linux": + libname = path + "libinchi.so.1" # "/usr/lib/libinchi.so.1" else: return None # - print (libname) + print(libname) try: - print ("Loading InChI Software library", libname) + print("Loading InChI Software library", libname) libinchi = cdll.LoadLibrary(libname) - print ("..loaded.") + print("..loaded.") makeinchi = libinchi.MakeINCHIFromMolfileText freeinchi = libinchi.FreeINCHI getikey = libinchi.GetINCHIKeyFromINCHI @@ -57,6 +60,8 @@ def load_inchi_library(path): return None # return (makeinchi, freeinchi, getikey) + + # # # @@ -67,10 +72,14 @@ def load_inchi_library(path): class inchi_Output(Structure): # zero-terminated C-strings allocated by GetINCHI() # to deallocate all of them call FreeINCHI() (see below) - _fields_ = [("szInChI", POINTER(c_char) ), - ("szAuxInfo", POINTER(c_char) ), - ("szMessage", POINTER(c_char) ), - ("szLog", POINTER(c_char) ) ] + _fields_ = [ + ("szInChI", POINTER(c_char)), + ("szAuxInfo", POINTER(c_char)), + ("szMessage", POINTER(c_char)), + ("szLog", POINTER(c_char)), + ] + + # # # @@ -78,34 +87,38 @@ class inchi_Output(Structure): # # # -def calc_inchikey( sinchi, calc_xkey, inchi_api ): +def calc_inchikey(sinchi, calc_xkey, inchi_api): # sikey = sxtra1 = sxtra2 = "" makeinchi, freeinchi, getikey = inchi_api # - c_sinchi = c_char_p(sinchi.encode('utf-8')) + c_sinchi = c_char_p(sinchi.encode("utf-8")) # # Fixed length buffers # - szIKey = create_string_buffer(256) - szXtra1 = create_string_buffer(256) - szXtra2 = create_string_buffer(256) + szIKey = create_string_buffer(256) + szXtra1 = create_string_buffer(256) + szXtra2 = create_string_buffer(256) # ikey_result = getikey(c_sinchi, calc_xkey, calc_xkey, szIKey, szXtra1, szXtra2) - if ikey_result==0 or ikey_result==11: - # 11 is a special case: + if ikey_result == 0 or ikey_result == 11: + # 11 is a special case: # reversal of InChI failed, InChIKey is computed with warning issued - sikey = cast( szIKey, c_char_p).value.decode("utf-8") - if (calc_xkey==1): - sxtra1 = cast( szXtra1, c_char_p).value.decode("utf-8") - sxtra2 = cast( szXtra2, c_char_p).value.decode("utf-8") - # - return ( ikey_result, sikey, sxtra1, sxtra2 ) + sikey = cast(szIKey, c_char_p).value.decode("utf-8") + if calc_xkey == 1: + sxtra1 = cast(szXtra1, c_char_p).value.decode("utf-8") + sxtra2 = cast(szXtra2, c_char_p).value.decode("utf-8") + # + return (ikey_result, sikey, sxtra1, sxtra2) + + ############################################################################## # # # -def inchify_molfile( molfile, inchi_options, show_auxinfo, calc_key, calc_xkey, inchi_api ): +def inchify_molfile( + molfile, inchi_options, show_auxinfo, calc_key, calc_xkey, inchi_api +): # makeinchi, freeinchi, getikey = inchi_api sinchi = slog = smessage = saux = sikey = sxtra1 = sxtra2 = "" @@ -116,24 +129,24 @@ def inchify_molfile( molfile, inchi_options, show_auxinfo, calc_key, calc_xkey, # # Fixed length buffers # - szIKey = create_string_buffer(256) - szXtra1 = create_string_buffer(256) - szXtra2 = create_string_buffer(256) + szIKey = create_string_buffer(256) + szXtra1 = create_string_buffer(256) + szXtra2 = create_string_buffer(256) # # Variable lenght buffers (will be reallocated by libinchi) # - szInChI = create_string_buffer(1) - szAuxInfo = create_string_buffer(1) - szMessage = create_string_buffer(1) - szLog = create_string_buffer(1) - ioutput = inchi_Output( szInChI, szAuxInfo, szMessage, szLog ) + szInChI = create_string_buffer(1) + szAuxInfo = create_string_buffer(1) + szMessage = create_string_buffer(1) + szLog = create_string_buffer(1) + ioutput = inchi_Output(szInChI, szAuxInfo, szMessage, szLog) # - #Make InChI + # Make InChI # - c_molfile = c_char_p(molfile.encode('utf-8')) - c_inchi_options = c_char_p(inchi_options.encode('utf-8')) + c_molfile = c_char_p(molfile.encode("utf-8")) + c_inchi_options = c_char_p(inchi_options.encode("utf-8")) # - inchi_result = makeinchi( c_molfile, c_inchi_options, byref(ioutput) ) + inchi_result = makeinchi(c_molfile, c_inchi_options, byref(ioutput)) # sinchi = cast(ioutput.szInChI, c_char_p).value.decode("utf-8") slog = cast(ioutput.szLog, c_char_p).value.decode("utf-8").strip() @@ -145,41 +158,54 @@ def inchify_molfile( molfile, inchi_options, show_auxinfo, calc_key, calc_xkey, ierr = 1 else: # no error or just warning ==> may compute InChIKey if requested - if calc_key==1: - ikey_result = getikey(ioutput.szInChI, calc_xkey, calc_xkey, szIKey, szXtra1, szXtra2) - if ikey_result==0 or ikey_result==11: - # 11 is a special case: + if calc_key == 1: + ikey_result = getikey( + ioutput.szInChI, calc_xkey, calc_xkey, szIKey, szXtra1, szXtra2 + ) + if ikey_result == 0 or ikey_result == 11: + # 11 is a special case: # reversal of InChI failed, InChIKey is computed with warning issued - sikey = cast( szIKey, c_char_p).value.decode("utf-8") - if (calc_xkey==1): - sxtra1 = cast( szXtra1, c_char_p).value.decode("utf-8") - sxtra2 = cast( szXtra2, c_char_p).value.decode("utf-8") + sikey = cast(szIKey, c_char_p).value.decode("utf-8") + if calc_xkey == 1: + sxtra1 = cast(szXtra1, c_char_p).value.decode("utf-8") + sxtra2 = cast(szXtra2, c_char_p).value.decode("utf-8") # # # - # Deallocate memory reallocated by inchi dll + # Deallocate memory reallocated by inchi dll # # Note the trick with None (Python's NULL) assignnment to pointers - # which have not been actually allocated (related strings are zero-length) - # within C library libinchi. + # which have not been actually allocated (related strings are zero-length) + # within C library libinchi. # We passed Python buffer_strings casted to char *, not real pointers - # so they could not be inited with NULL (as they would be in C)... - # So if say szLog or szMessage were actually not used in libinchi.dll - # and were not allocated, they should be massaged to become NULL at the end, + # so they could not be inited with NULL (as they would be in C)... + # So if say szLog or szMessage were actually not used in libinchi.dll + # and were not allocated, they should be massaged to become NULL at the end, # in order to avoid unnecessary/illegal freeing upon call of freeinchi(). # # -- all above is valid on no error. # - #print (ioutput.szInChI, ioutput.szAuxInfo, ioutput.szLog, ioutput.szMessage) - #print ( inchi_result, "{" + sinchi +"}, {" + slog +"}, {" + smessage +"}, {" + saux + "}" ) + # print (ioutput.szInChI, ioutput.szAuxInfo, ioutput.szLog, ioutput.szMessage) + # print ( inchi_result, "{" + sinchi +"}, {" + slog +"}, {" + smessage +"}, {" + saux + "}" ) # # # - if len(sinchi)==0: - ioutput.szInChI = None # Python's NULL - if len(slog)==0: + if len(sinchi) == 0: + ioutput.szInChI = None # Python's NULL + if len(slog) == 0: ioutput.szLog = None - if len(smessage)==0: - ioutput.szMessage = None + if len(smessage) == 0: + ioutput.szMessage = None freeinchi(byref(ioutput)) - return ( inchi_result, ikey_result, sinchi, slog, smessage, saux, sikey, sxtra1, sxtra2, ierr) + return ( + inchi_result, + ikey_result, + sinchi, + slog, + smessage, + saux, + sikey, + sxtra1, + sxtra2, + ierr, + ) diff --git a/INCHI-1-TEST/src/sdf_pipeline/core.py b/INCHI-1-TEST/src/sdf_pipeline/core.py index 871251d8..ca05bfde 100644 --- a/INCHI-1-TEST/src/sdf_pipeline/core.py +++ b/INCHI-1-TEST/src/sdf_pipeline/core.py @@ -1,8 +1,9 @@ import multiprocessing -from queue import Empty -from typing import Callable, TYPE_CHECKING from collections.abc import Generator from pathlib import Path +from queue import Empty +from typing import TYPE_CHECKING, Callable + from sdf_pipeline import logger, utils if TYPE_CHECKING: diff --git a/INCHI-1-TEST/src/sdf_pipeline/drivers.py b/INCHI-1-TEST/src/sdf_pipeline/drivers.py index 80bd4172..3b4e9e6c 100644 --- a/INCHI-1-TEST/src/sdf_pipeline/drivers.py +++ b/INCHI-1-TEST/src/sdf_pipeline/drivers.py @@ -9,13 +9,15 @@ """ -import sqlite3 import json -from typing import Callable, Any +import sqlite3 +from datetime import datetime from functools import partial from pathlib import Path -from datetime import datetime +from typing import Any, Callable + from pydantic import BaseModel, Field + from sdf_pipeline import core, logger @@ -51,18 +53,18 @@ def regression( number_of_consumer_processes=number_of_consumer_processes, ): molfile_id = consumer_result.molfile_id - assert ( - molfile_id not in processed_molfile_ids - ), f"Molfile ID {molfile_id} has been processed multiple times." + assert molfile_id not in processed_molfile_ids, ( + f"Molfile ID {molfile_id} has been processed multiple times." + ) processed_molfile_ids.add(molfile_id) reference_query = reference_db.execute( "SELECT result FROM results WHERE molfile_id = ?", (molfile_id,), ).fetchone() - assert ( - reference_query - ), f"Couldn't find molfile ID {molfile_id} in reference." + assert reference_query, ( + f"Couldn't find molfile ID {molfile_id} in reference." + ) reference_result = reference_query[0] current_result = json.dumps(consumer_result.result) @@ -95,9 +97,9 @@ def regression( - processed_molfile_ids ) - assert ( - not unprocessed_molfile_ids - ), f"Reference contains molfile IDs that haven't been processed: {unprocessed_molfile_ids}." + assert not unprocessed_molfile_ids, ( + f"Reference contains molfile IDs that haven't been processed: {unprocessed_molfile_ids}." + ) reference_db.close() diff --git a/INCHI-1-TEST/src/sdf_pipeline/utils.py b/INCHI-1-TEST/src/sdf_pipeline/utils.py index 8a0c586a..c4df69af 100644 --- a/INCHI-1-TEST/src/sdf_pipeline/utils.py +++ b/INCHI-1-TEST/src/sdf_pipeline/utils.py @@ -1,12 +1,11 @@ import gzip import random from pathlib import Path -from typing import Generator, Callable +from typing import Callable, Generator try: # Optional import of RDKit. - from rdkit import Chem - from rdkit import RDLogger + from rdkit import Chem, RDLogger # Suppress RDKit console output. RDLogger.DisableLog("rdApp.*") diff --git a/INCHI-1-TEST/tests/test_executable/conftest.py b/INCHI-1-TEST/tests/test_executable/conftest.py index 42288203..473c9a7a 100644 --- a/INCHI-1-TEST/tests/test_executable/conftest.py +++ b/INCHI-1-TEST/tests/test_executable/conftest.py @@ -1,9 +1,10 @@ -import pytest import subprocess +from dataclasses import dataclass +from pathlib import Path from sys import platform from typing import Callable -from pathlib import Path -from dataclasses import dataclass + +import pytest def pytest_addoption(parser): diff --git a/INCHI-1-TEST/tests/test_executable/test_alex_clark_structures.py b/INCHI-1-TEST/tests/test_executable/test_alex_clark_structures.py index bdefa1b6..722983b5 100644 --- a/INCHI-1-TEST/tests/test_executable/test_alex_clark_structures.py +++ b/INCHI-1-TEST/tests/test_executable/test_alex_clark_structures.py @@ -1,8 +1,9 @@ -import pytest +import tempfile from pathlib import Path + +import pytest from helpers import parse_inchi_from_executable_output from sdf_pipeline.utils import read_records_from_gzipped_sdf -import tempfile @pytest.fixture diff --git a/INCHI-1-TEST/tests/test_executable/test_io.py b/INCHI-1-TEST/tests/test_executable/test_io.py index dd38d1ed..74d741fa 100644 --- a/INCHI-1-TEST/tests/test_executable/test_io.py +++ b/INCHI-1-TEST/tests/test_executable/test_io.py @@ -1,10 +1,10 @@ import re -import pytest from pathlib import Path + +import pytest from helpers import parse_inchi_from_executable_output from sdf_pipeline.utils import select_records_from_gzipped_sdf - ID_PATTERN = re.compile(r"> \n(.*?)\n\$\$\$\$", re.DOTALL) diff --git a/INCHI-1-TEST/tests/test_executable/test_organometallics_ccdc.py b/INCHI-1-TEST/tests/test_executable/test_organometallics_ccdc.py index c96119ca..b98a28d6 100644 --- a/INCHI-1-TEST/tests/test_executable/test_organometallics_ccdc.py +++ b/INCHI-1-TEST/tests/test_executable/test_organometallics_ccdc.py @@ -1,8 +1,9 @@ -import pytest +import tempfile from pathlib import Path + +import pytest from helpers import parse_inchi_from_executable_output from sdf_pipeline.utils import read_records_from_gzipped_sdf -import tempfile @pytest.fixture diff --git a/INCHI-1-TEST/tests/test_executable/test_organometallics_pubchem.py b/INCHI-1-TEST/tests/test_executable/test_organometallics_pubchem.py index d29d07c3..1802fe0a 100644 --- a/INCHI-1-TEST/tests/test_executable/test_organometallics_pubchem.py +++ b/INCHI-1-TEST/tests/test_executable/test_organometallics_pubchem.py @@ -1,8 +1,9 @@ -import pytest +import tempfile from pathlib import Path + +import pytest from helpers import parse_inchi_from_executable_output from sdf_pipeline.utils import read_records_from_gzipped_sdf -import tempfile @pytest.fixture diff --git a/INCHI-1-TEST/tests/test_library/config/config_ci.py b/INCHI-1-TEST/tests/test_library/config/config_ci.py index 31d4cde7..651ba0db 100644 --- a/INCHI-1-TEST/tests/test_library/config/config_ci.py +++ b/INCHI-1-TEST/tests/test_library/config/config_ci.py @@ -1,5 +1,6 @@ import re from pathlib import Path + from inchi_tests.config_models import DataConfig diff --git a/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound.py b/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound.py index cdd43596..700a15ca 100644 --- a/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound.py +++ b/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound.py @@ -1,4 +1,5 @@ from pathlib import Path + from inchi_tests.config_models import DataConfig from inchi_tests.utils import get_molfile_id_pubchem diff --git a/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound3d.py b/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound3d.py index b90cbf3f..d4c436c6 100644 --- a/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound3d.py +++ b/INCHI-1-TEST/tests/test_library/config/config_pubchem_compound3d.py @@ -1,4 +1,5 @@ from pathlib import Path + from inchi_tests.config_models import DataConfig from inchi_tests.utils import get_molfile_id_pubchem diff --git a/INCHI-1-TEST/tests/test_library/config/config_pubchem_substance.py b/INCHI-1-TEST/tests/test_library/config/config_pubchem_substance.py index 01d5dd88..487b84e8 100644 --- a/INCHI-1-TEST/tests/test_library/config/config_pubchem_substance.py +++ b/INCHI-1-TEST/tests/test_library/config/config_pubchem_substance.py @@ -1,4 +1,5 @@ from pathlib import Path + from inchi_tests.config_models import DataConfig from inchi_tests.utils import get_molfile_id_pubchem diff --git a/INCHI-1-TEST/tests/test_library/data/pubchem/download.py b/INCHI-1-TEST/tests/test_library/data/pubchem/download.py index 1466afbe..cedee016 100644 --- a/INCHI-1-TEST/tests/test_library/data/pubchem/download.py +++ b/INCHI-1-TEST/tests/test_library/data/pubchem/download.py @@ -1,7 +1,7 @@ -import subprocess import shlex -from .utils import get_dataset_arg, DOWNLOAD_PATHS, PUBCHEM_DIR +import subprocess +from .utils import DOWNLOAD_PATHS, PUBCHEM_DIR, get_dataset_arg if __name__ == "__main__": dataset = get_dataset_arg() diff --git a/INCHI-1-TEST/tests/test_library/data/pubchem/utils.py b/INCHI-1-TEST/tests/test_library/data/pubchem/utils.py index 4f358c10..9a56c6d5 100644 --- a/INCHI-1-TEST/tests/test_library/data/pubchem/utils.py +++ b/INCHI-1-TEST/tests/test_library/data/pubchem/utils.py @@ -1,7 +1,6 @@ import argparse from pathlib import Path - DATASETS = ["compound", "compound3d", "substance"] DOWNLOAD_PATHS = { "compound": "Compound/CURRENT-Full", diff --git a/INCHI-1-TEST/tests/test_library/data/pubchem/validate.py b/INCHI-1-TEST/tests/test_library/data/pubchem/validate.py index 0831be2d..cad69b20 100644 --- a/INCHI-1-TEST/tests/test_library/data/pubchem/validate.py +++ b/INCHI-1-TEST/tests/test_library/data/pubchem/validate.py @@ -1,7 +1,8 @@ import hashlib + from inchi_tests.utils import get_progress -from .utils import get_dataset_arg, PUBCHEM_DIR +from .utils import PUBCHEM_DIR, get_dataset_arg if __name__ == "__main__": dataset = get_dataset_arg() diff --git a/INCHI-1-TEST/tests/test_library/inchi_tests/config_models.py b/INCHI-1-TEST/tests/test_library/inchi_tests/config_models.py index e1ff0d36..36b76e64 100644 --- a/INCHI-1-TEST/tests/test_library/inchi_tests/config_models.py +++ b/INCHI-1-TEST/tests/test_library/inchi_tests/config_models.py @@ -1,5 +1,6 @@ from typing import Callable -from pydantic import BaseModel, FilePath, DirectoryPath + +from pydantic import BaseModel, DirectoryPath, FilePath class DataConfig(BaseModel): diff --git a/INCHI-1-TEST/tests/test_library/inchi_tests/consumers.py b/INCHI-1-TEST/tests/test_library/inchi_tests/consumers.py index b57b2259..aa369d30 100644 --- a/INCHI-1-TEST/tests/test_library/inchi_tests/consumers.py +++ b/INCHI-1-TEST/tests/test_library/inchi_tests/consumers.py @@ -1,8 +1,10 @@ import ctypes import random from typing import Callable + from sdf_pipeline import drivers, utils -from inchi_tests.inchi_api import make_inchi_from_molfile_text, get_inchi_key_from_inchi + +from inchi_tests.inchi_api import get_inchi_key_from_inchi, make_inchi_from_molfile_text def regression_consumer( diff --git a/INCHI-1-TEST/tests/test_library/inchi_tests/inchi_api.py b/INCHI-1-TEST/tests/test_library/inchi_tests/inchi_api.py index 52b0d170..1bfdfe45 100644 --- a/INCHI-1-TEST/tests/test_library/inchi_tests/inchi_api.py +++ b/INCHI-1-TEST/tests/test_library/inchi_tests/inchi_api.py @@ -1,4 +1,5 @@ """`INCHI-1-SRC/INCHI_API/demos/python_sample` stripped to the essentials.""" + import ctypes diff --git a/INCHI-1-TEST/tests/test_library/inchi_tests/parse_log.py b/INCHI-1-TEST/tests/test_library/inchi_tests/parse_log.py index 643bb4e9..1e75acdb 100644 --- a/INCHI-1-TEST/tests/test_library/inchi_tests/parse_log.py +++ b/INCHI-1-TEST/tests/test_library/inchi_tests/parse_log.py @@ -1,11 +1,13 @@ +import importlib import json import sys -import importlib -from pathlib import Path -from difflib import SequenceMatcher from collections import defaultdict +from difflib import SequenceMatcher +from pathlib import Path from typing import Callable, Final, TextIO + from sdf_pipeline.utils import select_records_from_gzipped_sdf + from inchi_tests.utils import get_config_args diff --git a/INCHI-1-TEST/tests/test_library/inchi_tests/run_tests.py b/INCHI-1-TEST/tests/test_library/inchi_tests/run_tests.py index 36420681..fedeb6a2 100644 --- a/INCHI-1-TEST/tests/test_library/inchi_tests/run_tests.py +++ b/INCHI-1-TEST/tests/test_library/inchi_tests/run_tests.py @@ -1,14 +1,16 @@ -import os +import importlib import logging import multiprocessing +import os import sys -import importlib -from functools import partial from datetime import datetime +from functools import partial from pathlib import Path + from sdf_pipeline import drivers -from inchi_tests.utils import get_current_time, get_progress, get_config_args -from inchi_tests.consumers import regression_consumer, invariance_consumer + +from inchi_tests.consumers import invariance_consumer, regression_consumer +from inchi_tests.utils import get_config_args, get_current_time, get_progress def main(test, inchi_lib_path, data_config) -> None: diff --git a/INCHI-1-TEST/tests/test_library/test_multithreading.py b/INCHI-1-TEST/tests/test_library/test_multithreading.py index 0c96ded7..09648a66 100644 --- a/INCHI-1-TEST/tests/test_library/test_multithreading.py +++ b/INCHI-1-TEST/tests/test_library/test_multithreading.py @@ -6,13 +6,13 @@ and 11 means "SIGSEGV", i.e., segfault, see wikipedia.org/wiki/Signal_(IPC)#POSIX_signals. """ -import threading import ctypes +import threading from pathlib import Path + from inchi_tests.inchi_api import make_inchi_from_molfile_text from sdf_pipeline.utils import read_records_from_gzipped_sdf - SDF_PATH = Path("INCHI-1-TEST/tests/test_library/data/ci/inchi.sdf.gz") INCHI_LIB_PATH = ( "CMake_build/full_build/INCHI-1-SRC/INCHI_API/libinchi/src/lib/libinchi.so" diff --git a/INCHI-1-TEST/tests/test_meta/consumers.py b/INCHI-1-TEST/tests/test_meta/consumers.py index 51cd28a2..dffbe22a 100644 --- a/INCHI-1-TEST/tests/test_meta/consumers.py +++ b/INCHI-1-TEST/tests/test_meta/consumers.py @@ -1,5 +1,6 @@ import ctypes from typing import Callable + from sdf_pipeline import drivers diff --git a/INCHI-1-TEST/tests/test_meta/test_drivers.py b/INCHI-1-TEST/tests/test_meta/test_drivers.py index d85860e7..3b847920 100644 --- a/INCHI-1-TEST/tests/test_meta/test_drivers.py +++ b/INCHI-1-TEST/tests/test_meta/test_drivers.py @@ -1,19 +1,19 @@ -import pytest +import json +import logging import re import sqlite3 -import logging -import json +from functools import partial, reduce from operator import add -from functools import reduce from pathlib import Path -from functools import partial -from sdf_pipeline import drivers, core + +import pytest from consumers import ( - regression_consumer, invariance_consumer, raising_consumer, + regression_consumer, segfaulting_consumer, ) +from sdf_pipeline import core, drivers def _get_mcule_id(molfile: str) -> str: diff --git a/INCHI-1-TEST/tests/test_meta/test_performance.py b/INCHI-1-TEST/tests/test_meta/test_performance.py index 4b25b12e..8238ae9c 100644 --- a/INCHI-1-TEST/tests/test_meta/test_performance.py +++ b/INCHI-1-TEST/tests/test_meta/test_performance.py @@ -1,9 +1,10 @@ -import pytest import timeit -from pathlib import Path from functools import partial -from sdf_pipeline import drivers, core +from pathlib import Path + +import pytest from consumers import busy_consumer, regression_consumer +from sdf_pipeline import core, drivers def _get_pubchem_id(molfile: str) -> str: diff --git a/INCHI-1-TEST/tests/test_meta/test_permutation.py b/INCHI-1-TEST/tests/test_meta/test_permutation.py index 922b9ce9..213d5fb4 100644 --- a/INCHI-1-TEST/tests/test_meta/test_permutation.py +++ b/INCHI-1-TEST/tests/test_meta/test_permutation.py @@ -1,7 +1,8 @@ -import pytest import random -from sdf_pipeline.utils import permute_molblock + +import pytest from rdkit import Chem +from sdf_pipeline.utils import permute_molblock molblocks = [ """ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..0af6896d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +# this root-level pyproject.toml will cascade to all +# python code in each section of the project + +[tool.ruff] +extend-include = ["*.ipynb"] + +[tool.ruff.lint] +# See https://docs.astral.sh/ruff/rules +extend-select = [ + "F", # pyflakes + "I", # isort +] +ignore = [ + "F403", # FIXME ignore issues with star imports + "F405", # FIXME ignore issues with star imports + "E722", # FIXME do not use bare excepts +]