Basic tests, small fixes

This commit is contained in:
2023-03-08 02:49:56 +01:00
parent ab463bdc37
commit d7a1499da1
6 changed files with 464 additions and 26 deletions

3
.gitignore vendored
View File

@@ -4,4 +4,5 @@ __pycache__
dyn2py.egg-info
build
dist
docs
docs
tests/output_files

11
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"python.testing.unittestArgs": [
"-v",
"-s",
"./tests",
"-p",
"test_*.py"
],
"python.testing.pytestEnabled": false,
"python.testing.unittestEnabled": true
}

View File

@@ -130,6 +130,8 @@ class DynamoFile(File):
"""The uuid of the graph"""
name: str
"""The name of the graph, read from the file. Not the name of the file"""
python_nodes: list["PythonNode"]
"""Python node objects, read from this file"""
open_files: set["DynamoFile"] = set()
"""A set of open Dynamo files, before saving"""
@@ -195,10 +197,13 @@ class DynamoFile(File):
Returns:
list[PythonNode]: A list of PythonNodes in the file
"""
if not self in self.open_files:
self.read()
full_python_nodes = [n for n in self.full_dict["Nodes"]
if n["NodeType"] == "PythonScriptNode"]
python_nodes = []
self.python_nodes = []
for p_node in full_python_nodes:
# The name of the node is stored here:
@@ -207,12 +212,12 @@ class DynamoFile(File):
node_dict_from_dyn=p_node,
full_nodeviews_dict_from_dyn=node_views,
source_dynamo_file=self)
python_nodes.append(python_node)
self.python_nodes.append(python_node)
if not python_nodes:
if not self.python_nodes:
raise DynamoFileException("No python nodes in this file!")
return python_nodes
return self.python_nodes
def get_python_node_by_id(self, node_id: str) -> "PythonNode":
"""Get a PythonNode object from this Dynamo graph, by its id
@@ -223,15 +228,28 @@ class DynamoFile(File):
Returns:
PythonNode: The PythonNode with the given id
"""
python_node_dict = next((
n for n in self.full_dict["Nodes"] if n["Id"] == node_id
), {})
if not python_node_dict:
raise PythonNodeNotFoundException(
f"Node not found with id {node_id}")
if not self in self.open_files:
self.read()
python_node = PythonNode(
node_dict_from_dyn=python_node_dict)
# Find the node, if the nodes are not read yet:
if not self.python_nodes:
python_node_dict = next((
n for n in self.full_dict["Nodes"] if n["Id"] == node_id
), {})
if not python_node_dict:
raise PythonNodeNotFoundException(
f"Node not found with id {node_id}")
python_node = PythonNode(
node_dict_from_dyn=python_node_dict)
else:
python_node = next((
p for p in self.python_nodes if p.id == node_id
), None)
if not python_node:
raise PythonNodeNotFoundException(
f"Node not found with id {node_id}")
return python_node

View File

@@ -6,13 +6,16 @@ LOGLEVELS = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]
DEFAULT_LOGLEVEL = "INFO"
FILTERS = ["py", "dyn"]
class Options(argparse.Namespace):
"""Class for options for running a conversion like from the command line"""
def __init__(
self,
source: list[pathlib.Path | str] = [],
loglevel: str = DEFAULT_LOGLEVEL,
dry_run: bool = False,
force: bool = False,
backup: bool = False,
filter: str = "",
update: bool = False,
@@ -24,6 +27,7 @@ class Options(argparse.Namespace):
source (list[pathlib.Path | str], optional): List of files to run on. Defaults to [].
loglevel (str, optional): log level. Defaults to DEFAULT_LOGLEVEL.
dry_run (bool, optional): If it's a dry run. Defaults to False.
force (bool, optional): Overwrite files, even if they are older. Defaults to False.
backup (bool, optional): Create backup of modified files. Defaults to False.
filter (str, optional): 'dyn' or 'py' file filter for running on folders. Defaults to "".
update (bool, optional): Update mode, like inverse on Dynamo files. Defaults to False.
@@ -46,6 +50,7 @@ class Options(argparse.Namespace):
raise ValueError
self.dry_run = dry_run
self.force = force
self.backup = backup
if not filter or filter in FILTERS:
@@ -59,16 +64,3 @@ class Options(argparse.Namespace):
self.python_folder = pathlib.Path(python_folder)
else:
self.python_folder = python_folder
# super().__init__(
# source=self.source,
# loglevel=self.loglevel,
# dry_run=self.dry_run,
# backup=self.backup,
# filter=self.filter,
# update=self.update,
# python_folder=self.python_folder,
# )

View File

@@ -0,0 +1,367 @@
{
"Uuid": "3c3b4c05-9716-4e93-9360-ca0637cb5486",
"IsCustomNode": false,
"Description": "",
"Name": "python_nodes",
"ElementResolver": {
"ResolutionMap": {}
},
"Inputs": [],
"Outputs": [],
"Nodes": [
{
"ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels",
"NodeType": "PythonScriptNode",
"Code": "# Load the Python Standard and DesignScript Libraries\r\nimport sys\r\nimport clr\r\nclr.AddReference('ProtoGeometry')\r\nfrom Autodesk.DesignScript.Geometry import *\r\n\r\n# The inputs to this node will be stored as a list in the IN variables.\r\ndataEnteringNode = IN\r\n\r\n\r\n#Not renamed, Cpython3\r\n\r\n# Place your code below this line\r\n\r\n# Assign your output to the OUT variable.\r\nOUT = sys.version",
"Engine": "CPython3",
"EngineName": "CPython3",
"VariableInputPorts": true,
"Id": "ff087a3611b0478b95252f67e87be507",
"Inputs": [
{
"Id": "ac5bdff0617e4246b9b03101ec2ecaf1",
"Name": "IN[0]",
"Description": "Input #0",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "974d45015c8442d1a3b9f77859d827a8",
"Name": "OUT",
"Description": "Result of the python script",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Runs an embedded Python script."
},
{
"ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels",
"NodeType": "PythonScriptNode",
"Code": "\"\"\"\r\nMultiline comment \r\nblablabla\r\n\"\"\"\r\n\r\n# Load the Python Standard and DesignScript Libraries\r\nimport sys\r\nimport clr\r\nclr.AddReference('ProtoGeometry')\r\nfrom Autodesk.DesignScript.Geometry import *\r\n\r\n# The inputs to this node will be stored as a list in the IN variables.\r\ndataEnteringNode = IN\r\n\r\n#Renamed, Cpython3\r\n\r\n# Place your code below this line\r\n\r\n# Assign your output to the OUT variable.\r\nOUT = 0",
"Engine": "CPython3",
"EngineName": "CPython3",
"VariableInputPorts": true,
"Id": "d7704617c75e4bf1a5c387b7c3f001ea",
"Inputs": [
{
"Id": "c98cd3c591ae463eae69ff040a3989eb",
"Name": "IN[0]",
"Description": "Input #0",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "13c1e8c1f34d40ca97f25950269acd19",
"Name": "OUT",
"Description": "Result of the python script",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Runs an embedded Python script."
},
{
"ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels",
"NodeType": "PythonScriptNode",
"Code": "# Load the Python Standard and DesignScript Libraries\r\nimport sys\r\nimport clr\r\nclr.AddReference('ProtoGeometry')\r\nfrom Autodesk.DesignScript.Geometry import *\r\n\r\n# The inputs to this node will be stored as a list in the IN variables.\r\ndataEnteringNode = IN\r\n\r\n#Not renamed, IronPython2\r\n\r\n# Place your code below this line\r\n\r\n# Assign your output to the OUT variable.\r\nOUT = sys.version",
"Engine": "IronPython2",
"EngineName": "IronPython2",
"VariableInputPorts": true,
"Id": "2047ce859d424496963582fbb7b6417f",
"Inputs": [
{
"Id": "6728601fd8054c6ca9cc414655c6912d",
"Name": "IN[0]",
"Description": "Input #0",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "beb9265bbf8641df8d46fb80e78f8269",
"Name": "OUT",
"Description": "Result of the python script",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Runs an embedded Python script."
},
{
"ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels",
"NodeType": "PythonScriptNode",
"Code": "# Load the Python Standard and DesignScript Libraries\r\nimport sys\r\nimport clr\r\nclr.AddReference('ProtoGeometry')\r\nfrom Autodesk.DesignScript.Geometry import *\r\n\r\n# The inputs to this node will be stored as a list in the IN variables.\r\ndataEnteringNode = IN\r\n\r\n#Renamed, IPY2\r\n\r\n# Place your code below this line\r\n\r\n# Assign your output to the OUT variable.\r\nOUT = 0",
"Engine": "CPython3",
"EngineName": "CPython3",
"VariableInputPorts": true,
"Id": "d89c158e2e824a909753d7137fcdf56e",
"Inputs": [
{
"Id": "2a0f4220761341deac9e67ac0b97e02b",
"Name": "IN[0]",
"Description": "Input #0",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "780c6be36b994a4eb103162a49bbdad8",
"Name": "OUT",
"Description": "Result of the python script",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Runs an embedded Python script."
},
{
"ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore",
"NodeType": "CodeBlockNode",
"Code": "<>:\"/\\|?*",
"Id": "048f431a6a734d5d93a1bcc3d384dae4",
"Inputs": [
{
"Id": "18fc02088224473486e6e3cc732ec492",
"Name": "h",
"Description": "h",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "9c669d4dabef4f88bbc4a9f19700ff11",
"Name": "",
"Description": "Value of expression at line 1",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Allows for DesignScript code to be authored directly"
},
{
"ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels",
"NodeType": "PythonScriptNode",
"Code": "# Load the Python Standard and DesignScript Libraries\r\nimport sys\r\nimport clr\r\nclr.AddReference('ProtoGeometry')\r\nfrom Autodesk.DesignScript.Geometry import *\r\n\r\n# The inputs to this node will be stored as a list in the IN variables.\r\ndataEnteringNode = IN\r\n\r\n#Renamed, IPY2\r\n\r\n# Place your code below this line\r\n\r\n# Assign your output to the OUT variable.\r\nOUT = 0",
"Engine": "CPython3",
"EngineName": "CPython3",
"VariableInputPorts": true,
"Id": "d65fb4d27998455c8bf15c92883b7213",
"Inputs": [
{
"Id": "ed16a01bf82f46f68f279cfbdb28173a",
"Name": "IN[0]",
"Description": "Input #0",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "60947b135fd54247a9fcc0b04575a293",
"Name": "OUT",
"Description": "Result of the python script",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Runs an embedded Python script."
},
{
"ConcreteType": "PythonNodeModels.PythonNode, PythonNodeModels",
"NodeType": "PythonScriptNode",
"Code": "# Load the Python Standard and DesignScript Libraries\r\nimport sys\r\nimport clr\r\nclr.AddReference('ProtoGeometry')\r\nfrom Autodesk.DesignScript.Geometry import *\r\n\r\n# The inputs to this node will be stored as a list in the IN variables.\r\ndataEnteringNode = IN\r\n\r\n#Not renamed, IronPython2\r\n\r\n# Place your code below this line\r\n\r\n# Assign your output to the OUT variable.\r\nOUT = sys.version",
"Engine": "IronPython2",
"EngineName": "IronPython2",
"VariableInputPorts": true,
"Id": "4680a2f79a084cf9a61cbb11e3ce6af4",
"Inputs": [
{
"Id": "4e9c4364066b41a4991c4ff2860f4c12",
"Name": "IN[0]",
"Description": "Input #0",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "d29abf7c1ac44d7ba10c2dcf020a8f09",
"Name": "OUT",
"Description": "Result of the python script",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Runs an embedded Python script."
}
],
"Connectors": [],
"Dependencies": [],
"NodeLibraryDependencies": [],
"Thumbnail": "",
"GraphDocumentationURL": null,
"ExtensionWorkspaceData": [
{
"ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670",
"Name": "Properties",
"Version": "2.12",
"Data": {}
},
{
"ExtensionGuid": "DFBD9CC0-DB40-457A-939E-8C8555555A9D",
"Name": "Generative Design",
"Version": "3.0",
"Data": {}
}
],
"Author": "",
"Linting": {
"activeLinter": "None",
"activeLinterId": "7b75fb44-43fd-4631-a878-29f4d5d8399a",
"warningCount": 0,
"errorCount": 0
},
"Bindings": [],
"View": {
"Dynamo": {
"ScaleFactor": 1.0,
"HasRunWithoutCrash": true,
"IsVisibleInDynamoLibrary": true,
"Version": "2.16.1.2727",
"RunType": "Manual",
"RunPeriod": "1000"
},
"Camera": {
"Name": "Background Preview",
"EyeX": -17.0,
"EyeY": 24.0,
"EyeZ": 50.0,
"LookX": 12.0,
"LookY": -13.0,
"LookZ": -58.0,
"UpX": 0.0,
"UpY": 1.0,
"UpZ": 0.0
},
"ConnectorPins": [],
"NodeViews": [
{
"Id": "ff087a3611b0478b95252f67e87be507",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Name": "Python Script",
"ShowGeometry": true,
"Excluded": false,
"X": 330.854857997459,
"Y": 370.30582545813525
},
{
"Id": "d7704617c75e4bf1a5c387b7c3f001ea",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Name": "Renamed Cpython",
"ShowGeometry": true,
"Excluded": false,
"X": 332.19386487425072,
"Y": 553.74976757860486
},
{
"Id": "2047ce859d424496963582fbb7b6417f",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Name": "Python Script",
"ShowGeometry": true,
"Excluded": false,
"X": 786.04189521113619,
"Y": 262.8632861477945
},
{
"Id": "d89c158e2e824a909753d7137fcdf56e",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Name": "<>:\"/\\|?*",
"ShowGeometry": true,
"Excluded": false,
"X": 1033.1078482745011,
"Y": 551.97820171368517
},
{
"Id": "048f431a6a734d5d93a1bcc3d384dae4",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Name": "Code Block",
"ShowGeometry": true,
"Excluded": false,
"X": 614.48767528598637,
"Y": 656.55546226164051
},
{
"Id": "d65fb4d27998455c8bf15c92883b7213",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Name": "",
"ShowGeometry": true,
"Excluded": false,
"X": 1021.3666290027875,
"Y": 401.8583267396354
},
{
"Id": "4680a2f79a084cf9a61cbb11e3ce6af4",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Name": "Python Script Renamed",
"ShowGeometry": true,
"Excluded": false,
"X": 934.48445314636979,
"Y": 833.98973786470992
}
],
"Annotations": [],
"X": -97.703074094655562,
"Y": -321.86387971712088,
"Zoom": 1.1923804228516888
}
}

49
tests/test_DynamoFile.py Normal file
View File

@@ -0,0 +1,49 @@
import os
import unittest
import dyn2py
import pathlib
INPUT_DIR = "tests/input_files"
OUTPUT_DIR = "tests/output_files"
def cleanup():
output_dir = pathlib.Path(OUTPUT_DIR)
if output_dir.exists():
for f in output_dir.iterdir():
f.unlink()
else:
output_dir.mkdir()
class TestDynamoFile(unittest.TestCase):
def test_read(self):
dyn = dyn2py.DynamoFile(f"{INPUT_DIR}/python_nodes.dyn")
dyn.read()
self.assertEqual(dyn.uuid, "3c3b4c05-9716-4e93-9360-ca0637cb5486")
self.assertEqual(dyn.name, "python_nodes")
self.assertTrue(dyn in dyn.open_files)
def test_get_python_node(self):
dyn = dyn2py.DynamoFile(f"{INPUT_DIR}/python_nodes.dyn")
py_nodes = dyn.get_python_nodes()
py_node = dyn.get_python_node_by_id("d7704617c75e4bf1a5c387b7c3f001ea")
self.assertEqual(len(py_nodes), 6)
self.assertTrue(py_node)
self.assertTrue(py_node in py_nodes)
self.assertEqual(py_node.checksum, "1f3d9e6153804fe1ed37571a9cda8e26")
with self.assertRaises(dyn2py.PythonNodeNotFoundException):
dyn.get_python_node_by_id("wrongid")
def test_extract_python(self):
cleanup()
opt = dyn2py.Options(python_folder="tests/output_files")
dyn = dyn2py.DynamoFile(f"{INPUT_DIR}/python_nodes.dyn")
dyn.extract_python(options=opt)
output_dir = pathlib.Path("tests/output_files")
self.assertEqual(len(list(output_dir.iterdir())), 6)