From f9276587a7d89db9b47387acdcb19deaea2b08f6 Mon Sep 17 00:00:00 2001 From: Roberto Arista Date: Tue, 30 Jul 2024 15:47:30 +0200 Subject: [PATCH] test automatically generated python code --- scripting/generateDrawbotInit.py | 22 ++++--- scripting/imageObjectCodeExtractor.py | 84 +++++++++++++------------ tests/testAutomaticallyGeneratedCode.py | 26 ++++++++ 3 files changed, 81 insertions(+), 51 deletions(-) create mode 100644 tests/testAutomaticallyGeneratedCode.py diff --git a/scripting/generateDrawbotInit.py b/scripting/generateDrawbotInit.py index 222d96a4..86083c5e 100644 --- a/scripting/generateDrawbotInit.py +++ b/scripting/generateDrawbotInit.py @@ -5,22 +5,24 @@ """ -from pathlib import Path from drawBot.context.tools import drawBotbuiltins from drawBot import _drawBotDrawingTool +from pathlib import Path -if __name__ == '__main__': +INIT_PATH = Path(__file__).parent.parent / "drawBot/__init__.py" + +def generateInitCode(): code = [] for name in _drawBotDrawingTool.__all__: if name.startswith("_"): continue code.append(f"{name} = _drawBotDrawingTool.{name}") - + code.append("") code.append("# directly import FormattedString, BezierPath, and ImageObject as classes") code.append("from drawBot.context.baseContext import FormattedString, BezierPath") code.append("from drawBot.context.tools.imageObject import ImageObject") - + code.append("") code.append("from drawBot.context.tools import drawBotbuiltins") for name in dir(drawBotbuiltins): @@ -28,15 +30,15 @@ continue code.append(f"{name} = drawBotbuiltins.{name}") - initPath = Path( - Path(__file__).parent.parent / "drawBot/__init__.py" - ) - initText = initPath.read_text() + initText = INIT_PATH.read_text() before = [] for eachLine in initText.splitlines(): before.append(eachLine) if eachLine == "# --- section automatically generated --- #": break - with open(initPath, mode="w") as txtFile: - txtFile.write("\n".join(before) + "\n" + "\n".join(code)) + return "\n".join(before) + "\n" + "\n".join(code) + +if __name__ == '__main__': + initCode = generateInitCode() + INIT_PATH.write_text(initCode) diff --git a/scripting/imageObjectCodeExtractor.py b/scripting/imageObjectCodeExtractor.py index 18a0debd..5ef72168 100644 --- a/scripting/imageObjectCodeExtractor.py +++ b/scripting/imageObjectCodeExtractor.py @@ -3,6 +3,9 @@ import AppKit # type: ignore import Quartz # type: ignore +IMAGE_OBJECT_PATH = Path(__file__).parent.parent / "drawBot/context/tools/imageObject.py" +UNIT_TESTS_PATH = Path(__file__).parent.parent / "tests/testImageObject.py" + class CodeWriter: def __init__(self, INDENT=" "): @@ -339,11 +342,11 @@ def pythonifyDescription(description): return description -if __name__ == "__main__": +def generateImageObjectCode() -> tuple[str, str]: code = CodeWriter() unitTests = UnitTestWriter() unitTests.header() - + for filterName in allFilterNames: if filterName in excludeFilterNames: continue @@ -351,11 +354,11 @@ def pythonifyDescription(description): ciFilterAttributes = ciFilter.attributes() doc = CodeWriter() doc.add(AppKit.CIFilter.localizedDescriptionForFilterName_(filterName)) # type: ignore - + args = [] unitTestsArgs = [] inputCode = CodeWriter() - + inputKeys = [ inputKey for inputKey in ciFilter.inputKeys() @@ -368,9 +371,9 @@ def pythonifyDescription(description): ciFilterAttributes.get(x, dict()).get("CIAttributeDefault") is not None ) ) - + attributes = dict() - + if inputKeys or filterName == "CIRandomGenerator": doc.newline() doc.add("**Arguments:**") @@ -383,21 +386,21 @@ def pythonifyDescription(description): info = ciFilterAttributes.get(inputKey) default = info.get("CIAttributeDefault") defaultClass = info.get("CIAttributeClass") - + description = info.get("CIAttributeDescription", "") inputKey = camelCase(inputKey[5:]) arg = inputKey - + if inputKey in toCopy["image"]: arg += ": Self" - + if inputKey in argumentToHint: arg += argumentToHint[inputKey] - + # if filterName == "CIAztecCodeGenerator": # print(inputKeys) # print(ciFilterAttributes) - + if default is not None: if isinstance(default, AppKit.CIVector): # type: ignore if default.count() == 2: @@ -411,22 +414,22 @@ def pythonifyDescription(description): default.valueAtIndex_(i) for i in range(default.count()) ) arg += ": tuple" - + elif isinstance(default, bool): arg += ": bool" - + elif isinstance(default, (AppKit.NSString, str)): # type: ignore default = f"'{default}'" arg += ": str" - + elif isinstance(default, AppKit.NSNumber): # type: ignore default = float(default) arg += ": float" - + elif isinstance(default, AppKit.NSAffineTransform): # type: ignore default = tuple(default.transformStruct()) arg += ": TransformTuple" - + elif isinstance(default, AppKit.CIColor): # type: ignore default = ( default.red(), @@ -435,31 +438,31 @@ def pythonifyDescription(description): default.alpha(), ) arg += ": RGBAColorTuple" - + elif isinstance(default, AppKit.NSData): # type: ignore default = None arg += ": bytes | None" - + elif isinstance(default, type(Quartz.CGColorSpaceCreateDeviceCMYK())): # type: ignore default = None - + else: print(filterName, ciFilterAttributes) raise ValueError(f"We can't parse this default class of `{inputKey}`: {defaultClass}, {default}, {type(default)}") - + arg += f" = {default}" - + if filterName in degreesAngleFilterNames: value = inputKey else: value = converters.get(inputKey, inputKey).format(inputKey=inputKey) docValue = getVariableValue((inputKey, filterName), "a float") attributes[inputKey] = value - + doc.add(f"`{inputKey}` {docValue}. {pythonifyDescription(description)}") args.append(arg) - - + + match inputKey: case inputKey if inputKey.endswith("Image"): value = "sampleImage" @@ -474,7 +477,7 @@ def pythonifyDescription(description): case _: value = default unitTestsArgs.append(f"{inputKey}={value}") - + drawBotFilterName = camelCase(filterName[2:]) code.add( f"def {drawBotFilterName}" @@ -493,36 +496,35 @@ def pythonifyDescription(description): filterDict["isGenerator"] = "True" if filterName.endswith("CodeGenerator"): filterDict["fitImage"] = "True" - + code.addDict("filterDict", filterDict) - + code.add("self._addFilter(filterDict)") code.dedent() code.newline() - + unitTests.add(f"def test_{drawBotFilterName}(self):") unitTests.indent() unitTests.add("img = drawBot.ImageObject()") unitTests.add(f"img.{drawBotFilterName}({', '.join(unitTestsArgs)})") unitTests.newline() unitTests.dedent() - - imageObjectPath = Path( - Path(__file__).parent.parent / "drawBot/context/tools/imageObject.py" - ) - imageObjectText = imageObjectPath.read_text() - + + imageObjectText = IMAGE_OBJECT_PATH.read_text() + beforeFilters = [] for eachLine in imageObjectText.splitlines(): beforeFilters.append(eachLine) if eachLine == " # --- filters ---": break + + imageObjectCode = "\n".join(beforeFilters) + "\n" + code.get(indentLevel=1).replace("“", '"').replace("”", '"') + unitTests.footer() + unitTestsCode = unitTests.get() - with open(imageObjectPath, mode="w") as txtFile: - txtFile.write("\n".join(beforeFilters) + "\n" + code.get(indentLevel=1).replace("“", '"').replace("”", '"')) + return imageObjectCode, unitTestsCode - unitTestsPath = Path( - Path(__file__).parent.parent / "tests/testImageObject.py" - ) - unitTests.footer() - unitTestsPath.write_text(unitTests.get()) +if __name__ == "__main__": + imageObjectCode, unitTestsCode = generateImageObjectCode() + IMAGE_OBJECT_PATH.write_text(imageObjectCode) + UNIT_TESTS_PATH.write_text(unitTestsCode) diff --git a/tests/testAutomaticallyGeneratedCode.py b/tests/testAutomaticallyGeneratedCode.py new file mode 100644 index 00000000..09ef7a7d --- /dev/null +++ b/tests/testAutomaticallyGeneratedCode.py @@ -0,0 +1,26 @@ + +import sys +import unittest +from pathlib import Path + +sys.path.append(str((Path(__file__).parent.parent / "scripting").resolve())) + +from generateDrawbotInit import generateInitCode, INIT_PATH +from imageObjectCodeExtractor import generateImageObjectCode, IMAGE_OBJECT_PATH + + +class AutomaticallyGeneratedCodeTester(unittest.TestCase): + + def test_init(self): + initCode = generateInitCode() + self.assertEqual(INIT_PATH.read_text(), initCode) + + def test_imageObject(self): + imageObjectCode, _ = generateImageObjectCode() + self.assertEqual(IMAGE_OBJECT_PATH.read_text(), imageObjectCode) + + +if __name__ == '__main__': + import doctest + doctest.testmod() + sys.exit(unittest.main())