diff --git a/gerbmerge/amacro.py b/gerbmerge/amacro.py index f6b0e21..4d47720 100644 --- a/gerbmerge/amacro.py +++ b/gerbmerge/amacro.py @@ -112,7 +112,7 @@ class ApertureMacroPrimitive: valids = None if valids is None: - raise RuntimeError, 'Undefined aperture macro primitive code %d' % code + raise RuntimeError('Undefined aperture macro primitive code %d' % code) # We expect exactly the number of fields required, except for macro # type 4 which is an outline and has a variable number of points. @@ -126,18 +126,18 @@ class ApertureMacroPrimitive: # - last field is rotation if self.code==4: if len(fields) < 2: - raise RuntimeError, 'Outline macro primitive has way too few fields' + raise RuntimeError('Outline macro primitive has way too few fields') try: N = int(fields[1]) except: - raise RuntimeError, 'Outline macro primitive has non-integer number of points' + raise RuntimeError('Outline macro primitive has non-integer number of points') if len(fields) != (5+2*N): - raise RuntimeError, 'Outline macro primitive has %d fields...expecting %d fields' % (len(fields), 3+2*N) + raise RuntimeError('Outline macro primitive has %d fields...expecting %d fields' % (len(fields), 3+2*N)) else: if len(fields) != len(valids): - raise RuntimeError, 'Macro primitive has %d fields...expecting %d fields' % (len(fields), len(valids)) + raise RuntimeError('Macro primitive has %d fields...expecting %d fields' % (len(fields), len(valids))) # Convert each parameter on the input line to an entry in the self.parms # list, using either int() or float() conversion. @@ -150,7 +150,7 @@ class ApertureMacroPrimitive: try: self.parms.append(converter(fields[parmix])) except: - raise RuntimeError, 'Aperture macro primitive parameter %d has incorrect type' % (parmix+1) + raise RuntimeError('Aperture macro primitive parameter %d has incorrect type' % (parmix+1)) def setFromLine(self, line): # Account for DOS line endings and get rid of line ending and '*' at the end @@ -164,12 +164,12 @@ class ApertureMacroPrimitive: try: code = int(fields[0]) except: - raise RuntimeError, 'Illegal aperture macro primitive code "%s"' % fields[0] + raise RuntimeError('Illegal aperture macro primitive code "%s"' % fields[0]) self.setFromFields(code, fields[1:]) except: - print '='*20 - print "==> ", line - print '='*20 + print('='*20) + print("==> ", line) + print('='*20) raise def rotate(self, flip): @@ -283,7 +283,7 @@ def parseApertureMacro(s, fid): M.add(P) else: - raise RuntimeError, "Premature end-of-file while parsing aperture macro" + raise RuntimeError("Premature end-of-file while parsing aperture macro") else: return None @@ -295,7 +295,7 @@ def addToApertureMacroTable(AM): # Must sort keys by integer value, not string since 99 comes before 100 # as an integer but not a string. - keys = map(int, map(lambda K: K[1:], GAMT.keys())) + keys = list(map(int, [K[1:] for K in list(GAMT.keys())])) keys.sort() if len(keys): @@ -338,25 +338,23 @@ if __name__=="__main__": MR = M.rotated(0) # Generate the Gerber so we can view it - fid = file('amacro.ger', 'wt') - print >> fid, \ -"""G75* + fid = open('amacro.ger', 'wt') + print("""G75* G70* %OFA0B0*% %FSLAX24Y24*% %IPPOS*% -%LPD*%""" +%LPD*%""", file=fid) M.writeDef(fid) MR.writeDef(fid) - print >> fid, \ -"""%ADD10TEST*% + print("""%ADD10TEST*% %ADD11TESTR*% D10* X010000Y010000D03* D11* X015000Y010000D03* -M02*""" +M02*""", file=fid) fid.close() - print M - print MR + print(M) + print(MR) diff --git a/gerbmerge/aptable.py b/gerbmerge/aptable.py index 51eb253..51641bc 100644 --- a/gerbmerge/aptable.py +++ b/gerbmerge/aptable.py @@ -168,17 +168,17 @@ def parseAperture(s, knownMacroNames): code, dimx, dimy = match.groups() if ap[0] in ('Macro',): - if knownMacroNames.has_key(dimx): + if dimx in knownMacroNames: dimx = knownMacroNames[dimx] # dimx is now GLOBAL, permanent macro name (e.g., 'M2') else: - raise RuntimeError, 'Aperture Macro name "%s" not defined' % dimx + raise RuntimeError('Aperture Macro name "%s" not defined' % dimx) else: try: dimx = float(dimx) if dimy: dimy = float(dimy) except: - raise RuntimeError, "Illegal floating point aperture size" + raise RuntimeError("Illegal floating point aperture size") return Aperture(ap, code, dimx, dimy) @@ -221,7 +221,7 @@ def constructApertureTable(fileList): # [andreika]: units conversion units_div = 1.0 - fid = file(fname,'rt') + fid = open(fname,'rt') for line in fid: # Get rid of CR line = line.replace('\x0D', '') @@ -276,19 +276,19 @@ def constructApertureTable(fileList): # Now, go through and assign sequential codes to all apertures code = 10 - for val in AT.values(): + for val in list(AT.values()): key = 'D%d' % code GAT[key] = val val.code = key code += 1 if 0: - keylist = config.GAT.keys() + keylist = list(config.GAT.keys()) keylist.sort() - print 'Apertures' - print '=========' + print('Apertures') + print('=========') for key in keylist: - print '%s' % config.GAT[key] + print('%s' % config.GAT[key]) sys.exit(0) def findHighestApertureCode(keys): @@ -304,7 +304,7 @@ def findHighestApertureCode(keys): def addToApertureTable(AP): GAT = config.GAT - lastCode = findHighestApertureCode(GAT.keys()) + lastCode = findHighestApertureCode(list(GAT.keys())) code = 'D%d' % (lastCode+1) GAT[code] = AP AP.code = code @@ -315,7 +315,7 @@ def findInApertureTable(AP): """Return 'D10', for example in response to query for an object of type Aperture()""" hash = AP.hash() - for key, val in config.GAT.items(): + for key, val in list(config.GAT.items()): if hash==val.hash(): return key @@ -335,16 +335,16 @@ def findOrAddAperture(AP): if __name__=="__main__": constructApertureTable(sys.argv[1:]) - keylist = config.GAMT.keys() + keylist = list(config.GAMT.keys()) keylist.sort() - print 'Aperture Macros' - print '===============' + print('Aperture Macros') + print('===============') for key in keylist: - print '%s' % config.GAMT[key] + print('%s' % config.GAMT[key]) - keylist = config.GAT.keys() + keylist = list(config.GAT.keys()) keylist.sort() - print 'Apertures' - print '=========' + print('Apertures') + print('=========') for key in keylist: - print '%s' % config.GAT[key] + print('%s' % config.GAT[key]) diff --git a/gerbmerge/config.py b/gerbmerge/config.py index 721e9ee..28a629c 100644 --- a/gerbmerge/config.py +++ b/gerbmerge/config.py @@ -15,7 +15,7 @@ http://github.com/unwireddevices/gerbmerge """ import sys -import ConfigParser +import configparser import re import string @@ -116,7 +116,7 @@ MinimumFeatureDimension = {} # hash string. The value is the aperture code (e.g., 'D10') or macro name (e.g., 'M5'). def buildRevDict(D): RevD = {} - for key,val in D.items(): + for key,val in list(D.items()): RevD[val.hash()] = key return RevD @@ -127,12 +127,12 @@ def parseStringList(L): if 0: if L[0]=="'": if L[-1] != "'": - raise RuntimeError, "Illegal configuration string '%s'" % L + raise RuntimeError("Illegal configuration string '%s'" % L) L = L[1:-1] elif L[0]=='"': if L[-1] != '"': - raise RuntimeError, "Illegal configuration string '%s'" % L + raise RuntimeError("Illegal configuration string '%s'" % L) L = L[1:-1] # This pattern matches quotes at the beginning and end...quotes must match @@ -153,14 +153,14 @@ def parseToolList(fname): TL = {} try: - fid = file(fname, 'rt') - except Exception, detail: - raise RuntimeError, "Unable to open tool list file '%s':\n %s" % (fname, str(detail)) + fid = open(fname, 'rt') + except Exception as detail: + raise RuntimeError("Unable to open tool list file '%s':\n %s" % (fname, str(detail))) pat_in = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*in\s*') pat_mm = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*mm\s*') pat_mil = re.compile(r'\s*(T\d+)\s+([0-9.]+)\s*(?:mil)?') - for line in fid.xreadlines(): + for line in fid: line = string.strip(line) if (not line) or (line[0] in ('#', ';')): continue @@ -182,7 +182,7 @@ def parseToolList(fname): try: size = float(size) except: - raise RuntimeError, "Tool size in file '%s' is not a valid floating-point number:\n %s" % (fname,line) + raise RuntimeError("Tool size in file '%s' is not a valid floating-point number:\n %s" % (fname,line)) if mil: size = size*0.001 # Convert mil to inches @@ -192,8 +192,8 @@ def parseToolList(fname): # Canonicalize tool so that T1 becomes T01 tool = 'T%02d' % int(tool[1:]) - if TL.has_key(tool): - raise RuntimeError, "Tool '%s' defined more than once in tool list file '%s'" % (tool,fname) + if tool in TL: + raise RuntimeError("Tool '%s' defined more than once in tool list file '%s'" % (tool,fname)) TL[tool]=size fid.close() @@ -215,38 +215,38 @@ def parseToolList(fname): def parseConfigFile(fname, Config=Config, Jobs=Jobs): global DefaultToolList - CP = ConfigParser.ConfigParser() - CP.readfp(file(fname.rstrip(),'rt')) + CP = configparser.ConfigParser() + CP.readfp(open(fname.rstrip(),'rt')) # First parse global options if CP.has_section('Options'): for opt in CP.options('Options'): # Is it one we expect - if Config.has_key(opt): + if opt in Config: # Yup...override it Config[opt] = CP.get('Options', opt) - elif CP.defaults().has_key(opt): + elif opt in CP.defaults(): pass # Ignore DEFAULTS section keys elif opt in ('fabricationdrawing', 'outlinelayer'): - print '*'*73 - print '\nThe FabricationDrawing and OutlineLayer configuration options have been' - print 'renamed as of GerbMerge version 1.0. Please consult the documentation for' - print 'a description of the new options, then modify your configuration file.\n' - print '*'*73 + print('*'*73) + print('\nThe FabricationDrawing and OutlineLayer configuration options have been') + print('renamed as of GerbMerge version 1.0. Please consult the documentation for') + print('a description of the new options, then modify your configuration file.\n') + print('*'*73) sys.exit(1) else: - raise RuntimeError, "Unknown option '%s' in [Options] section of configuration file" % opt + raise RuntimeError("Unknown option '%s' in [Options] section of configuration file" % opt) else: - raise RuntimeError, "Missing [Options] section in configuration file" + raise RuntimeError("Missing [Options] section in configuration file") # Ensure we got a tool list - if not Config.has_key('toollist'): - raise RuntimeError, "INTERNAL ERROR: Missing tool list assignment in [Options] section" + if 'toollist' not in Config: + raise RuntimeError("INTERNAL ERROR: Missing tool list assignment in [Options] section") # Make integers integers, floats floats - for key,val in Config.items(): + for key,val in list(Config.items()): try: val = int(val) Config[key]=val @@ -283,7 +283,7 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs): for index in range(0, len(temp), 2): MinimumFeatureDimension[ temp[index] ] = float( temp[index + 1] ) except: - raise RuntimeError, "Illegal configuration string:" + Config['minimumfeaturesize'] + raise RuntimeError("Illegal configuration string:" + Config['minimumfeaturesize']) # Process MergeOutputFiles section to set output file names if CP.has_section('MergeOutputFiles'): @@ -303,10 +303,10 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs): # Ensure all jobs have a board outline if not CP.has_option(jobname, 'boardoutline'): - raise RuntimeError, "Job '%s' does not have a board outline specified" % jobname + raise RuntimeError("Job '%s' does not have a board outline specified" % jobname) if not CP.has_option(jobname, 'drills'): - raise RuntimeError, "Job '%s' does not have a drills layer specified" % jobname + raise RuntimeError("Job '%s' does not have a drills layer specified" % jobname) for layername in CP.options(jobname): if layername[0]=='*' or layername=='boardoutline': @@ -323,10 +323,10 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs): del apfiles if 0: - keylist = GAMT.keys() + keylist = list(GAMT.keys()) keylist.sort() for key in keylist: - print '%s' % GAMT[key] + print('%s' % GAMT[key]) sys.exit(0) # Parse the tool list @@ -350,8 +350,8 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs): if jobname=='MergeOutputFiles': continue if jobname=='GerbMergeGUI': continue - print '' # empty line before hand for readability - print 'Reading data from', jobname, '...' + print('') # empty line before hand for readability + print('Reading data from', jobname, '...') J = jobs.Job(jobname) @@ -369,12 +369,12 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs): try: J.ExcellonDecimals = int(fname) except: - raise RuntimeError, "Excellon decimals '%s' in config file is not a valid integer" % fname + raise RuntimeError("Excellon decimals '%s' in config file is not a valid integer" % fname) elif layername=='repeat': try: J.Repeat = int(fname) except: - raise RuntimeError, "Repeat count '%s' in config file is not a valid integer" % fname + raise RuntimeError("Repeat count '%s' in config file is not a valid integer" % fname) for layername in CP.options(jobname): fname = CP.get(jobname, layername) @@ -388,34 +388,34 @@ def parseConfigFile(fname, Config=Config, Jobs=Jobs): # Emit warnings if some layers are missing LL = LayerList.copy() - for layername in J.apxlat.keys(): - assert LL.has_key(layername) + for layername in list(J.apxlat.keys()): + assert layername in LL del LL[layername] if LL: if errstr=='ERROR': do_abort=1 - print '%s: Job %s is missing the following layers:' % (errstr, jobname) - for layername in LL.keys(): - print ' %s' % layername + print('%s: Job %s is missing the following layers:' % (errstr, jobname)) + for layername in list(LL.keys()): + print(' %s' % layername) # Store the job in the global Jobs dictionary, keyed by job name Jobs[jobname] = J if do_abort: - raise RuntimeError, 'Exiting since jobs are missing layers. Set AllowMissingLayers=1\nto override.' + raise RuntimeError('Exiting since jobs are missing layers. Set AllowMissingLayers=1\nto override.') if __name__=="__main__": CP = parseConfigFile(sys.argv[1]) - print Config + print(Config) sys.exit(0) if 0: - for key, val in CP.defaults().items(): - print '%s: %s' % (key,val) + for key, val in list(CP.defaults().items()): + print('%s: %s' % (key,val)) for section in CP.sections(): - print '[%s]' % section + print('[%s]' % section) for opt in CP.options(section): - print ' %s=%s' % (opt, CP.get(section, opt)) + print(' %s=%s' % (opt, CP.get(section, opt))) diff --git a/gerbmerge/drillcluster.py b/gerbmerge/drillcluster.py index cf9b15a..f8282eb 100644 --- a/gerbmerge/drillcluster.py +++ b/gerbmerge/drillcluster.py @@ -34,7 +34,7 @@ def cluster(drills, tolerance, debug = _DEBUG): debug_print("Clustering drill sizes ...", True) # Loop through all drill sizes - sizes = drills.keys() + sizes = list(drills.keys()) sizes.sort() for size in sizes: @@ -106,7 +106,7 @@ def remap(jobs, globalToolMap, debug = _DEBUG): debug_print( str(job.xcommands) ) new_tools = {} new_commands = {} - for tool, diam in job.xdiam.items(): + for tool, diam in list(job.xdiam.items()): ##debug_print("\n Current tool: " + tool + " (" + str_d(diam) + ")") @@ -147,9 +147,9 @@ def debug_print(text, status = False, newLine = True): if _DEBUG or (status and _STATUS): if newLine: - print " ", text + print(" ", text) else: - print " ", text, + print(" ", text, end=' ') def str_d(drills): """ @@ -180,7 +180,7 @@ def drillsToString(drills): """ string = "" - drills = drills.items() + drills = list(drills.items()) drills.sort() for size, drill in drills: string += drill + " = " + str_d(size) + "\n " @@ -194,7 +194,7 @@ def drillsToString(drills): if __name__=="__main__": import random - print " Clustering random drills..." + print(" Clustering random drills...") old = {} tool_num = 0 diff --git a/gerbmerge/fabdrawing.py b/gerbmerge/fabdrawing.py index ea2d2bb..498f890 100644 --- a/gerbmerge/fabdrawing.py +++ b/gerbmerge/fabdrawing.py @@ -25,7 +25,7 @@ def writeDrillHits(fid, Place, Tools): try: size = config.GlobalToolMap[tool] except: - raise RuntimeError, "INTERNAL ERROR: Tool code %s not found in global tool list" % tool + raise RuntimeError("INTERNAL ERROR: Tool code %s not found in global tool list" % tool) #for row in Layout: # row.writeDrillHits(fid, size, toolNumber) @@ -157,9 +157,9 @@ def writeUserText(fid, X, Y): if not fname: return try: - tfile = file(fname, 'rt') - except Exception, detail: - raise RuntimeError, "Could not open fabrication drawing text file '%s':\n %s" % (fname,str(detail)) + tfile = open(fname, 'rt') + except Exception as detail: + raise RuntimeError("Could not open fabrication drawing text file '%s':\n %s" % (fname,str(detail))) lines = tfile.readlines() tfile.close() @@ -170,7 +170,7 @@ def writeUserText(fid, X, Y): for line in lines: # Get rid of CR - line = string.replace(line, '\x0D', '') + line = str.replace(line, '\x0D', '') # Chop off \n #if line[-1] in string.whitespace: diff --git a/gerbmerge/geometry.py b/gerbmerge/geometry.py index 4d20d50..63e57d6 100644 --- a/gerbmerge/geometry.py +++ b/gerbmerge/geometry.py @@ -15,7 +15,7 @@ import math # Ensure all list elements are unique def uniqueify(L): - return {}.fromkeys(L).keys() + return list({}.fromkeys(L).keys()) # This function rounds an (X,Y) point to integer co-ordinates def roundPoint(pt): @@ -343,4 +343,4 @@ if __name__=="__main__": assert isRect1InRect2( (100,100,500,500), (0,600,300,300) ) == False assert isRect1InRect2( (100,100,500,500), (0,0,500,500) ) == True - print 'All tests pass' + print('All tests pass') diff --git a/gerbmerge/gerbmerge.py b/gerbmerge/gerbmerge.py index cb83bf0..bfdc3e4 100755 --- a/gerbmerge/gerbmerge.py +++ b/gerbmerge/gerbmerge.py @@ -60,8 +60,7 @@ config.PlacementFile = None GUI = None def usage(): - print \ -""" + print(""" Usage: gerbmerge [Options] configfile [layoutfile] Options: @@ -89,7 +88,7 @@ the layout file (if any) is ignored. NOTE: The dimensions of each job are determined solely by the maximum extent of the board outline layer for each job. -""" +""") sys.exit(1) # changed these two writeGerberHeader files to take metric units (mm) into account: @@ -146,14 +145,14 @@ G71* writeGerberHeader = writeGerberHeader22degrees def writeApertureMacros(fid, usedDict): - keys = config.GAMT.keys() + keys = list(config.GAMT.keys()) keys.sort() for key in keys: if key in usedDict: config.GAMT[key].writeDef(fid) def writeApertures(fid, usedDict): - keys = config.GAT.keys() + keys = list(config.GAT.keys()) keys.sort() for key in keys: if key in usedDict: @@ -254,7 +253,7 @@ def writeCropMarks(fid, drawing_code, OriginX, OriginY, MaxXExtent, MaxYExtent): fid.write('X%07dY%07dD01*\n' % (util.in2gerb(x+cropW), util.in2gerb(y+0.000))) def disclaimer(): - print """ + print(""" **************************************************** * R E A D C A R E F U L L Y * * * @@ -279,14 +278,14 @@ def disclaimer(): To agree to the above terms, press 'y' then Enter. Any other key will exit the program. -""" +""") - s = raw_input() + s = input() if s == 'y': - print + print() return - print "\nExiting..." + print("\nExiting...") sys.exit(0) def tile_jobs(Jobs): @@ -318,9 +317,9 @@ def tile_jobs(Jobs): if not tile: # add metric support (1/1000 mm vs. 1/100,000 inch) if config.Config['measurementunits'] == 'inch': - raise RuntimeError, 'Panel size %.2f"x%.2f" is too small to hold jobs' % (PX,PY) + raise RuntimeError('Panel size %.2f"x%.2f" is too small to hold jobs' % (PX,PY)) else: - raise RuntimeError, 'Panel size %.2fmmx%.2fmm is too small to hold jobs' % (PX,PY) + raise RuntimeError('Panel size %.2fmmx%.2fmm is too small to hold jobs' % (PX,PY)) return tile @@ -339,7 +338,7 @@ def merge(opts, args, gui = None): elif arg=='normal': writeGerberHeader = writeGerberHeader22degrees else: - raise RuntimeError, 'Unknown octagon format' + raise RuntimeError('Unknown octagon format') elif opt in ('--random-search',): config.AutoSearchType = RANDOM_SEARCH elif opt in ('--full-search',): @@ -358,10 +357,10 @@ def merge(opts, args, gui = None): elif opt in ('-s', '--skipdisclaimer'): skipDisclaimer = 1 else: - raise RuntimeError, "Unknown option: %s" % opt + raise RuntimeError("Unknown option: %s" % opt) if len(args) > 2 or len(args) < 1: - raise RuntimeError, 'Invalid number of arguments' + raise RuntimeError('Invalid number of arguments') if (skipDisclaimer == 0): disclaimer() @@ -372,7 +371,7 @@ def merge(opts, args, gui = None): config.parseConfigFile(args[0]) # Force all X and Y coordinates positive by adding absolute value of minimum X and Y - for name, job in config.Jobs.iteritems(): + for name, job in config.Jobs.items(): min_x, min_y = job.mincoordinates() shift_x = shift_y = 0 if min_x < 0: shift_x = abs(min_x) @@ -381,31 +380,31 @@ def merge(opts, args, gui = None): job.fixcoordinates( shift_x, shift_y ) # Display job properties - for job in config.Jobs.values(): - print 'Job %s:' % job.name, + for job in list(config.Jobs.values()): + print('Job %s:' % job.name, end=' ') if job.Repeat > 1: - print '(%d instances)' % job.Repeat + print('(%d instances)' % job.Repeat) else: - print - print ' Extents: (%d,%d)-(%d,%d)' % (job.minx,job.miny,job.maxx,job.maxy) + print() + print(' Extents: (%d,%d)-(%d,%d)' % (job.minx,job.miny,job.maxx,job.maxy)) # add metric support (1/1000 mm vs. 1/100,000 inch) if config.Config['measurementunits'] == 'inch': - print ' Size: %f" x %f"' % (job.width_in(), job.height_in()) + print(' Size: %f" x %f"' % (job.width_in(), job.height_in())) else: - print ' Size: %5.3fmm x %5.3fmm' % (job.width_in(), job.height_in()) - print + print(' Size: %5.3fmm x %5.3fmm' % (job.width_in(), job.height_in())) + print() # Trim drill locations and flash data to board extents if config.TrimExcellon: updateGUI("Trimming Excellon data...") - print 'Trimming Excellon data to board outlines ...' - for job in config.Jobs.values(): + print('Trimming Excellon data to board outlines ...') + for job in list(config.Jobs.values()): job.trimExcellon() if config.TrimGerber: updateGUI("Trimming Gerber data...") - print 'Trimming Gerber data to board outlines ...' - for job in config.Jobs.values(): + print('Trimming Gerber data to board outlines ...') + for job in list(config.Jobs.values()): job.trimGerber() # We start origin at (0.1", 0.1") just so we don't get numbers close to 0 @@ -416,7 +415,7 @@ def merge(opts, args, gui = None): # Read the layout file and construct the nested list of jobs. If there # is no layout file, do auto-layout. updateGUI("Performing layout...") - print 'Performing layout ...' + print('Performing layout ...') if len(args) > 1: Layout = parselayout.parseLayoutFile(args[1]) @@ -439,7 +438,7 @@ def merge(opts, args, gui = None): Place.addFromFile(config.PlacementFile, config.Jobs) else: # Do an automatic layout based on our tiling algorithm. - tile = tile_jobs(config.Jobs.values()) + tile = tile_jobs(list(config.Jobs.values())) Place = placement.Placement() Place.addFromTiling(tile, OriginX + config.Config['leftmargin'], OriginY + config.Config['bottommargin']) @@ -491,9 +490,9 @@ def merge(opts, args, gui = None): drawing_code1 = aptable.addToApertureTable(AP) updateGUI("Writing merged files...") - print 'Writing merged output files ...' + print('Writing merged output files ...') - for layername in config.LayerList.keys(): + for layername in list(config.LayerList.keys()): lname = layername if lname[0]=='*': lname = lname[1:] @@ -504,7 +503,7 @@ def merge(opts, args, gui = None): fullname = 'merged.%s.ger' % lname OutputFiles.append(fullname) #print 'Writing %s ...' % fullname - fid = file(fullname, 'wt') + fid = open(fullname, 'wt') writeGerberHeader(fid) # Determine which apertures and macros are truly needed @@ -516,12 +515,12 @@ def merge(opts, args, gui = None): apmUsedDict.update(apmd) # Increase aperature sizes to match minimum feature dimension - if config.MinimumFeatureDimension.has_key(layername): + if layername in config.MinimumFeatureDimension: - print ' Thickening', lname, 'feature dimensions ...' + print(' Thickening', lname, 'feature dimensions ...') # Fix each aperture used in this layer - for ap in apUsedDict.keys(): + for ap in list(apUsedDict.keys()): new = config.GAT[ap].getAdjusted( config.MinimumFeatureDimension[layername] ) if not new: ## current aperture size met minimum requirement continue @@ -595,7 +594,7 @@ def merge(opts, args, gui = None): if fullname and fullname.lower() != "none": OutputFiles.append(fullname) #print 'Writing %s ...' % fullname - fid = file(fullname, 'wt') + fid = open(fullname, 'wt') writeGerberHeader(fid) # Write width-1 aperture to file @@ -624,7 +623,7 @@ def merge(opts, args, gui = None): if fullname and fullname.lower() != "none": OutputFiles.append(fullname) #print 'Writing %s ...' % fullname - fid = file(fullname, 'wt') + fid = open(fullname, 'wt') writeGerberHeader(fid) # Write width-1 aperture to file @@ -644,19 +643,19 @@ def merge(opts, args, gui = None): # of tools. if 0: Tools = {} - for job in config.Jobs.values(): - for key in job.xcommands.keys(): + for job in list(config.Jobs.values()): + for key in list(job.xcommands.keys()): Tools[key] = 1 - Tools = Tools.keys() + Tools = list(Tools.keys()) Tools.sort() else: toolNum = 0 # First construct global mapping of diameters to tool numbers - for job in config.Jobs.values(): - for tool,diam in job.xdiam.items(): - if config.GlobalToolRMap.has_key(diam): + for job in list(config.Jobs.values()): + for tool,diam in list(job.xdiam.items()): + if diam in config.GlobalToolRMap: continue toolNum += 1 @@ -665,24 +664,24 @@ def merge(opts, args, gui = None): # Cluster similar tool sizes to reduce number of drills if config.Config['drillclustertolerance'] > 0: config.GlobalToolRMap = drillcluster.cluster( config.GlobalToolRMap, config.Config['drillclustertolerance'] ) - drillcluster.remap( Place.jobs, config.GlobalToolRMap.items() ) + drillcluster.remap( Place.jobs, list(config.GlobalToolRMap.items()) ) # Now construct mapping of tool numbers to diameters - for diam,tool in config.GlobalToolRMap.items(): + for diam,tool in list(config.GlobalToolRMap.items()): config.GlobalToolMap[tool] = diam # Tools is just a list of tool names - Tools = config.GlobalToolMap.keys() + Tools = list(config.GlobalToolMap.keys()) Tools.sort() fullname = config.Config['fabricationdrawingfile'] if fullname and fullname.lower() != 'none': if len(Tools) > strokes.MaxNumDrillTools: - raise RuntimeError, "Only %d different tool sizes supported for fabrication drawing." % strokes.MaxNumDrillTools + raise RuntimeError("Only %d different tool sizes supported for fabrication drawing." % strokes.MaxNumDrillTools) OutputFiles.append(fullname) #print 'Writing %s ...' % fullname - fid = file(fullname, 'wt') + fid = open(fullname, 'wt') writeGerberHeader(fid) writeApertures(fid, {drawing_code1: None}) fid.write('%s*\n' % drawing_code1) # Choose drawing aperture @@ -699,7 +698,7 @@ def merge(opts, args, gui = None): fullname = 'merged.drills.xln' OutputFiles.append(fullname) #print 'Writing %s ...' % fullname - fid = file(fullname, 'wt') + fid = open(fullname, 'wt') writeExcellonHeader(fid) @@ -709,7 +708,7 @@ def merge(opts, args, gui = None): try: size = config.GlobalToolMap[tool] except: - raise RuntimeError, "INTERNAL ERROR: Tool code %s not found in global tool map" % tool + raise RuntimeError("INTERNAL ERROR: Tool code %s not found in global tool map" % tool) writeExcellonTool(fid, tool, size) writeExcellonHeaderEnd(fid) @@ -755,57 +754,57 @@ def merge(opts, args, gui = None): fullname = 'merged.toollist.drl' OutputFiles.append(fullname) #print 'Writing %s ...' % fullname - fid = file(fullname, 'wt') + fid = open(fullname, 'wt') - print '-'*50 + print('-'*50) # add metric support (1/1000 mm vs. 1/100,000 inch) if config.Config['measurementunits'] == 'inch': - print ' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY) - print ' Job Area : %.2f sq. in.' % totalarea + print(' Job Size : %f" x %f"' % (MaxXExtent-OriginX, MaxYExtent-OriginY)) + print(' Job Area : %.2f sq. in.' % totalarea) else: - print ' Job Size : %.2fmm x %.2fmm' % (MaxXExtent-OriginX, MaxYExtent-OriginY) - print ' Job Area : %.0f mm2' % totalarea + print(' Job Size : %.2fmm x %.2fmm' % (MaxXExtent-OriginX, MaxYExtent-OriginY)) + print(' Job Area : %.0f mm2' % totalarea) - print ' Area Usage : %.1f%%' % (jobarea/totalarea*100) - print ' Drill hits : %d' % drillhits + print(' Area Usage : %.1f%%' % (jobarea/totalarea*100)) + print(' Drill hits : %d' % drillhits) if config.Config['measurementunits'] == 'inch': - print 'Drill density : %.1f hits/sq.in.' % (drillhits/totalarea) + print('Drill density : %.1f hits/sq.in.' % (drillhits/totalarea)) else: - print 'Drill density : %.2f hits/cm2' % (100*drillhits/totalarea) + print('Drill density : %.2f hits/cm2' % (100*drillhits/totalarea)) - print '\nTool List:' + print('\nTool List:') smallestDrill = 999.9 for tool in Tools: if ToolStats[tool]: if config.Config['measurementunits'] == 'inch': fid.write('%s %.4fin\n' % (tool, config.GlobalToolMap[tool])) - print ' %s %.4f" %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool]) + print(' %s %.4f" %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool])) else: fid.write('%s %.4fmm\n' % (tool, config.GlobalToolMap[tool])) - print ' %s %.4fmm %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool]) + print(' %s %.4fmm %5d hits' % (tool, config.GlobalToolMap[tool], ToolStats[tool])) smallestDrill = min(smallestDrill, config.GlobalToolMap[tool]) fid.close() if config.Config['measurementunits'] == 'inch': - print "Smallest Tool: %.4fin" % smallestDrill + print("Smallest Tool: %.4fin" % smallestDrill) else: - print "Smallest Tool: %.4fmm" % smallestDrill + print("Smallest Tool: %.4fmm" % smallestDrill) - print - print 'Output Files :' + print() + print('Output Files :') for f in OutputFiles: - print ' ', f + print(' ', f) if (MaxXExtent-OriginX)>config.Config['panelwidth'] or (MaxYExtent-OriginY)>config.Config['panelheight']: - print '*'*75 - print '*' + print('*'*75) + print('*') # add metric support (1/1000 mm vs. 1/100,000 inch) if config.Config['measurementunits'] == 'inch': - print '* ERROR: Merged job exceeds panel dimensions of %.1f"x%.1f"' % (config.Config['panelwidth'],config.Config['panelheight']) + print('* ERROR: Merged job exceeds panel dimensions of %.1f"x%.1f"' % (config.Config['panelwidth'],config.Config['panelheight'])) else: - print '* ERROR: Merged job exceeds panel dimensions of %.1fmmx%.1fmm' % (config.Config['panelwidth'],config.Config['panelheight']) - print '*' - print '*'*75 + print('* ERROR: Merged job exceeds panel dimensions of %.1fmmx%.1fmm' % (config.Config['panelwidth'],config.Config['panelheight'])) + print('*') + print('*'*75) sys.exit(1) # Done! @@ -826,19 +825,19 @@ def main(): if opt in ('-h', '--help'): usage() elif opt in ('-v', '--version'): - print """ + print(""" GerbMerge Version %s -- Combine multiple Gerber/Excellon files This program is licensed under the GNU General Public License (GPL) Version 3. See LICENSE file or http://www.fsf.org for details of this license. ProvideYourOwn - http://provideyourown.com -""" % (__version__) +""" % (__version__)) sys.exit(0) elif opt in ('--octagons', '--random-search','--full-search','--rs-fsjobs','--place-file','--no-trim-gerber','--no-trim-excellon', '--search-timeout', '-s', '--skipdisclaimer'): pass ## arguments are valid else: - raise RuntimeError, "Unknown option: %s" % opt + raise RuntimeError("Unknown option: %s" % opt) if len(args) > 2 or len(args) < 1: usage() diff --git a/gerbmerge/jobs.py b/gerbmerge/jobs.py index efe969c..0f4e853 100644 --- a/gerbmerge/jobs.py +++ b/gerbmerge/jobs.py @@ -18,7 +18,7 @@ http://github.com/unwireddevices/gerbmerge import sys import re import string -import __builtin__ +import builtins import copy import types @@ -235,17 +235,17 @@ class Job: self.maxy += y_shift # Shift all commands - for layer, command in self.commands.iteritems(): + for layer, command in self.commands.items(): # Loop through each command in each layer for index in range( len(command) ): c = command[index] # Shift X and Y coordinate of command - if type(c) == types.TupleType: ## ensure that command is of type tuple + if type(c) == tuple: ## ensure that command is of type tuple command_list = list(c) ## convert tuple to list - if (type( command_list[0] ) == types.IntType) \ - and (type( command_list[1] ) == types.IntType): ## ensure that first two elemenst are integers + if (type( command_list[0] ) == int) \ + and (type( command_list[1] ) == int): ## ensure that first two elemenst are integers command_list[0] += x_shift command_list[1] += y_shift command[index] = tuple(command_list) ## convert list back to tuple @@ -253,7 +253,7 @@ class Job: self.commands[layer] = command ## set modified command # Shift all excellon commands - for tool, command in self.xcommands.iteritems(): + for tool, command in self.xcommands.items(): # Loop through each command in each layer for index in range( len(command) ): @@ -261,12 +261,12 @@ class Job: # Shift X and Y coordinate of command command_list = list(c) ## convert tuple to list - if ( type( command_list[0] ) == types.IntType ) \ - and ( type( command_list[1] ) == types.IntType ): ## ensure that first two elemenst are integers + if ( type( command_list[0] ) == int ) \ + and ( type( command_list[1] ) == int ): ## ensure that first two elemenst are integers command_list[0] += x_shift / 10 command_list[1] += y_shift / 10 - if ( type( command_list[2] ) == types.IntType ) \ - and ( type( command_list[3] ) == types.IntType ): ## ensure that first two elemenst are integerslen(command_list) == 4: + if ( type( command_list[2] ) == int ) \ + and ( type( command_list[3] ) == int ): ## ensure that first two elemenst are integerslen(command_list) == 4: # G85 command, need to shift the second pair of xy, too. command_list[2] += x_shift / 10 command_list[3] += y_shift / 10 @@ -286,7 +286,7 @@ class Job: #print 'Reading data from %s ...' % fullname - fid = file(fullname, 'rt') + fid = open(fullname, 'rt') currtool = None self.apxlat[layername] = {} @@ -338,7 +338,7 @@ class Job: for line in fid: # Get rid of CR characters (0x0D) and leading/trailing blanks - line = string.replace(line, '\x0D', '').strip() + line = str.replace(line, '\x0D', '').strip() # Old location of format_pat search. Now moved down into the sub-line parse loop below. @@ -354,11 +354,11 @@ class Job: match = apdef_pat.match(line) if match: if currtool: - raise RuntimeError, "File %s has an aperture definition that comes after drawing commands." % fullname + raise RuntimeError("File %s has an aperture definition that comes after drawing commands." % fullname) A = aptable.parseAperture(line, self.apmxlat[layername]) if not A: - raise RuntimeError, "Unknown aperture definition in file %s" % fullname + raise RuntimeError("Unknown aperture definition in file %s" % fullname) # [andreika]: apply units if type(A.dimx) == float or type(A.dimx) == int: A.dimx *= units_div @@ -366,11 +366,11 @@ class Job: A.dimy *= units_div hash = A.hash() - if not RevGAT.has_key(hash): + if hash not in RevGAT: #print line #print self.apmxlat #print RevGAT - raise RuntimeError, 'File %s has aperture definition "%s" not in global aperture table.' % (fullname, hash) + raise RuntimeError('File %s has aperture definition "%s" not in global aperture table.' % (fullname, hash)) # This says that all draw commands with this aperture code will # be replaced by aperture self.apxlat[layername][code]. @@ -402,7 +402,7 @@ class Job: continue # ignore it so func doesn't choke on it if line[:3] == '%SF': # scale factor - we will ignore it - print 'Scale factor parameter ignored: ' + line + print('Scale factor parameter ignored: ' + line) continue # end basic diptrace fixes @@ -411,11 +411,11 @@ class Job: M = amacro.parseApertureMacro(line,fid) if M: if currtool: - raise RuntimeError, "File %s has an aperture macro definition that comes after drawing commands." % fullname + raise RuntimeError("File %s has an aperture macro definition that comes after drawing commands." % fullname) hash = M.hash() - if not RevGAMT.has_key(hash): - raise RuntimeError, 'File %s has aperture macro definition not in global aperture macro table:\n%s' % (fullname, hash) + if hash not in RevGAMT: + raise RuntimeError('File %s has aperture macro definition not in global aperture macro table:\n%s' % (fullname, hash)) # This says that all aperture definition commands that reference this macro name # will be replaced by aperture macro name self.apmxlat[layername][macroname]. @@ -445,9 +445,9 @@ class Job: continue if item[0]=='T': # omit trailing zeroes - raise RuntimeError, "Trailing zeroes not supported in RS274X files" + raise RuntimeError("Trailing zeroes not supported in RS274X files") if item[0]=='I': # incremental co-ordinates - raise RuntimeError, "Incremental co-ordinates not supported in RS274X files" + raise RuntimeError("Incremental co-ordinates not supported in RS274X files") if item[0]=='N': # Maximum digits for N* commands...ignore it continue @@ -508,7 +508,7 @@ class Job: continue - raise RuntimeError, "G-Code 'G%02d' is not supported" % gcode + raise RuntimeError("G-Code 'G%02d' is not supported" % gcode) # See if this is a tool change (aperture change) command match = tool_pat.match(sub_line) @@ -541,8 +541,8 @@ class Job: continue # Map it using our translation table - if not self.apxlat[layername].has_key(currtool): - raise RuntimeError, 'File %s has tool change command "%s" with no corresponding translation' % (fullname, currtool) + if currtool not in self.apxlat[layername]: + raise RuntimeError('File %s has tool change command "%s" with no corresponding translation' % (fullname, currtool)) currtool = self.apxlat[layername][currtool] @@ -561,17 +561,17 @@ class Job: match = drawXY_pat.match(sub_line) isLastShorthand = False # By default assume we don't make use of last_x and last_y if match: - x, y, d = map(__builtin__.int, match.groups()) + x, y, d = list(map(builtins.int, match.groups())) else: match = drawX_pat.match(sub_line) if match: - x, d = map(__builtin__.int, match.groups()) + x, d = list(map(builtins.int, match.groups())) y = last_y isLastShorthand = True # Indicate we're making use of last_x/last_y else: match = drawY_pat.match(sub_line) if match: - y, d = map(__builtin__.int, match.groups()) + y, d = list(map(builtins.int, match.groups())) x = last_x isLastShorthand = True # Indicate we're making use of last_x/last_y @@ -579,17 +579,17 @@ class Job: if match is None: match = cdrawXY_pat.match(sub_line) if match: - x, y, I, J, d = map(__builtin__.int, match.groups()) + x, y, I, J, d = list(map(builtins.int, match.groups())) else: match = cdrawX_pat.match(sub_line) if match: - x, I, J, d = map(__builtin__.int, match.groups()) + x, I, J, d = list(map(builtins.int, match.groups())) y = last_y isLastShorthand = True # Indicate we're making use of last_x/last_y else: match = cdrawY_pat.match(sub_line) if match: - y, I, J, d = map(__builtin__.int, match.groups()) + y, I, J, d = list(map(builtins.int, match.groups())) x = last_x isLastShorthand = True # Indicate we're making use of last_x/last_y @@ -601,7 +601,7 @@ class Job: if (d != 2) and (last_gmode != 36): # [andreika]: check for fill mode more accurately if not in_fill_mode: - raise RuntimeError, 'File %s has draw command %s with no aperture chosen' % (fullname, sub_line) + raise RuntimeError('File %s has draw command %s with no aperture chosen' % (fullname, sub_line)) # Save last_x/y BEFORE scaling to 2.5 format else subsequent single-ordinate # flashes (e.g., Y with no X) will be scaled twice! @@ -649,7 +649,7 @@ class Job: if match: break else: - raise RuntimeError, 'File %s has uninterpretable line:\n %s' % (fullname, line) + raise RuntimeError('File %s has uninterpretable line:\n %s' % (fullname, line)) sub_line = sub_line[match.end():] # end while still things to match on this line @@ -657,13 +657,13 @@ class Job: fid.close() if 0: - print layername - print self.commands[layername] + print(layername) + print(self.commands[layername]) def parseExcellon(self, fullname): #print 'Reading data from %s ...' % fullname - fid = file(fullname, 'rt') + fid = open(fullname, 'rt') currtool = None suppress_leading = True # Suppress leading zeros by default, equivalent to 'INCH,TZ' @@ -706,14 +706,14 @@ class Job: V.append(None) return tuple(V) - for line in fid.xreadlines(): + for line in fid: # Get rid of CR characters - line = string.replace(line, '\x0D', '') + line = str.replace(line, '\x0D', '') # add support for DipTrace if line[:6]=='METRIC': if (config.Config['measurementunits'] == 'inch'): - raise RuntimeError, "File %s units do match config file" % fullname + raise RuntimeError("File %s units do match config file" % fullname) else: #print "ignoring METRIC directive: " + line continue # ignore it so func doesn't choke on it @@ -746,14 +746,14 @@ class Job: try: diam = float(diam) except: - raise RuntimeError, "File %s has illegal tool diameter '%s'" % (fullname, diam) + raise RuntimeError("File %s has illegal tool diameter '%s'" % (fullname, diam)) # Canonicalize tool number because Protel (of course) sometimes specifies it # as T01 and sometimes as T1. We canonicalize to T01. currtool = 'T%02d' % int(currtool[1:]) - if self.xdiam.has_key(currtool): - raise RuntimeError, "File %s defines tool %s more than once" % (fullname, currtool) + if currtool in self.xdiam: + raise RuntimeError("File %s defines tool %s more than once" % (fullname, currtool)) self.xdiam[currtool] = diam continue @@ -779,13 +779,13 @@ class Job: try: diam = self.ToolList[currtool] except: - raise RuntimeError, "File %s uses tool code %s that is not defined in the job's tool list" % (fullname, currtool) + raise RuntimeError("File %s uses tool code %s that is not defined in the job's tool list" % (fullname, currtool)) else: try: diam = config.DefaultToolList[currtool] except: #print config.DefaultToolList - raise RuntimeError, "File %s uses tool code %s that is not defined in default tool list" % (fullname, currtool) + raise RuntimeError("File %s uses tool code %s that is not defined in default tool list" % (fullname, currtool)) self.xdiam[currtool] = diam continue @@ -811,7 +811,7 @@ class Job: if match: if currtool is None: - raise RuntimeError, 'File %s has plunge command without previous tool selection' % fullname + raise RuntimeError('File %s has plunge command without previous tool selection' % fullname) try: self.xcommands[currtool].append((x,y,stop_x,stop_y)) @@ -827,10 +827,10 @@ class Job: if pat.match(line): break else: - raise RuntimeError, 'File %s has uninterpretable line:\n %s' % (fullname, line) + raise RuntimeError('File %s has uninterpretable line:\n %s' % (fullname, line)) def hasLayer(self, layername): - return self.commands.has_key(layername) + return layername in self.commands def writeGerber(self, fid, layername, Xoff, Yoff): "Write out the data such that the lower-left corner of this job is at the given (X,Y) position, in inches" @@ -858,7 +858,7 @@ class Job: # due to panelizing. fid.write('X%07dY%07dD02*\n' % (X, Y)) for cmd in self.commands[layername]: - if type(cmd) is types.TupleType: + if type(cmd) is tuple: if len(cmd)==3: x, y, d = cmd fid.write('X%07dY%07dD%02d*\n' % (x+DX, y+DY, d)) @@ -877,7 +877,7 @@ class Job: def findTools(self, diameter): "Find the tools, if any, with the given diameter in inches. There may be more than one!" L = [] - for tool, diam in self.xdiam.items(): + for tool, diam in list(self.xdiam.items()): if diam==diameter: L.append(tool) return L @@ -927,7 +927,7 @@ class Job: # Boogie for ltool in ltools: - if self.xcommands.has_key(ltool): + if ltool in self.xcommands: for cmd in self.xcommands[ltool]: x, y, stop_x, stop_y = cmd new_x = x+DX @@ -965,7 +965,7 @@ class Job: ltools = self.findTools(diameter) for ltool in ltools: - if self.xcommands.has_key(ltool): + if ltool in self.xcommands: for cmd in self.xcommands[ltool]: x, y, stop_x, stop_y = cmd # add metric support (1/1000 mm vs. 1/100,000 inch) @@ -979,7 +979,7 @@ class Job: GAT=config.GAT - if self.apertures.has_key(layername): + if layername in self.apertures: apdict = {}.fromkeys(self.apertures[layername]) apmlist = [GAT[ap].dimx for ap in self.apertures[layername] if GAT[ap].apname=='Macro'] apmdict = {}.fromkeys(apmlist) @@ -990,8 +990,8 @@ class Job: def makeLocalApertureCode(self, layername, AP): "Find or create a layer-specific aperture code to represent the global aperture given" - if AP.code not in self.apxlat[layername].values(): - lastCode = aptable.findHighestApertureCode(self.apxlat[layername].keys()) + if AP.code not in list(self.apxlat[layername].values()): + lastCode = aptable.findHighestApertureCode(list(self.apxlat[layername].keys())) localCode = 'D%d' % (lastCode+1) self.apxlat[layername][localCode] = AP.code @@ -1008,7 +1008,7 @@ class Job: lastAperture = None for cmd in self.commands[layername]: - if type(cmd) == types.TupleType: + if type(cmd) == tuple: # It is a data command: tuple (X, Y, D), all integers, or (X, Y, I, J, D), all integers. if len(cmd)==3: x, y, d = cmd @@ -1180,12 +1180,12 @@ class Job: self.commands[layername] = newcmds def trimGerber(self): - for layername in self.commands.keys(): + for layername in list(self.commands.keys()): self.trimGerberLayer(layername) def trimExcellon(self): "Remove plunge commands that are outside job dimensions" - keys = self.xcommands.keys() + keys = list(self.xcommands.keys()) for toolname in keys: # Remember Excellon is 2.4 format while Gerber data is 2.5 format validList = [tup for tup in self.xcommands[toolname] @@ -1390,10 +1390,10 @@ def rotateJob(job, degrees = 90, flip = 0, firstpass = True): # those apertures which have an orientation: rectangles, ovals, and macros. ToolChangeReplace = {} - for layername in job.apxlat.keys(): + for layername in list(job.apxlat.keys()): J.apxlat[layername] = {} - for ap in job.apxlat[layername].keys(): + for ap in list(job.apxlat[layername].keys()): code = job.apxlat[layername][ap] A = GAT[code] @@ -1438,18 +1438,18 @@ def rotateJob(job, degrees = 90, flip = 0, firstpass = True): offset = 0 else: offset = job.maxy-job.miny - for layername in job.commands.keys(): + for layername in list(job.commands.keys()): J.commands[layername] = [] J.apertures[layername] = [] for cmd in job.commands[layername]: # Is it a drawing command? - if type(cmd) is types.TupleType: + if type(cmd) is tuple: if len(cmd)==3: - x, y, d = map(__builtin__.int, cmd) + x, y, d = list(map(builtins.int, cmd)) II=JJ=None else: - x, y, II, JJ, d, signed = map(__builtin__.int, cmd) # J is already used as Job object + x, y, II, JJ, d, signed = list(map(builtins.int, cmd)) # J is already used as Job object else: # No, must be a string indicating aperture change, G-code, or RS274-X command. if cmd[0] in ('G', '%'): @@ -1497,13 +1497,13 @@ def rotateJob(job, degrees = 90, flip = 0, firstpass = True): J.commands[layername].append((newx,newy,d)) if 0: - print job.minx, job.miny, offset - print layername - print J.commands[layername] + print(job.minx, job.miny, offset) + print(layername) + print(J.commands[layername]) # Finally, rotate drills. Offset is in hundred-thousandths (2.5) while Excellon # data is in 2.4 format. - for tool in job.xcommands.keys(): + for tool in list(job.xcommands.keys()): J.xcommands[tool] = [] for x,y,stop_x,stop_y in job.xcommands[tool]: diff --git a/gerbmerge/makestroke.py b/gerbmerge/makestroke.py index 551ef7f..4883eeb 100644 --- a/gerbmerge/makestroke.py +++ b/gerbmerge/makestroke.py @@ -93,7 +93,7 @@ def writeChar(fid, c, X, Y, degrees): try: glyph = strokes.StrokeMap[c] except: - raise RuntimeError, 'No glyph for character %s' % hex(ord(c)) + raise RuntimeError('No glyph for character %s' % hex(ord(c))) writeGlyph(fid, glyph, X, Y, degrees, c) @@ -140,7 +140,7 @@ if __name__=="__main__": s = string.digits+string.letters+string.punctuation #s = "The quick brown fox jumped over the lazy dog!" - fid = file('test.ger','wt') + fid = open('test.ger','wt') fid.write("""G75* G70* %OFA0B0*% diff --git a/gerbmerge/parselayout.py b/gerbmerge/parselayout.py index 0a8d0e6..9bfb3fa 100644 --- a/gerbmerge/parselayout.py +++ b/gerbmerge/parselayout.py @@ -184,7 +184,7 @@ def findJob(jobname, rotatedFlipped, Jobs=config.Jobs): try: - for existingjob in Jobs.keys(): + for existingjob in list(Jobs.keys()): if existingjob.lower() == fullname.lower(): ## job names are case insensitive job = Jobs[existingjob] return jobs.JobLayout(job) @@ -194,13 +194,13 @@ def findJob(jobname, rotatedFlipped, Jobs=config.Jobs): # Perhaps we just don't have a rotated or flipped job yet if rotatedFlipped[0] or rotatedFlipped[1]: try: - for existingjob in Jobs.keys(): + for existingjob in list(Jobs.keys()): if existingjob.lower() == jobname.lower(): ## job names are case insensitive job = Jobs[existingjob] except: - raise RuntimeError, "Job name '%s' not found" % jobname + raise RuntimeError("Job name '%s' not found" % jobname) else: - raise RuntimeError, "Job name '%s' not found" % jobname + raise RuntimeError("Job name '%s' not found" % jobname) # Make a rotated/flipped job job = jobs.rotateJob(job, rotatedFlipped[0], rotatedFlipped[1]) @@ -228,14 +228,14 @@ def parseJobSpec(spec, data): elif rotation == "Rotate270": rotated = 270 else: - raise RuntimeError, "Unsupported rotation: %s" % rotation + raise RuntimeError("Unsupported rotation: %s" % rotation) else: rotated = 0 return findJob(jobname, [rotated, 0]) else: - raise RuntimeError, "Matrix panels not yet supported" + raise RuntimeError("Matrix panels not yet supported") def parseColSpec(spec, data): jobs = Col() @@ -302,9 +302,9 @@ def parseLayoutFile(fname): """ try: - fid = file(fname, 'rt') - except Exception, detail: - raise RuntimeError, "Unable to open layout file: %s\n %s" % (fname, str(detail)) + fid = open(fname, 'rt') + except Exception as detail: + raise RuntimeError("Unable to open layout file: %s\n %s" % (fname, str(detail))) data = fid.read() fid.close() @@ -312,16 +312,16 @@ def parseLayoutFile(fname): # Replace all CR's in data with nothing, to convert DOS line endings # to unix format (all LF's). - data = string.replace(data, '\x0D', '') + data = str.replace(data, '\x0D', '') tree = parser.parse(data) # Last element of tree is number of characters parsed if not tree[0]: - raise RuntimeError, "Layout file cannot be parsed" + raise RuntimeError("Layout file cannot be parsed") if tree[2] != len(data): - raise RuntimeError, "Parse error at character %d in layout file" % tree[2] + raise RuntimeError("Parse error at character %d in layout file" % tree[2]) Rows = [] for rowspec in tree[1]: @@ -333,7 +333,7 @@ def parseLayoutFile(fname): return Rows if __name__=="__main__": - fid = file(sys.argv[1]) + fid = open(sys.argv[1]) testdata = fid.read() fid.close() diff --git a/gerbmerge/placement.py b/gerbmerge/placement.py index 1293453..407b809 100644 --- a/gerbmerge/placement.py +++ b/gerbmerge/placement.py @@ -52,11 +52,11 @@ class Placement: def write(self, fname): """Write placement to a file""" - fid = file(fname, 'wt') + fid = open(fname, 'wt') for job in self.jobs: fid.write('%s %.3f %.3f\n' % (job.job.name, job.x, job.y)) # added; thought it would be useful to know - print "job locations: job - %s x,y(%f,%f)" % (job.job.name, job.x, job.y) + print("job locations: job - %s x,y(%f,%f)" % (job.job.name, job.x, job.y)) fid.close() def addFromFile(self, fname, Jobs): @@ -65,9 +65,9 @@ class Placement: comment = re.compile(r'\s*(?:#.+)?$') try: - fid = file(fname, 'rt') + fid = open(fname, 'rt') except: - print 'Unable to open placement file: "%s"' % fname + print('Unable to open placement file: "%s"' % fname) sys.exit(1) lines = fid.readlines() @@ -78,7 +78,7 @@ class Placement: match = pat.match(line) if not match: - print 'Cannot interpret placement line in placement file:\n %s' % line + print('Cannot interpret placement line in placement file:\n %s' % line) sys.exit(1) jobname, X, Y = match.groups() @@ -86,7 +86,7 @@ class Placement: X = float(X) Y = float(Y) except: - print 'Illegal (X,Y) co-ordinates in placement file:\n %s' % line + print('Illegal (X,Y) co-ordinates in placement file:\n %s' % line) sys.exit(1) # rotated or flipped diff --git a/gerbmerge/schwartz.py b/gerbmerge/schwartz.py index e243961..dc9f5ce 100644 --- a/gerbmerge/schwartz.py +++ b/gerbmerge/schwartz.py @@ -18,9 +18,9 @@ def schwartz(List, Metric): def pairing(element, M = Metric): return (M(element), element) - paired = map(pairing, List) + paired = list(map(pairing, List)) paired.sort() - return map(stripit, paired) + return list(map(stripit, paired)) def stripit2(pair): return pair[0] @@ -31,8 +31,8 @@ def schwartz2(List, Metric): def pairing(element, M = Metric): return (M(element), element) - paired = map(pairing, List) + paired = list(map(pairing, List)) paired.sort() - theList = map(stripit, paired) - theMetrics = map(stripit2, paired) + theList = list(map(stripit, paired)) + theMetrics = list(map(stripit2, paired)) return (theList, theMetrics) diff --git a/gerbmerge/scoring.py b/gerbmerge/scoring.py index ecb2b80..0d73659 100644 --- a/gerbmerge/scoring.py +++ b/gerbmerge/scoring.py @@ -177,7 +177,7 @@ def mergeLines(Lines): # Extend horizontal lines NewHLines = {} - for yval,lines in HLines.items(): + for yval,lines in list(HLines.items()): # yval is the Y ordinate of this group of lines. lines is the set of all # lines with this Y ordinate. NewHLines[yval] = [] @@ -199,7 +199,7 @@ def mergeLines(Lines): # Extend vertical lines NewVLines = {} - for xval,lines in VLines.items(): + for xval,lines in list(VLines.items()): # xval is the X ordinate of this group of lines. lines is the set of all # lines with this X ordinate. NewVLines[xval] = [] @@ -228,7 +228,7 @@ def mergeLines(Lines): # or within each other. We will have to sort all horizontal lines by their # Y ordinates and group them according to Y ordinates that are close enough # to each other. - yvals = HLines.keys() + yvals = list(HLines.keys()) clusters = clusterOrdinates(yvals) # A list of clustered tuples containing yvals for cluster in clusters: @@ -240,7 +240,7 @@ def mergeLines(Lines): # Y ordinate. Merge them together. NewHLines.extend(mergeHLines(clusterLines)) - xvals = VLines.keys() + xvals = list(VLines.keys()) clusters = clusterOrdinates(xvals) for cluster in clusters: clusterLines = [] diff --git a/gerbmerge/tilesearch1.py b/gerbmerge/tilesearch1.py index dc03510..e3022b7 100644 --- a/gerbmerge/tilesearch1.py +++ b/gerbmerge/tilesearch1.py @@ -19,11 +19,11 @@ import gerbmerge _StartTime = 0.0 # Start time of tiling _CkpointTime = 0.0 # Next time to print stats -_Placements = 0L # Number of placements attempted -_PossiblePermutations = 0L # Number of different ways of ordering jobs -_Permutations = 0L # Number of different job orderings already computed +_Placements = 0 # Number of placements attempted +_PossiblePermutations = 0 # Number of different ways of ordering jobs +_Permutations = 0 # Number of different job orderings already computed _TBestTiling = None # Best tiling so far -_TBestScore = float(sys.maxint) # Smallest area so far +_TBestScore = float(sys.maxsize) # Smallest area so far _PrintStats = 1 # Print statistics every 3 seconds def printTilingStats(): @@ -42,11 +42,11 @@ def printTilingStats(): # add metric support (1/1000 mm vs. 1/100,000 inch) if config.Config['measurementunits'] == 'inch': - print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \ - (percent, _Permutations, _Placements, area, utilization), + print("\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \ + (percent, _Permutations, _Placements, area, utilization), end=' ') else: - print "\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. mm / Best utilization: %.1f%%" % \ - (percent, _Permutations, _Placements, area, utilization), + print("\r %5.2f%% complete / %ld/%ld Perm/Place / Smallest area: %.1f sq. mm / Best utilization: %.1f%%" % \ + (percent, _Permutations, _Placements, area, utilization), end=' ') if gerbmerge.GUI is not None: @@ -86,7 +86,7 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config): global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore, _Permutations, _PrintStats if not TSoFar: - return (None, float(sys.maxint)) + return (None, float(sys.maxsize)) if not Jobs: # Update the best tiling and score. If the new tiling matches @@ -118,12 +118,12 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config): remaining_jobs = Jobs[:job_ix]+Jobs[job_ix+1:] if 0: - print "Level %d (%s)" % (level, job.name) + print("Level %d (%s)" % (level, job.name)) TSoFar.joblist() for J in remaining_jobs: - print J[2].name, ", ", - print - print '-'*75 + print(J[2].name, ", ", end=' ') + print() + print('-'*75) # Construct add-points for the non-rotated and rotated job. # As an optimization, do not construct add-points for the rotated @@ -154,7 +154,7 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config): # Premature prune due to not being able to put this job anywhere. We # have pruned off 2^M permutations where M is the length of the remaining # jobs. - _Permutations += 2L**len(remaining_jobs) + _Permutations += 2**len(remaining_jobs) if addpoints2: for ix in addpoints2: @@ -170,7 +170,7 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config): # Premature prune due to not being able to put this job anywhere. We # have pruned off 2^M permutations where M is the length of the remaining # jobs. - _Permutations += 2L**len(remaining_jobs) + _Permutations += 2**len(remaining_jobs) # If we've been at this for 3 seconds, print some status information if _PrintStats and time.time() > _CkpointTime: @@ -185,9 +185,9 @@ def _tile_search1(Jobs, TSoFar, firstAddPoint, cfg=config.Config): # end for each job in job list def factorial(N): - if (N <= 1): return 1L + if (N <= 1): return 1 - prod = long(N) + prod = int(N) while (N > 2): N -= 1 prod *= N @@ -198,10 +198,10 @@ def initialize(printStats=1): global _StartTime, _CkpointTime, _Placements, _TBestTiling, _TBestScore, _Permutations, _PossiblePermutations, _PrintStats _PrintStats = printStats - _Placements = 0L - _Permutations = 0L + _Placements = 0 + _Permutations = 0 _TBestTiling = None - _TBestScore = float(sys.maxint) + _TBestScore = float(sys.maxsize) def tile_search1(Jobs, X, Y): """Wrapper around _tile_search1 to handle keyboard interrupt, etc.""" @@ -215,36 +215,36 @@ def tile_search1(Jobs, X, Y): # This is assuming all jobs are unique and each job has a rotation (i.e., is not # square). Practically, these assumptions make no difference because the software # currently doesn't optimize for cases of repeated jobs. - _PossiblePermutations = (2L**len(Jobs))*factorial(len(Jobs)) + _PossiblePermutations = (2**len(Jobs))*factorial(len(Jobs)) #print "Possible permutations:", _PossiblePermutations - print '='*70 - print "Starting placement using exhaustive search." - print "There are %ld possible permutations..." % _PossiblePermutations, + print('='*70) + print("Starting placement using exhaustive search.") + print("There are %ld possible permutations..." % _PossiblePermutations, end=' ') if _PossiblePermutations < 1e4: - print "this'll take no time at all." + print("this'll take no time at all.") elif _PossiblePermutations < 1e5: - print "surf the web for a few minutes." + print("surf the web for a few minutes.") elif _PossiblePermutations < 1e6: - print "take a long lunch." + print("take a long lunch.") elif _PossiblePermutations < 1e7: - print "come back tomorrow." + print("come back tomorrow.") else: - print "don't hold your breath." - print "Press Ctrl-C to stop and use the best placement so far." - print "Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100) + print("don't hold your breath.") + print("Press Ctrl-C to stop and use the best placement so far.") + print("Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100)) try: _tile_search1(Jobs, tiling.Tiling(X,Y), 1) printTilingStats() - print + print() except KeyboardInterrupt: printTilingStats() - print - print "Interrupted." + print() + print("Interrupted.") computeTime = time.time() - _StartTime - print "Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime) - print '='*70 + print("Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime)) + print('='*70) return _TBestTiling diff --git a/gerbmerge/tilesearch2.py b/gerbmerge/tilesearch2.py index ca8396f..9af1fb4 100644 --- a/gerbmerge/tilesearch2.py +++ b/gerbmerge/tilesearch2.py @@ -21,9 +21,9 @@ import gerbmerge _StartTime = 0.0 # Start time of tiling _CkpointTime = 0.0 # Next time to print stats -_Placements = 0L # Number of placements attempted +_Placements = 0 # Number of placements attempted _TBestTiling = None # Best tiling so far -_TBestScore = float(sys.maxint) # Smallest area so far +_TBestScore = float(sys.maxsize) # Smallest area so far def printTilingStats(): global _CkpointTime @@ -38,11 +38,11 @@ def printTilingStats(): # add metric support (1/1000 mm vs. 1/100,000 inch) if config.Config['measurementunits'] == 'inch': - print "\r %ld placements / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \ - (_Placements, area, utilization), + print("\r %ld placements / Smallest area: %.1f sq. in. / Best utilization: %.1f%%" % \ + (_Placements, area, utilization), end=' ') else: - print "\r %ld placements / Smallest area: %.1f sq. mm / Best utilization: %.0f%%" % \ - (_Placements, area, utilization), + print("\r %ld placements / Smallest area: %.1f sq. mm / Best utilization: %.0f%%" % \ + (_Placements, area, utilization), end=' ') if gerbmerge.GUI is not None: sys.stdout.flush() @@ -64,7 +64,7 @@ def _tile_search2(Jobs, X, Y, cfg=config.Config): # Must escape with Ctrl-C while 1: T = tiling.Tiling(X,Y) - joborder = r.sample(range(N), N) + joborder = r.sample(list(range(N)), N) minInletSize = tiling.minDimension(Jobs) @@ -127,32 +127,32 @@ def tile_search2(Jobs, X, Y): _StartTime = time.time() _CkpointTime = _StartTime + 3 - _Placements = 0L + _Placements = 0 _TBestTiling = None - _TBestScore = float(sys.maxint) + _TBestScore = float(sys.maxsize) - print '='*70 + print('='*70) if (config.Config['searchtimeout'] > 0): - print "Starting random placement trials. You can press Ctrl-C to" - print "stop the process and use the best placement so far, or wait" - print "for the automatic timeout in %i seconds." % config.Config['searchtimeout'] + print("Starting random placement trials. You can press Ctrl-C to") + print("stop the process and use the best placement so far, or wait") + print("for the automatic timeout in %i seconds." % config.Config['searchtimeout']) else: - print "Starting random placement trials. You must press Ctrl-C to" - print "stop the process and use the best placement so far." - print "You can specify a timeout by setting 'SearchTimeout' in Layout.cfg" - print "Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100) + print("Starting random placement trials. You must press Ctrl-C to") + print("stop the process and use the best placement so far.") + print("You can specify a timeout by setting 'SearchTimeout' in Layout.cfg") + print("Estimated maximum possible utilization is %.1f%%." % (tiling.maxUtilization(Jobs)*100)) try: _tile_search2(Jobs, X, Y) printTilingStats() - print + print() except KeyboardInterrupt: printTilingStats() - print - print "Interrupted." + print() + print("Interrupted.") computeTime = time.time() - _StartTime - print "Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime) - print '='*70 + print("Computed %ld placements in %d seconds / %.1f placements/second" % (_Placements, computeTime, _Placements/computeTime)) + print('='*70) return _TBestTiling diff --git a/gerbmerge/tiling.py b/gerbmerge/tiling.py index 8fc83be..16236fc 100644 --- a/gerbmerge/tiling.py +++ b/gerbmerge/tiling.py @@ -319,7 +319,7 @@ mirrored-L corner __ | | def bounds(self): """Return 2-tuple ((minX, minY), (maxX, maxY)) of rectangular region defined by all jobs""" - minX = minY = float(sys.maxint) + minX = minY = float(sys.maxsize) maxX = maxY = 0.0 for bl,tr,job in self.jobs: @@ -368,7 +368,7 @@ def maxUtilization(Jobs): # Utility function to compute the minimum dimension along any axis of all jobs. # Used to remove inlets. def minDimension(Jobs): - M = float(sys.maxint) + M = float(sys.maxsize) for Xdim,Ydim,job,rjob in Jobs: M = min(M,Xdim) M = min(M,Ydim)