From dfb10943182de660f4cda5d4bf01e41c989a510f Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 19 Jan 2017 15:48:32 -0800 Subject: [PATCH] Path logging interface and bulk transfer of HoldingTags dressup to use module. --- src/Mod/Path/CMakeLists.txt | 2 + .../PathScripts/PathDressupHoldingTags.py | 124 +++++------ src/Mod/Path/PathScripts/PathLog.py | 163 +++++++++++++++ src/Mod/Path/PathTests/TestPathLog.py | 195 ++++++++++++++++++ src/Mod/Path/PathTests/TestPathPost.py | 1 - src/Mod/Path/TestPathApp.py | 3 +- 6 files changed, 419 insertions(+), 69 deletions(-) create mode 100644 src/Mod/Path/PathScripts/PathLog.py create mode 100644 src/Mod/Path/PathTests/TestPathLog.py diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index aecf79d898..3068df25c9 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -39,6 +39,7 @@ SET(PathScripts_SRCS PathScripts/PathJob.py PathScripts/PathKurveUtils.py PathScripts/PathLoadTool.py + PathScripts/PathLog.py PathScripts/PathMillFace.py PathScripts/PathPlane.py PathScripts/PathPocket.py @@ -79,6 +80,7 @@ SET(PathScripts_SRCS PathTests/TestPathDepthParams.py PathTests/TestPathDressupHoldingTags.py PathTests/TestPathGeom.py + PathTests/TestPathLog.py PathTests/TestPathPost.py PathTests/__init__.py PathTests/test_linuxcnc_00.ngc diff --git a/src/Mod/Path/PathScripts/PathDressupHoldingTags.py b/src/Mod/Path/PathScripts/PathDressupHoldingTags.py index 62666c591d..df498396cf 100644 --- a/src/Mod/Path/PathScripts/PathDressupHoldingTags.py +++ b/src/Mod/Path/PathScripts/PathDressupHoldingTags.py @@ -27,14 +27,12 @@ import Draft import DraftGeomUtils import DraftGui import Path +import PathScripts.PathLog as PathLog import PathScripts.PathPreferencesPathDressup as PathPreferencesPathDressup import Part import copy import math -import cProfile -import time - from DraftGui import todo from PathScripts import PathUtils from PathScripts.PathGeom import * @@ -48,16 +46,13 @@ from pivy import coin def translate(text, context = "PathDressup_HoldingTags", disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) -debugDressup = False -def debugPrint(msg): - if debugDressup: - print(msg) +#PathLog.setLevel(PathLog.Level.DEBUG) def debugEdge(edge, prefix, force = False): - pf = edge.valueAt(edge.FirstParameter) - pl = edge.valueAt(edge.LastParameter) - if force or debugDressup: + if force or PathLog.getLevel() == PathLog.Level.DEBUG: + pf = edge.valueAt(edge.FirstParameter) + pl = edge.valueAt(edge.LastParameter) if type(edge.Curve) == Part.Line or type(edge.Curve) == Part.LineSegment: print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pl.x, pl.y, pl.z)) else: @@ -65,7 +60,7 @@ def debugEdge(edge, prefix, force = False): print("%s %s((%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f) - (%.2f, %.2f, %.2f))" % (prefix, type(edge.Curve), pf.x, pf.y, pf.z, pm.x, pm.y, pm.z, pl.x, pl.y, pl.z)) def debugMarker(vector, label, color = None, radius = 0.5): - if debugDressup: + if PathLog.getLevel() == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Sphere", label) obj.Label = label obj.Radius = radius @@ -74,7 +69,7 @@ def debugMarker(vector, label, color = None, radius = 0.5): obj.ViewObject.ShapeColor = color def debugCylinder(vector, r, height, label, color = None): - if debugDressup: + if PathLog.getLevel() == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Cylinder", label) obj.Label = label obj.Radius = r @@ -85,7 +80,7 @@ def debugCylinder(vector, r, height, label, color = None): obj.ViewObject.ShapeColor = color def debugCone(vector, r1, r2, height, label, color = None): - if debugDressup: + if PathLog.getLevel() == PathLog.Level.DEBUG: obj = FreeCAD.ActiveDocument.addObject("Part::Cone", label) obj.Label = label obj.Radius1 = r1 @@ -162,7 +157,7 @@ class HoldingTagsPreferences: class Tag: def __init__(self, x, y, width, height, angle, radius, enabled=True): - debugPrint("Tag(%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d)" % (x, y, width, height, angle, radius, enabled)) + PathLog.track("%.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d" % (x, y, width, height, angle, radius, enabled)) self.x = x self.y = y self.width = math.fabs(width) @@ -198,7 +193,7 @@ class Tag: self.isSquare = True self.solid = Part.makeCylinder(r1, height) radius = min(min(self.radius, r1), self.height) - debugPrint("Part.makeCone(%f, %f)" % (r1, height)) + PathLog.debug("Part.makeCone(%f, %f)" % (r1, height)) elif self.angle > 0.0 and height > 0.0: # cone rad = math.radians(self.angle) @@ -215,29 +210,29 @@ class Tag: height = r1 * tangens * 1.01 self.actualHeight = height self.r2 = r2 - debugPrint("Part.makeCone(%f, %f, %f)" % (r1, r2, height)) + PathLog.debug("Part.makeCone(%f, %f, %f)" % (r1, r2, height)) self.solid = Part.makeCone(r1, r2, height) else: # degenerated case - no tag - debugPrint("Part.makeSphere(%f / 10000)" % (r1)) + PathLog.debug("Part.makeSphere(%f / 10000)" % (r1)) self.solid = Part.makeSphere(r1 / 10000) if not R == 0: # testing is easier if the solid is not rotated angle = -PathGeom.getAngle(self.originAt(0)) * 180 / math.pi - debugPrint("solid.rotate(%f)" % angle) + PathLog.debug("solid.rotate(%f)" % angle) self.solid.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), angle) - debugPrint("solid.translate(%s)" % self.originAt(z)) + PathLog.debug("solid.translate(%s)" % self.originAt(z)) self.solid.translate(self.originAt(z - 0.01 * self.actualHeight)) self.realRadius = radius if radius != 0: - debugPrint("makeFillet(%.4f)" % radius) + PathLog.debug("makeFillet(%.4f)" % radius) self.solid = self.solid.makeFillet(radius, [self.solid.Edges[0]]) def filterIntersections(self, pts, face): if type(face.Surface) == Part.Cone or type(face.Surface) == Part.Cylinder or type(face.Surface) == Part.Toroid: - #print("it's a cone/cylinder, checking z") + PathLog.track("it's a cone/cylinder, checking z") return filter(lambda pt: pt.z >= self.bottom() and pt.z <= self.top(), pts) if type(face.Surface) == Part.Plane: - #print("it's a plane, checking R") + PathLog.track("it's a plane, checking R") c = face.Edges[0].Curve if (type(c) == Part.Circle): return filter(lambda pt: (pt - c.Center).Length <= c.Radius or PathGeom.isRoughly((pt - c.Center).Length, c.Radius), pts) @@ -312,9 +307,9 @@ class MapWireToTag: self.edges = [] self.entry = i if tail: - debugPrint("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter))) + PathLog.debug("MapWireToTag(%s - %s)" % (i, tail.valueAt(tail.FirstParameter))) else: - debugPrint("MapWireToTag(%s - )" % i) + PathLog.debug("MapWireToTag(%s - )" % i) self.complete = False self.haveProblem = False @@ -342,11 +337,11 @@ class MapWireToTag: def cleanupEdges(self, edges): # want to remove all edges from the wire itself, and all internal struts - #print("+cleanupEdges") - #print(" edges:") + PathLog.track("+cleanupEdges") + PathLog.debug(" edges:") for e in edges: debugEdge(e, ' ') - #print(":") + PathLog.debug(":") self.edgesCleanup = [copy.copy(edges)] # remove any edge that has a point inside the tag solid @@ -413,7 +408,7 @@ class MapWireToTag: return edges def orderAndFlipEdges(self, edges): - #print("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z)) + PathLog.track("entry(%.2f, %.2f, %.2f), exit(%.2f, %.2f, %.2f)" % (self.entry.x, self.entry.y, self.entry.z, self.exit.x, self.exit.y, self.exit.z)) self.edgesOrder = [] outputEdges = [] p0 = self.entry @@ -450,11 +445,11 @@ class MapWireToTag: debugEdge(e, ' ', False) raise ValueError("No connection to %s" % (p0)) elif lastP: - debugPrint("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z)) + PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)" % (p0.x, p0.y, p0.z, lastP.x, lastP.y, lastP.z)) else: - debugPrint("xxxxxx (%.2f, %.2f, %.2f) -" % (p0.x, p0.y, p0.z)) + PathLog.debug("xxxxxx (%.2f, %.2f, %.2f) -" % (p0.x, p0.y, p0.z)) lastP = p0 - #print("-cleanupEdges") + PathLog.track("-") return outputEdges def isStrut(self, edge): @@ -546,14 +541,9 @@ class _RapidEdges: return True return False - def debugPrint(self): - debugPrint('rapid:') - for r in self.rapid: - debugEdge(r, ' ') - class PathData: def __init__(self, obj): - debugPrint("PathData(%s)" % obj.Base.Name) + PathLog.track(obj.Base.Name) self.obj = obj self.wire, rapid = PathGeom.wireForPath(obj.Base.Path) self.rapid = _RapidEdges(rapid) @@ -593,7 +583,7 @@ class PathData: return (edges[0], edges[-1]) def generateTags(self, obj, count, width=None, height=None, angle=None, radius=None, spacing=None): - debugPrint("generateTags(%s, %s, %s, %s, %s)" % (count, width, height, angle, spacing)) + PathLog.track(count, width, height, angle, spacing) #for e in self.base.Edges: # debugMarker(e.Vertexes[0].Point, 'base', (0.0, 1.0, 1.0), 0.2) @@ -613,7 +603,7 @@ class PathData: startIndex = 0 for i in range(0, len(self.base.Edges)): edge = self.base.Edges[i] - debugPrint(' %d: %.2f' % (i, edge.Length)) + PathLog.debug(' %d: %.2f' % (i, edge.Length)) if edge.Length == longestEdge.Length: startIndex = i break @@ -628,10 +618,10 @@ class PathData: minLength = min(2. * W, longestEdge.Length) - debugPrint("length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f) minLength=%.2f" % (self.base.Length, shortestEdge.Length, shortestEdge.Length/self.base.Length, longestEdge.Length, longestEdge.Length / self.base.Length, minLength)) - debugPrint(" start: index=%-2d count=%d (length=%.2f, distance=%.2f)" % (startIndex, startCount, startEdge.Length, tagDistance)) - debugPrint(" -> lastTagLength=%.2f)" % lastTagLength) - debugPrint(" -> currentLength=%.2f)" % currentLength) + PathLog.debug("length=%.2f shortestEdge=%.2f(%.2f) longestEdge=%.2f(%.2f) minLength=%.2f" % (self.base.Length, shortestEdge.Length, shortestEdge.Length/self.base.Length, longestEdge.Length, longestEdge.Length / self.base.Length, minLength)) + PathLog.debug(" start: index=%-2d count=%d (length=%.2f, distance=%.2f)" % (startIndex, startCount, startEdge.Length, tagDistance)) + PathLog.debug(" -> lastTagLength=%.2f)" % lastTagLength) + PathLog.debug(" -> currentLength=%.2f)" % currentLength) edgeDict = { startIndex: startCount } @@ -646,7 +636,7 @@ class PathData: for (i, count) in edgeDict.iteritems(): edge = self.base.Edges[i] - debugPrint(" %d: %d" % (i, count)) + PathLog.debug(" %d: %d" % (i, count)) #debugMarker(edge.Vertexes[0].Point, 'base', (1.0, 0.0, 0.0), 0.2) #debugMarker(edge.Vertexes[1].Point, 'base', (0.0, 1.0, 0.0), 0.2) if 0 != count: @@ -665,10 +655,10 @@ class PathData: tagCount += 1 lastTagLength += tagDistance if tagCount > 0: - debugPrint(" index=%d -> count=%d" % (index, tagCount)) + PathLog.debug(" index=%d -> count=%d" % (index, tagCount)) edgeDict[index] = tagCount else: - debugPrint(" skipping=%-2d (%.2f)" % (index, edge.Length)) + PathLog.debug(" skipping=%-2d (%.2f)" % (index, edge.Length)) return (currentLength, lastTagLength) @@ -701,7 +691,7 @@ class PathData: ordered.append(t) # disable all tags that are not on the base wire. for tag in tags: - FreeCAD.Console.PrintMessage("Tag #%d not on base wire - disabling\n" % len(ordered)) + PathLog.notice("Tag #%d not on base wire - disabling\n" % len(ordered)) tag.enabled = False ordered.append(tag) return ordered @@ -756,7 +746,7 @@ class ObjectDressup: return True def createPath(self, obj, pathData, tags): - #print("createPath") + PathLog.track() commands = [] lastEdge = 0 lastTag = 0 @@ -776,7 +766,7 @@ class ObjectDressup: mapper = None while edge or lastEdge < len(pathData.edges): - debugPrint("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags))) + PathLog.debug("------- lastEdge = %d/%d.%d/%d" % (lastEdge, lastTag, t, len(tags))) if not edge: edge = pathData.edges[lastEdge] debugEdge(edge, "======= new edge: %d/%d" % (lastEdge, len(pathData.edges))) @@ -836,19 +826,19 @@ class ObjectDressup: if tag.enabled: if prev: if prev.solid.common(tag.solid).Faces: - FreeCAD.Console.PrintMessage("Tag #%d intersects with previous tag - disabling\n" % i) - debugPrint("this tag = %d [%s]" % (i, tag.solid.BoundBox)) + PathLog.notice("Tag #%d intersects with previous tag - disabling\n" % i) + PathLog.debug("this tag = %d [%s]" % (i, tag.solid.BoundBox)) tag.enabled = False elif self.pathData.edges: e = self.pathData.edges[0] p0 = e.valueAt(e.FirstParameter) p1 = e.valueAt(e.LastParameter) if tag.solid.isInside(p0, PathGeom.Tolerance, True) or tag.solid.isInside(p1, PathGeom.Tolerance, True): - FreeCAD.Console.PrintMessage("Tag #%d intersects with starting point - disabling\n" % i) + PathLog.notice("Tag #%d intersects with starting point - disabling\n" % i) tag.enabled = False if tag.enabled: prev = tag - debugPrint("previousTag = %d [%s]" % (i, prev)) + PathLog.debug("previousTag = %d [%s]" % (i, prev)) else: disabled.append(i) tags.append(tag) @@ -856,6 +846,7 @@ class ObjectDressup: return (tags, positions, disabled) def execute(self, obj): + #import cProfile #pr = cProfile.Profile() #pr.enable() self.doExecute(obj) @@ -881,7 +872,7 @@ class ObjectDressup: if hasattr(obj, "Positions"): self.tags, positions, disabled = self.createTagsPositionDisabled(obj, obj.Positions, obj.Disabled) if obj.Disabled != disabled: - debugPrint("Updating properties.... %s vs. %s" % (obj.Disabled, disabled)) + PathLog.debug("Updating properties.... %s vs. %s" % (obj.Disabled, disabled)) obj.Positions = positions obj.Disabled = disabled @@ -894,11 +885,11 @@ class ObjectDressup: def processTags(self, obj): tagID = 0 - if debugDressup: + if PathLog.getLevel() == PathLog.Level.DEBUG: for tag in self.tags: tagID += 1 if tag.enabled: - debugPrint("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ)) + PathLog.debug("x=%s, y=%s, z=%s" % (tag.x, tag.y, self.pathData.minZ)) #debugMarker(FreeCAD.Vector(tag.x, tag.y, self.pathData.minZ), "tag-%02d" % tagID , (1.0, 0.0, 1.0), 0.5) #if tag.angle != 90: # debugCone(tag.originAt(self.pathData.minZ), tag.r1, tag.r2, tag.actualHeight, "tag-%02d" % tagID) @@ -906,15 +897,14 @@ class ObjectDressup: # debugCylinder(tag.originAt(self.pathData.minZ), tag.fullWidth()/2, tag.actualHeight, "tag-%02d" % tagID) obj.Path = self.createPath(obj, self.pathData, self.tags) - #print("execute - done") def setup(self, obj, generate=False): - debugPrint("setup") + PathLog.debug("setup") self.obj = obj try: pathData = PathData(obj) except ValueError: - FreeCAD.Console.PrintError(translate("Cannot insert holding tags for this path - please select a Profile path\n")) + PathLog.error(translate("Cannot insert holding tags for this path - please select a Profile path\n")) return None self.toolRadius = 5 @@ -938,7 +928,7 @@ class ObjectDressup: return self.pathData def setXyEnabled(self, triples): - debugPrint("setXyEnabled") + PathLog.track() if not hasattr(self, 'pathData'): self.setup(self.obj) positions = [] @@ -1038,7 +1028,7 @@ class TaskPanel: self.obj.Proxy.setXyEnabled(tags) def updateTagsView(self): - #print("updateTagsView") + PathLog.track() self.formTags.lwTags.blockSignals(True) self.formTags.lwTags.clear() for i, pos in enumerate(self.obj.Positions): @@ -1066,7 +1056,7 @@ class TaskPanel: self.obj.Proxy.execute(self.obj) self.updateTagsView() - #if debugDressup: + #if PathLog.getLevel() == PathLog.Level.DEBUG: # # this causes a big of an echo and a double click on the spin buttons, don't know why though # FreeCAD.ActiveDocument.recompute() @@ -1359,7 +1349,7 @@ class ViewProviderDressup: self.tags = tags def selectTag(self, index): - #print("selectTag(%s)" % index) + PathLog.track(index) for i, tag in enumerate(self.tags): tag.setSelected(i == index) @@ -1401,14 +1391,14 @@ class CommandPathDressupHoldingTags: # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelection() if len(selection) != 1: - FreeCAD.Console.PrintError(translate("Please select one path object\n")) + PathLog.error(translate("Please select one path object\n")) return baseObject = selection[0] if not baseObject.isDerivedFrom("Path::Feature"): - FreeCAD.Console.PrintError(translate("The selected object is not a path\n")) + PathLog.error(translate("The selected object is not a path\n")) return if baseObject.isDerivedFrom("Path::FeatureCompoundPython"): - FreeCAD.Console.PrintError(translate("Please select a Profile object")) + PathLog.error(translate("Please select a Profile object")) return # everything ok! @@ -1430,4 +1420,4 @@ if FreeCAD.GuiUp: # register the FreeCAD command FreeCADGui.addCommand('PathDressup_HoldingTags', CommandPathDressupHoldingTags()) -FreeCAD.Console.PrintLog("Loading PathDressupHoldingTags... done\n") +PathLog.notice("Loading PathDressupHoldingTags... done\n") diff --git a/src/Mod/Path/PathScripts/PathLog.py b/src/Mod/Path/PathScripts/PathLog.py new file mode 100644 index 0000000000..193712768e --- /dev/null +++ b/src/Mod/Path/PathScripts/PathLog.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2016 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import FreeCAD +import os +import traceback + +class Level: + """Enumeration of log levels, used for setLevel and getLevel.""" + RESET = -1 + ERROR = 0 + WARNING = 1 + NOTICE = 2 + INFO = 3 + DEBUG = 4 + + _names = { ERROR: 'ERROR', WARNING: 'WARNING', NOTICE: 'NOTICE', INFO: 'INFO', DEBUG: 'DEBUG' } + + @classmethod + def toString(cls, level): + return cls._names.get(level, 'UNKNOWN') + +_defaultLogLevel = Level.NOTICE +_moduleLogLevel = { } +_useConsole = True +_trackModule = { } +_trackAll = False + +def logToConsole(yes): + """(boolean) - if set to True (default behaviour) log messages are printed to the console. Otherwise they are printed to stdout.""" + global _useConsole + _useConsole = yes + +def setLevel(level, module = None): + """(level, module = None) + if no module is specified the default log level is set. + Otherwise the module specific log level is changed (use RESET to clear).""" + global _defaultLogLevel + global _moduleLogLevel + if module: + if level == Level.RESET: + if _moduleLogLevel.get(module, -1) != -1: + del _moduleLogLevel[module] + else: + _moduleLogLevel[module] = level + else: + if level == Level.RESET: + _defaultLogLevel = Level.NOTICE + _moduleLogLevel = { } + else: + _defaultLogLevel = level + +def getLevel(module = None): + """(module = None) - return the global (None) or module specific log level.""" + if module: + return _moduleLogLevel.get(module, _defaultLogLevel) + return _defaultLogLevel + +def _caller(): + """internal function to determine the calling module.""" + file, line, func, text = traceback.extract_stack(limit=3)[0] + return os.path.splitext(os.path.basename(file))[0], line, func + +def _log(level, (module, line, func), msg): + """internal function to do the logging""" + if getLevel(module) >= level: + message = "%s.%s: %s" % (module, Level.toString(level), msg) + if _useConsole: + message += "\n" + if level == Level.NOTICE: + FreeCAD.Console.PrintLog(message) + elif level == Level.WARNING: + FreeCAD.Console.PrintWarning(message) + elif level == Level.ERROR: + FreeCAD.Console.PrintError(message) + else: + FreeCAD.Console.PrintMessage(message) + else: + print(message) + return message + return None + +def debug(msg): + """(message)""" + return _log(Level.DEBUG, _caller(), msg) +def info(msg): + """(message)""" + return _log(Level.INFO, _caller(), msg) +def notice(msg): + """(message)""" + return _log(Level.NOTICE, _caller(), msg) +def warning(msg): + """(message)""" + return _log(Level.WARNING, _caller(), msg) +def error(msg): + """(message)""" + return _log(Level.ERROR, _caller(), msg) + +def trackAllModules(boolean): + """(boolean) - if True all modules will be tracked, otherwise tracking is up to the module setting.""" + global _trackAll + _trackAll = boolean + +def untrackAllModules(): + """In addition to stop tracking all modules it also clears the tracking flag for all individual modules.""" + global _trackAll + global _trackModule + _trackAll = False + _trackModule = { } + +def trackModule(module = None): + """(module = None) - start tracking given module, current module if not set.""" + global _trackModule + if module: + _trackModule[module] = True + else: + mod, line, func = _caller() + _trackModule[mod] = True + +def untrackModule(module = None): + """(module = None) - stop tracking given module, current module if not set.""" + global _trackModule + if module and _trackModule.get(module, None): + del _trackModule[module] + elif not module: + mod, line, func = _caller() + if _trackModule.get(mod, None): + del _trackModule[mod] + +def track(*args): + """(....) - call with arguments of current function you want logged if tracking is enabled.""" + module, line, func = _caller() + if _trackAll or _trackModule.get(module, None): + message = "%s(%d).%s(%s)" % (module, line, func, ', '.join([str(arg) for arg in args])) + if _useConsole: + FreeCAD.Console.PrintMessage(message + "\n") + else: + print(message) + return message + return None + + diff --git a/src/Mod/Path/PathTests/TestPathLog.py b/src/Mod/Path/PathTests/TestPathLog.py new file mode 100644 index 0000000000..f9b1c81bb5 --- /dev/null +++ b/src/Mod/Path/PathTests/TestPathLog.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2016 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import PathScripts.PathLog as PathLog +import unittest + +class TestPathLog(unittest.TestCase): + """Some basic tests for the logging framework.""" + + MODULE = 'TestPathLog' # file name without extension + + def setUp(self): + PathLog.setLevel(PathLog.Level.RESET) + PathLog.untrackAllModules() + + def callerFile(self): + return PathLog._caller()[0] + def callerLine(self): + return PathLog._caller()[1] + def callerFunc(self): + return PathLog._caller()[2] + + def test00(self): + """Check for proper module extraction.""" + self.assertEqual(self.callerFile(), self.MODULE) + + def test01(self): + """Check for proper function extraction.""" + self.assertEqual(self.callerFunc(), 'test01') + + def test10(self): + """Verify default log levels is NOTICE.""" + self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE) + + def test11(self): + """Verify setting global log level.""" + PathLog.setLevel(PathLog.Level.DEBUG) + + self.assertEqual(PathLog.getLevel(), PathLog.Level.DEBUG) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG) + + def test12(self): + """Verify setting module log level.""" + PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE) + + self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG) + + def test13(self): + """Verify setting other modul's log level doesn't change this one's.""" + # if this test fails then most likely the global RESET is broken + PathLog.setLevel(PathLog.Level.DEBUG, 'SomeOtherModule') + + self.assertEqual(PathLog.getLevel(), PathLog.Level.NOTICE) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.NOTICE) + + def test14(self): + """Verify resetting log level for module falls back to global level.""" + PathLog.setLevel(PathLog.Level.DEBUG, self.MODULE) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG) + # changing global log level does not affect module + PathLog.setLevel(PathLog.Level.ERROR) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG) + # resetting module log level restores global log level for module + PathLog.setLevel(PathLog.Level.RESET, self.MODULE) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.ERROR) + # changing the global log level will also change the module log level + PathLog.setLevel(PathLog.Level.DEBUG) + self.assertEqual(PathLog.getLevel(self.MODULE), PathLog.Level.DEBUG) + + def test20(self): + """Verify debug logs aren't logged by default.""" + self.assertIsNone(PathLog.debug("this")) + + def test21(self): + """Verify debug logs are logged if log level is set to DEBUG.""" + PathLog.setLevel(PathLog.Level.DEBUG) + self.assertIsNotNone(PathLog.debug("this")) + + def test30(self): + """Verify log level ERROR.""" + PathLog.setLevel(PathLog.Level.ERROR) + self.assertIsNone(PathLog.debug('something')) + self.assertIsNone(PathLog.info('something')) + self.assertIsNone(PathLog.notice('something')) + self.assertIsNone(PathLog.warning('something')) + self.assertIsNotNone(PathLog.error('something')) + + def test31(self): + """Verify log level WARNING.""" + PathLog.setLevel(PathLog.Level.WARNING) + self.assertIsNone(PathLog.debug('something')) + self.assertIsNone(PathLog.info('something')) + self.assertIsNone(PathLog.notice('something')) + self.assertIsNotNone(PathLog.warning('something')) + self.assertIsNotNone(PathLog.error('something')) + + def test32(self): + """Verify log level NOTICE.""" + PathLog.setLevel(PathLog.Level.NOTICE) + self.assertIsNone(PathLog.debug('something')) + self.assertIsNone(PathLog.info('something')) + self.assertIsNotNone(PathLog.notice('something')) + self.assertIsNotNone(PathLog.warning('something')) + self.assertIsNotNone(PathLog.error('something')) + + def test33(self): + """Verify log level INFO.""" + PathLog.setLevel(PathLog.Level.INFO) + self.assertIsNone(PathLog.debug('something')) + self.assertIsNotNone(PathLog.info('something')) + self.assertIsNotNone(PathLog.notice('something')) + self.assertIsNotNone(PathLog.warning('something')) + self.assertIsNotNone(PathLog.error('something')) + + def test34(self): + """Verify log level DEBUG.""" + PathLog.setLevel(PathLog.Level.DEBUG) + self.assertIsNotNone(PathLog.debug('something')) + self.assertIsNotNone(PathLog.info('something')) + self.assertIsNotNone(PathLog.notice('something')) + self.assertIsNotNone(PathLog.warning('something')) + self.assertIsNotNone(PathLog.error('something')) + + def test50(self): + """Verify no tracking by default.""" + self.assertIsNone(PathLog.track('this', 'and', 'that')) + + def test51(self): + """Verify enabling tracking for module results in tracking.""" + PathLog.trackModule() + # Don't want to rely on the line number matching - still want some + # indication that track does the right thing .... + msg = PathLog.track('this', 'and', 'that') + self.assertTrue(msg.startswith(self.MODULE)) + self.assertTrue(msg.endswith('test51(this, and, that)')) + + def test52(self): + """Verify untracking stops tracking.""" + PathLog.trackModule() + self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + PathLog.untrackModule() + self.assertIsNone(PathLog.track('this', 'and', 'that')) + + def test53(self): + """Verify trackAllModules works correctly.""" + PathLog.trackAllModules(True) + self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + PathLog.trackAllModules(False) + self.assertIsNone(PathLog.track('this', 'and', 'that')) + PathLog.trackAllModules(True) + PathLog.trackModule() + self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + PathLog.trackAllModules(False) + self.assertIsNotNone(PathLog.track('this', 'and', 'that')) + + def test60(self): + """Verify track handles no argument.""" + PathLog.trackModule() + msg = PathLog.track() + self.assertTrue(msg.startswith(self.MODULE)) + self.assertTrue(msg.endswith('test60()')) + + def test61(self): + """Verify track handles arbitrary argument types correctly.""" + PathLog.trackModule() + msg = PathLog.track('this', None, 1, 18.25) + self.assertTrue(msg.startswith(self.MODULE)) + self.assertTrue(msg.endswith('test61(this, None, 1, 18.25)')) + + def testzz(self): + """Restoring environment after tests.""" + PathLog.setLevel(PathLog.Level.RESET) diff --git a/src/Mod/Path/PathTests/TestPathPost.py b/src/Mod/Path/PathTests/TestPathPost.py index 0ea6ba81d2..efcdc1a323 100644 --- a/src/Mod/Path/PathTests/TestPathPost.py +++ b/src/Mod/Path/PathTests/TestPathPost.py @@ -74,7 +74,6 @@ class PathPostTestCases(unittest.TestCase): contour.OffsetExtra = 0.0 contour.Direction = 'CW' contour.UseComp = True - contour.PlungeAngle = 90.0 PathScripts.PathUtils.addToJob(contour) PathScripts.PathContour.ObjectContour.setDepths(contour.Proxy, contour) self.doc.recompute() diff --git a/src/Mod/Path/TestPathApp.py b/src/Mod/Path/TestPathApp.py index 074643ad36..f5e3abf0c2 100644 --- a/src/Mod/Path/TestPathApp.py +++ b/src/Mod/Path/TestPathApp.py @@ -24,10 +24,11 @@ import TestApp +from PathTests.TestPathLog import TestPathLog from PathTests.TestPathCore import TestPathCore from PathTests.TestPathPost import PathPostTestCases from PathTests.TestPathGeom import TestPathGeom from PathTests.TestPathDepthParams import depthTestCases -from PathTests.TestPathDressupHoldingTags import TestHoldingTags +#from PathTests.TestPathDressupHoldingTags import TestHoldingTags