From 80ac009c86e46fc81d67d08e5e983e0128ee112b Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:38:09 -0400 Subject: [PATCH 01/13] PEP8 --- tools/src/kicadbomtovendor.py | 304 +++++++++++++++++----------------- 1 file changed, 153 insertions(+), 151 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 8570022..41e3bcd 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -7,256 +7,258 @@ # # Output is a tab-delimited file. # -# # -import re -import sys +# import argparse +import re import xml.etree.ElementTree + + # # Main program # -def main() : +def main(): # Get command line arguments parser = argparse.ArgumentParser(description="KiCAD XML bill of materials converter to .CSV format") parser.add_argument("--verbose", default=False, action="store_true", help="set verbose mode") parser.add_argument("--split", metavar="COLUMN", help="Split BOM into multiple output files based on this column") parser.add_argument("--select", action="append", metavar="COLUMN=VALUE", help="COLUMN=VALUE to select") parser.add_argument("files", action="append", help="XML file") - args = parser.parse_args() # parse command line + args = parser.parse_args() # parse command line print(args) verbose = args.verbose - selects = {} # dict of (k, set(v)) - splitcol = args.split # split on this arg - if splitcol is not None : - splitcol = splitcol.upper() # upper case column name - print('Split on column "%s".' % (splitcol,)) # split on this column + selects = {} # dict of (k, set(v)) + splitcol = args.split # split on this arg + if splitcol is not None: + splitcol = splitcol.upper() # upper case column name + print('Split on column "%s".' % (splitcol,)) # split on this column # Accumulate select keys - if args.select is not None : - for select in args.select : - parts = select.split("=") # split at "=" - if len(parts) != 2 : # must be COLUMN=VALUE + if args.select is not None: + for select in args.select: + parts = select.split("=") # split at "=" + if len(parts) != 2: # must be COLUMN=VALUE print('"--select COLUMN=VALUE" required.') parser.print_help() exit(1) k = parts[0].strip().upper() - v = parts[1].strip().upper() # save selects as upper case - if k not in selects : - selects[k] = set() # need set for this key - selects[k].add(v) # add value to set for this key + v = parts[1].strip().upper() # save selects as upper case + if k not in selects: + selects[k] = set() # need set for this key + selects[k].add(v) # add value to set for this key print("Selection rules: ") - for (k, sset) in selects.items() : + for (k, sset) in selects.items(): print(' Select if %s is in %s.' % (k, list(sset))) # Process all files on command line - for infname in args.files : + for infname in args.files: print(infname) - try : - (outfname, suffix) = infname.rsplit(".",1) # remove suffix - except ValueError : + try: + (outfname, suffix) = infname.rsplit(".", 1) # remove suffix + except ValueError: print("Input must be a .xml file.") parser.print_help() exit(1) - if suffix.lower() != "xml" : # must be XML + if suffix.lower() != "xml": # must be XML print("Input must be a .xml file.") parser.print_help() exit(1) cv = Converter(selects, splitcol, verbose) - cv.convert(infname, outfname) - -# + cv.convert(infname, outfname) + + # + + # converter -- converts file -# -class Converter(object) : - FIXEDFIELDS = ["REF","FOOTPRINT","VALUE", "QUANTITY"] # fields we always want - NOTDIFFERENTPART = set(["REF"]) # still same part if this doesn't match - OUTPUTSUFFIX = ".csv" # file output suffix +# +class Converter(object): + FIXEDFIELDS = ["REF", "FOOTPRINT", "VALUE", "QUANTITY"] # fields we always want + NOTDIFFERENTPART = set(["REF"]) # still same part if this doesn't match + OUTPUTSUFFIX = ".csv" # file output suffix + + def __init__(self, selects, splitcol, verbose=False): + self.selects = selects # selection list + self.splitcol = splitcol # split output into multiple files based on this + self.verbose = verbose # debug info + self.tree = None # no tree yet + self.fieldset = set(self.FIXEDFIELDS) # set of all field names found + self.fieldlist = None # list of column headings - def __init__(self, selects, splitcol, verbose = False) : - self.selects = selects # selection list - self.splitcol = splitcol # split output into multiple files based on this - self.verbose = verbose # debug info - self.tree = None # no tree yet - self.fieldset = set(self.FIXEDFIELDS) # set of all field names found - self.fieldlist = None # list of column headings - - def cleanstr(self, s) : + def cleanstr(self, s): """ Clean up a string to avoid CSV format problems """ - return(re.sub(r'\s+|,',' ', s).strip()) # remove tabs, newlines, and commas - - def handlecomp1(self, comp) : + return (re.sub(r'\s+|,', ' ', s).strip()) # remove tabs, newlines, and commas + + def handlecomp1(self, comp): """ Handle one component entry, pass 1 - find all field names """ - for field in comp.iter("field") : # for all "field" items - name = field.get("name") # get all "name" names + for field in comp.iter("field"): # for all "field" items + name = field.get("name") # get all "name" names name = name.upper() self.fieldset.add(name) - - def selectitem(self, fieldvals) : + + def selectitem(self, fieldvals): """ Given a set of field values, decide if we want to keep this one. - + All SELECTs must be true. """ - for k in self.selects : # for all select rules - if k not in fieldvals : # if not found, fail, unless missing allowed - if "" not in self.selects[k] : # if "--select FOO=" allow - return(False) # fails - if fieldvals[k].upper() not in self.selects[k] :# if no find - return(False) - return(True) # no select succeeded - - def handlecomp2(self, comp) : + for k in self.selects: # for all select rules + if k not in fieldvals: # if not found, fail, unless missing allowed + if "" not in self.selects[k]: # if "--select FOO=" allow + return (False) # fails + if fieldvals[k].upper() not in self.selects[k]: # if no find + return (False) + return (True) # no select succeeded + + def handlecomp2(self, comp): """ Handle one component entry, pass 2 - Collect and output fields """ fieldvals = dict() - try : + try: ref = comp.attrib.get("ref") - footprint = getattr(comp.find("footprint"),"text","") + footprint = getattr(comp.find("footprint"), "text", "") value = comp.find("value").text - except (ValueError, AttributeError) as message : + except (ValueError, AttributeError) as message: print("Required field missing from %s" % (comp.attrib)) exit(1) fieldvals["REF"] = ref fieldvals["FOOTPRINT"] = footprint fieldvals["VALUE"] = value - fieldvals["QUANTITY"] = "1" # one item at this point - if self.verbose : + fieldvals["QUANTITY"] = "1" # one item at this point + if self.verbose: print("%s" % (fieldvals,)) - # Get user-defined fields - for field in comp.iter("field") : + # Get user-defined fields + for field in comp.iter("field"): name = field.get("name") name = name.upper() fieldvals[name] = field.text - if self.verbose : + if self.verbose: print("%s" % (fieldvals,)) - if self.selectitem(fieldvals) : # if we want this item - return(self.assembleline(fieldvals)) # return list of fields - return(None) - - def assembleline(self, fieldvals) : + if self.selectitem(fieldvals): # if we want this item + return (self.assembleline(fieldvals)) # return list of fields + return (None) + + def assembleline(self, fieldvals): """ Assemble output fields into a list """ - s = '' # empty line - outfields = [] # output fields - for fname in self.fieldlist : # for all fields - if fname in fieldvals : - val = fieldvals[fname] # get value - else : - val = "" # empty string otherwise - outfields.append(self.cleanstr(val)) # remove things not desirable in CSV files - return(outfields) # ordered list of fields - - def issamepart(self, rowa, rowb) : + s = '' # empty line + outfields = [] # output fields + for fname in self.fieldlist: # for all fields + if fname in fieldvals: + val = fieldvals[fname] # get value + else: + val = "" # empty string otherwise + outfields.append(self.cleanstr(val)) # remove things not desirable in CSV files + return (outfields) # ordered list of fields + + def issamepart(self, rowa, rowb): """ - True if both lists represent the same part + True if both lists represent the same part """ - if rowa is None or rowb is None : - return(False) # None doesn't match - for i in range(len(self.fieldlist)) : # across 3 lists in sync - if self.fieldlist[i] in self.NOTDIFFERENTPART : # some fields, such as REF, don't mean a new part + if rowa is None or rowb is None: + return (False) # None doesn't match + for i in range(len(self.fieldlist)): # across 3 lists in sync + if self.fieldlist[i] in self.NOTDIFFERENTPART: # some fields, such as REF, don't mean a new part continue - if rowa[i] != rowb[i] : - return(False) - return(True) # all important fields matched - - def additems(self, rows) : + if rowa[i] != rowb[i]: + return (False) + return (True) # all important fields matched + + def additems(self, rows): """ Combine multiple instances of same part, adding to quantity """ - quanpos = self.fieldlist.index("QUANTITY") # get index of quantity column - refpos = self.fieldlist.index("REF") # get index of ref column + quanpos = self.fieldlist.index("QUANTITY") # get index of quantity column + refpos = self.fieldlist.index("REF") # get index of ref column outrows = [] prevrow = None quan = 0 - refs = [] # concat REF fields - for row in rows : # for all rows - if not self.issamepart(prevrow, row) : # if control break - if prevrow is not None : - prevrow[quanpos] = str(quan) # set quantity - prevrow[refpos] = " ".join(refs) # set list of refs - outrows.append(prevrow) # output stored row + refs = [] # concat REF fields + for row in rows: # for all rows + if not self.issamepart(prevrow, row): # if control break + if prevrow is not None: + prevrow[quanpos] = str(quan) # set quantity + prevrow[refpos] = " ".join(refs) # set list of refs + outrows.append(prevrow) # output stored row quan = 0 - refs = [] - prevrow = row # process new row - quan = quan + int(row[quanpos]) # add this quantity - refs.append(row[refpos]) # add to list of refs + refs = [] + prevrow = row # process new row + quan = quan + int(row[quanpos]) # add this quantity + refs.append(row[refpos]) # add to list of refs + + if prevrow is not None: # end of file + prevrow[quanpos] = str(quan) # do last item + prevrow[refpos] = " ".join(refs) # set list of refs + outrows.append(prevrow) # output stored row + return (outrows) # return summed rows - if prevrow is not None : # end of file - prevrow[quanpos] = str(quan) # do last item - prevrow[refpos] = " ".join(refs) # set list of refs - outrows.append(prevrow) # output stored row - return(outrows) # return summed rows - - def outputfile(self, rows, outfname) : + def outputfile(self, rows, outfname): """ Output one file containing given rows """ - heading = ",".join(self.fieldlist) # heading line - outf = open(outfname,"w") # open output file - outf.write(heading + "\n") # heading line - for row in rows : # output all rows - s = ",".join(row) - outf.write(s + "\n") # print to file - outf.close() # done - print('Created file "%s".' % (outfname),) # report - - def splitrows(self, splitcol, rows) : + heading = ",".join(self.fieldlist) # heading line + outf = open(outfname, "w") # open output file + outf.write(heading + "\n") # heading line + for row in rows: # output all rows + s = ",".join(row) + outf.write(s + "\n") # print to file + outf.close() # done + print('Created file "%s".' % (outfname), ) # report + + def splitrows(self, splitcol, rows): """ Split rows into multiple lists of rows based on values in selected column - + Usually used to break up a BOM by vendor """ - rowsets = {} # col value, [rows] - splitpos = self.fieldlist.index(splitcol) # get index of split column - if splitpos is None : # must find + rowsets = {} # col value, [rows] + splitpos = self.fieldlist.index(splitcol) # get index of split column + if splitpos is None: # must find print('Column "%s" from --split not found in BOM' % (splitcol,)) exit(1) - for row in rows : - k = row[splitpos] # get split column value - if k is None or k == "" : - k = "NONE" # use NONE if no value available - k = k.upper() # always in upper case - if k not in rowsets : - rowsets[k] = [] # add new output file - rowsets[k].append(row) # add this row to appropriate output file - return(rowsets) # returns col value, [rows] - - def convert(self, infname, outfname) : + for row in rows: + k = row[splitpos] # get split column value + if k is None or k == "": + k = "NONE" # use NONE if no value available + k = k.upper() # always in upper case + if k not in rowsets: + rowsets[k] = [] # add new output file + rowsets[k].append(row) # add this row to appropriate output file + return (rowsets) # returns col value, [rows] + + def convert(self, infname, outfname): """ Convert one file """ self.tree = xml.etree.ElementTree.parse(infname) - root = self.tree.getroot() # root element + root = self.tree.getroot() # root element # Pass 1 - inventory fields - for comp in root.iter("comp") : + for comp in root.iter("comp"): self.handlecomp1(comp) - if self.verbose : + if self.verbose: print("Field names found: %s" % (self.fieldset)) self.fieldlist = list(self.fieldset) - self.fieldlist.sort() # sort in place + self.fieldlist.sort() # sort in place # Pass 2 - accumulate rows rows = [] - for comp in root.iter("comp") : + for comp in root.iter("comp"): row = self.handlecomp2(comp) - if row is not None : + if row is not None: rows.append(row) # Pass 3 - combine rows of same items rows.sort() - rows = self.additems(rows) # combine items + rows = self.additems(rows) # combine items # Pass 4 - output rows - if self.splitcol is None : # all in one file - self.outputfile(outfname + self.OUTPUTSUFFIX) # one file - else : # one file per column value + if self.splitcol is None: # all in one file + self.outputfile(outfname + self.OUTPUTSUFFIX) # one file + else: # one file per column value rowsets = self.splitrows(self.splitcol, rows) - for (colval, rows) in rowsets.items() : # output multiple files + for (colval, rows) in rowsets.items(): # output multiple files self.outputfile(rows, outfname + "-" + colval + self.OUTPUTSUFFIX) - -if __name__ == "__main__" : - main() +if __name__ == "__main__": + main() From fd54bad21014cfde862a7e28e638f9239b0f592e Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:39:28 -0400 Subject: [PATCH 02/13] Convert to .format() strings --- tools/src/kicadbomtovendor.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 41e3bcd..99d384d 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -31,7 +31,7 @@ def main(): splitcol = args.split # split on this arg if splitcol is not None: splitcol = splitcol.upper() # upper case column name - print('Split on column "%s".' % (splitcol,)) # split on this column + print('Split on column "{}".'.format(splitcol)) # split on this column # Accumulate select keys if args.select is not None: for select in args.select: @@ -47,7 +47,7 @@ def main(): selects[k].add(v) # add value to set for this key print("Selection rules: ") for (k, sset) in selects.items(): - print(' Select if %s is in %s.' % (k, list(sset))) + print(' Select if {} is in {}.'.format(k, list(sset))) # Process all files on command line for infname in args.files: print(infname) @@ -121,21 +121,21 @@ def handlecomp2(self, comp): footprint = getattr(comp.find("footprint"), "text", "") value = comp.find("value").text except (ValueError, AttributeError) as message: - print("Required field missing from %s" % (comp.attrib)) + print("Required field missing from {}".format(comp.attrib)) exit(1) fieldvals["REF"] = ref fieldvals["FOOTPRINT"] = footprint fieldvals["VALUE"] = value fieldvals["QUANTITY"] = "1" # one item at this point if self.verbose: - print("%s" % (fieldvals,)) + print("{}".format(fieldvals)) # Get user-defined fields for field in comp.iter("field"): name = field.get("name") name = name.upper() fieldvals[name] = field.text if self.verbose: - print("%s" % (fieldvals,)) + print("{}".format(fieldvals)) if self.selectitem(fieldvals): # if we want this item return (self.assembleline(fieldvals)) # return list of fields return (None) @@ -206,7 +206,7 @@ def outputfile(self, rows, outfname): s = ",".join(row) outf.write(s + "\n") # print to file outf.close() # done - print('Created file "%s".' % (outfname), ) # report + print('Created file "{}".'.format(outfname)) # report def splitrows(self, splitcol, rows): """ @@ -217,7 +217,7 @@ def splitrows(self, splitcol, rows): rowsets = {} # col value, [rows] splitpos = self.fieldlist.index(splitcol) # get index of split column if splitpos is None: # must find - print('Column "%s" from --split not found in BOM' % (splitcol,)) + print('Column "{}" from --split not found in BOM'.format(splitcol)) exit(1) for row in rows: k = row[splitpos] # get split column value @@ -239,7 +239,7 @@ def convert(self, infname, outfname): for comp in root.iter("comp"): self.handlecomp1(comp) if self.verbose: - print("Field names found: %s" % (self.fieldset)) + print("Field names found: {}".format(self.fieldset)) self.fieldlist = list(self.fieldset) self.fieldlist.sort() # sort in place # Pass 2 - accumulate rows From 23408a1612fbb37ccfb1b7b501514a3f76a103a6 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:39:57 -0400 Subject: [PATCH 03/13] Remove redundant parens --- tools/src/kicadbomtovendor.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 99d384d..47e3760 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -86,7 +86,7 @@ def cleanstr(self, s): """ Clean up a string to avoid CSV format problems """ - return (re.sub(r'\s+|,', ' ', s).strip()) # remove tabs, newlines, and commas + return re.sub(r'\s+|,', ' ', s).strip() # remove tabs, newlines, and commas def handlecomp1(self, comp): """ @@ -106,10 +106,10 @@ def selectitem(self, fieldvals): for k in self.selects: # for all select rules if k not in fieldvals: # if not found, fail, unless missing allowed if "" not in self.selects[k]: # if "--select FOO=" allow - return (False) # fails + return False # fails if fieldvals[k].upper() not in self.selects[k]: # if no find - return (False) - return (True) # no select succeeded + return False + return True # no select succeeded def handlecomp2(self, comp): """ @@ -137,8 +137,8 @@ def handlecomp2(self, comp): if self.verbose: print("{}".format(fieldvals)) if self.selectitem(fieldvals): # if we want this item - return (self.assembleline(fieldvals)) # return list of fields - return (None) + return self.assembleline(fieldvals) # return list of fields + return None def assembleline(self, fieldvals): """ @@ -152,20 +152,20 @@ def assembleline(self, fieldvals): else: val = "" # empty string otherwise outfields.append(self.cleanstr(val)) # remove things not desirable in CSV files - return (outfields) # ordered list of fields + return outfields # ordered list of fields def issamepart(self, rowa, rowb): """ True if both lists represent the same part """ if rowa is None or rowb is None: - return (False) # None doesn't match + return False # None doesn't match for i in range(len(self.fieldlist)): # across 3 lists in sync if self.fieldlist[i] in self.NOTDIFFERENTPART: # some fields, such as REF, don't mean a new part continue if rowa[i] != rowb[i]: - return (False) - return (True) # all important fields matched + return False + return True # all important fields matched def additems(self, rows): """ @@ -193,7 +193,7 @@ def additems(self, rows): prevrow[quanpos] = str(quan) # do last item prevrow[refpos] = " ".join(refs) # set list of refs outrows.append(prevrow) # output stored row - return (outrows) # return summed rows + return outrows # return summed rows def outputfile(self, rows, outfname): """ @@ -227,7 +227,7 @@ def splitrows(self, splitcol, rows): if k not in rowsets: rowsets[k] = [] # add new output file rowsets[k].append(row) # add this row to appropriate output file - return (rowsets) # returns col value, [rows] + return rowsets # returns col value, [rows] def convert(self, infname, outfname): """ From b7b5832af788200b07bb0101ad4cca71776ebe60 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:41:07 -0400 Subject: [PATCH 04/13] `exit` is a helper for the interactive shell - `sys.exit` is intended for use in programs. See: https://stackoverflow.com/questions/6501121/difference-between-exit-and-sys-exit-in-python --- tools/src/kicadbomtovendor.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 47e3760..048c55a 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -11,6 +11,7 @@ # import argparse import re +import sys import xml.etree.ElementTree @@ -39,7 +40,7 @@ def main(): if len(parts) != 2: # must be COLUMN=VALUE print('"--select COLUMN=VALUE" required.') parser.print_help() - exit(1) + sys.exit(1) k = parts[0].strip().upper() v = parts[1].strip().upper() # save selects as upper case if k not in selects: @@ -56,11 +57,11 @@ def main(): except ValueError: print("Input must be a .xml file.") parser.print_help() - exit(1) + sys.exit(1) if suffix.lower() != "xml": # must be XML print("Input must be a .xml file.") parser.print_help() - exit(1) + sys.exit(1) cv = Converter(selects, splitcol, verbose) cv.convert(infname, outfname) @@ -122,7 +123,7 @@ def handlecomp2(self, comp): value = comp.find("value").text except (ValueError, AttributeError) as message: print("Required field missing from {}".format(comp.attrib)) - exit(1) + sys.exit(1) fieldvals["REF"] = ref fieldvals["FOOTPRINT"] = footprint fieldvals["VALUE"] = value @@ -218,7 +219,7 @@ def splitrows(self, splitcol, rows): splitpos = self.fieldlist.index(splitcol) # get index of split column if splitpos is None: # must find print('Column "{}" from --split not found in BOM'.format(splitcol)) - exit(1) + sys.exit(1) for row in rows: k = row[splitpos] # get split column value if k is None or k == "": From ed8f4a25e6c92a518a294a706e8f7772a3952aee Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:43:23 -0400 Subject: [PATCH 05/13] Switch to defaultdict instead of key check --- tools/src/kicadbomtovendor.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 048c55a..2ed776d 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -14,6 +14,7 @@ import sys import xml.etree.ElementTree +from collections import defaultdict # # Main program @@ -28,7 +29,7 @@ def main(): args = parser.parse_args() # parse command line print(args) verbose = args.verbose - selects = {} # dict of (k, set(v)) + selects = defaultdict(set) # dict of (k, set(v)) splitcol = args.split # split on this arg if splitcol is not None: splitcol = splitcol.upper() # upper case column name @@ -43,8 +44,7 @@ def main(): sys.exit(1) k = parts[0].strip().upper() v = parts[1].strip().upper() # save selects as upper case - if k not in selects: - selects[k] = set() # need set for this key + selects[k].add(v) # add value to set for this key print("Selection rules: ") for (k, sset) in selects.items(): @@ -215,7 +215,7 @@ def splitrows(self, splitcol, rows): Usually used to break up a BOM by vendor """ - rowsets = {} # col value, [rows] + rowsets = defaultdict(list) # col value, [rows] splitpos = self.fieldlist.index(splitcol) # get index of split column if splitpos is None: # must find print('Column "{}" from --split not found in BOM'.format(splitcol)) @@ -225,8 +225,6 @@ def splitrows(self, splitcol, rows): if k is None or k == "": k = "NONE" # use NONE if no value available k = k.upper() # always in upper case - if k not in rowsets: - rowsets[k] = [] # add new output file rowsets[k].append(row) # add this row to appropriate output file return rowsets # returns col value, [rows] From 6e7c57e86ce25b4933230036685190470524f34f Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:44:39 -0400 Subject: [PATCH 06/13] Use context manager to open file --- tools/src/kicadbomtovendor.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 2ed776d..00e300c 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -201,12 +201,11 @@ def outputfile(self, rows, outfname): Output one file containing given rows """ heading = ",".join(self.fieldlist) # heading line - outf = open(outfname, "w") # open output file - outf.write(heading + "\n") # heading line - for row in rows: # output all rows - s = ",".join(row) - outf.write(s + "\n") # print to file - outf.close() # done + with open(outfname, "w") as outf: # open output file + outf.write(heading + "\n") # heading line + for row in rows: # output all rows + s = ",".join(row) + outf.write(s + "\n") # print to file print('Created file "{}".'.format(outfname)) # report def splitrows(self, splitcol, rows): From 9ab641bac9d4a5ff8bb64c5f324e1fe19dcb2633 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:44:51 -0400 Subject: [PATCH 07/13] Use augmented assignments --- tools/src/kicadbomtovendor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 00e300c..1b8df2e 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -187,7 +187,7 @@ def additems(self, rows): quan = 0 refs = [] prevrow = row # process new row - quan = quan + int(row[quanpos]) # add this quantity + quan += int(row[quanpos]) # add this quantity refs.append(row[refpos]) # add to list of refs if prevrow is not None: # end of file From 550f5f6fbe93f0e1f0f72b0b2dc466aa6cc501de Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:46:23 -0400 Subject: [PATCH 08/13] Avoid string concatenation --- tools/src/kicadbomtovendor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 1b8df2e..0acaac7 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -255,7 +255,7 @@ def convert(self, infname, outfname): else: # one file per column value rowsets = self.splitrows(self.splitcol, rows) for (colval, rows) in rowsets.items(): # output multiple files - self.outputfile(rows, outfname + "-" + colval + self.OUTPUTSUFFIX) + self.outputfile(rows, "{}-{}{}".format(outfname, colval, self.OUTPUTSUFFIX)) if __name__ == "__main__": From 4742fdfb10411c9a7f0fae4dc3842e2c7ecedfc5 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:46:43 -0400 Subject: [PATCH 09/13] Fix error when not using splitcol --- tools/src/kicadbomtovendor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 0acaac7..5f622b5 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -251,7 +251,7 @@ def convert(self, infname, outfname): rows = self.additems(rows) # combine items # Pass 4 - output rows if self.splitcol is None: # all in one file - self.outputfile(outfname + self.OUTPUTSUFFIX) # one file + self.outputfile(rows, outfname + self.OUTPUTSUFFIX) # one file else: # one file per column value rowsets = self.splitrows(self.splitcol, rows) for (colval, rows) in rowsets.items(): # output multiple files From f1e8b8f2d5b2e00d68ed163c6ac7472d41e3c5d5 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:49:30 -0400 Subject: [PATCH 10/13] remove unused variable --- tools/src/kicadbomtovendor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 5f622b5..d2f1a08 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -16,6 +16,7 @@ from collections import defaultdict + # # Main program # @@ -145,7 +146,6 @@ def assembleline(self, fieldvals): """ Assemble output fields into a list """ - s = '' # empty line outfields = [] # output fields for fname in self.fieldlist: # for all fields if fname in fieldvals: From b9563097e9a9cc853fd1a3fc9c0c1750b2ffde72 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:53:11 -0400 Subject: [PATCH 11/13] Use os.path.splitext --- tools/src/kicadbomtovendor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index d2f1a08..1eb702c 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -13,6 +13,7 @@ import re import sys import xml.etree.ElementTree +import os from collections import defaultdict @@ -48,13 +49,13 @@ def main(): selects[k].add(v) # add value to set for this key print("Selection rules: ") - for (k, sset) in selects.items(): + for k, sset in selects.items(): print(' Select if {} is in {}.'.format(k, list(sset))) # Process all files on command line for infname in args.files: print(infname) try: - (outfname, suffix) = infname.rsplit(".", 1) # remove suffix + outfname, suffix = os.path.splitext(infname) # remove suffix except ValueError: print("Input must be a .xml file.") parser.print_help() @@ -254,7 +255,7 @@ def convert(self, infname, outfname): self.outputfile(rows, outfname + self.OUTPUTSUFFIX) # one file else: # one file per column value rowsets = self.splitrows(self.splitcol, rows) - for (colval, rows) in rowsets.items(): # output multiple files + for colval, rows in rowsets.items(): # output multiple files self.outputfile(rows, "{}-{}{}".format(outfname, colval, self.OUTPUTSUFFIX)) From 6dcae2f8476577116bf16236c05cbd2f09a84580 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:58:36 -0400 Subject: [PATCH 12/13] Remove obsolete try/except --- tools/src/kicadbomtovendor.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 1eb702c..317d2d3 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -54,12 +54,7 @@ def main(): # Process all files on command line for infname in args.files: print(infname) - try: - outfname, suffix = os.path.splitext(infname) # remove suffix - except ValueError: - print("Input must be a .xml file.") - parser.print_help() - sys.exit(1) + outfname, suffix = os.path.splitext(infname) # remove suffix if suffix.lower() != "xml": # must be XML print("Input must be a .xml file.") parser.print_help() From 20e6666fe4aa3b3529c0a21a86ce35c9090965c4 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Sat, 13 Apr 2019 11:59:41 -0400 Subject: [PATCH 13/13] reorder imports --- tools/src/kicadbomtovendor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/src/kicadbomtovendor.py b/tools/src/kicadbomtovendor.py index 317d2d3..8f4a728 100644 --- a/tools/src/kicadbomtovendor.py +++ b/tools/src/kicadbomtovendor.py @@ -10,11 +10,10 @@ # # import argparse +import os import re import sys import xml.etree.ElementTree -import os - from collections import defaultdict