Skip to content

Commit

Permalink
test automatically generated python code
Browse files Browse the repository at this point in the history
  • Loading branch information
roberto-arista committed Jul 30, 2024
1 parent 1bdd28d commit f927658
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 51 deletions.
22 changes: 12 additions & 10 deletions scripting/generateDrawbotInit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,40 @@
"""

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}")

This comment has been minimized.

Copy link
@justvanrossum

justvanrossum Jul 30, 2024

Collaborator

Hm, there’s a bunch of weird whitespace changes here.

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):
if name.startswith("_"):
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)
84 changes: 43 additions & 41 deletions scripting/imageObjectCodeExtractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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=" "):
Expand Down Expand Up @@ -339,23 +342,23 @@ 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
ciFilter = AppKit.CIFilter.filterWithName_(filterName) # type: ignore
ciFilterAttributes = ciFilter.attributes()
doc = CodeWriter()
doc.add(AppKit.CIFilter.localizedDescriptionForFilterName_(filterName)) # type: ignore

args = []
unitTestsArgs = []
inputCode = CodeWriter()

inputKeys = [
inputKey
for inputKey in ciFilter.inputKeys()
Expand All @@ -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:**")
Expand All @@ -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:
Expand All @@ -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(),
Expand All @@ -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"
Expand All @@ -474,7 +477,7 @@ def pythonifyDescription(description):
case _:
value = default
unitTestsArgs.append(f"{inputKey}={value}")

drawBotFilterName = camelCase(filterName[2:])
code.add(
f"def {drawBotFilterName}"
Expand All @@ -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)
26 changes: 26 additions & 0 deletions tests/testAutomaticallyGeneratedCode.py
Original file line number Diff line number Diff line change
@@ -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())

0 comments on commit f927658

Please sign in to comment.