Skip to content

Commit

Permalink
fix type issues
Browse files Browse the repository at this point in the history
  • Loading branch information
YajJackson committed Apr 13, 2024
1 parent 63a65ba commit b9e358b
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 15 deletions.
11 changes: 7 additions & 4 deletions godot_parser/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@ def load_parent_scene(self) -> "GDScene":
raise RuntimeError(
"Could not find parent scene resource id(%d)" % root.instance
)
return GDScene.load(gdpath_to_filepath(self.project_root, parent_res.path))
scene = GDScene.load(gdpath_to_filepath(self.project_root, parent_res.path))
assert isinstance(scene, GDScene)
return scene

@contextmanager
def use_tree(self):
Expand Down Expand Up @@ -302,12 +304,12 @@ def get_node(self, path: str = ".") -> Optional[GDNodeSection]:
return node.section if node is not None else None

@classmethod
def parse(cls: Type[GDFileType], contents: str) -> GDFileType:
def parse(cls: Type[GDFileType], contents: str):
"""Parse the contents of a Godot file"""
return cls.from_parser(scene_file.parse_string(contents, parseAll=True))

@classmethod
def load(cls: Type[GDFileType], filepath: str) -> GDFileType:
def load(cls: Type[GDFileType], filepath: str):
with open(filepath, "r", encoding="utf-8") as ifile:
try:
file = cls.parse(ifile.read())
Expand All @@ -330,7 +332,8 @@ def from_parser(cls: Type[GDFileType], parse_result):
resource = GDResource.__new__(GDResource)
resource._sections = list(parse_result)
return resource
return cls(*parse_result)

raise GodotFileException("Unknown GDFileType %d" % first_section.header.name)

def write(self, filename: str):
"""Writes this to a file"""
Expand Down
1 change: 1 addition & 0 deletions godot_parser/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ def _load_parent_scene(root: Node, file: GDFile):
parent_file: GDFile = file.load_parent_scene()
parent_tree = Tree.build(parent_file)
# Transfer parent scene's children to this scene
assert parent_tree.root is not None, "Parent scene has no root node"
for child in parent_tree.root.get_children():
root.add_child(child)
# Mark the entire parent tree as inherited
Expand Down
16 changes: 14 additions & 2 deletions godot_parser/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
Group,
Keyword,
Opt,
ParseResults,
ParserElement,
QuotedString,
Suppress,
Word,
Expand All @@ -16,10 +18,20 @@

from .objects import GDObject

boolean = (

def parse_action_function(parse_results: ParseResults) -> bool:
result = parse_results[0]
if isinstance(result, bool):
return result
if isinstance(result, str):
return result.lower() == "true"
raise ValueError(f"Unexpected result: {result}")


boolean: ParserElement = (
(Keyword("true") | Keyword("false"))
.set_name("bool")
.set_parse_action(lambda x: x[0].lower() == "true")
.set_parse_action(parse_action_function)
)

null = Keyword("null").set_parse_action(lambda _: [None])
Expand Down
15 changes: 14 additions & 1 deletion tests/test_gdfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import unittest

from godot_parser import GDFile, GDObject, GDResource, GDResourceSection, GDScene, Node
from godot_parser.sections import GDExtResourceSection, GDSubResourceSection


class TestGDFile(unittest.TestCase):
Expand Down Expand Up @@ -174,10 +175,15 @@ def test_addremove_ext_res(self):
node["texture_pool"] = GDObject("ResourcePool", res2.reference)

s = scene.find_section(path="res://Res.tscn")
assert s is not None

scene.remove_section(s)
scene.renumber_resource_ids()

s = scene.find_section("ext_resource")
assert s is not None
assert isinstance(s, GDExtResourceSection)

self.assertEqual(s.id, 1)
self.assertEqual(node["texture"], s.reference)
self.assertEqual(node["textures"][0], s.reference)
Expand All @@ -187,7 +193,7 @@ def test_addremove_ext_res(self):
def test_remove_unused_resource(self):
"""Can remove unused resources"""
scene = GDScene()
res = scene.add_ext_resource("res://Res.tscn", "PackedScene")
scene.add_ext_resource("res://Res.tscn", "PackedScene")
scene.remove_unused_resources()
resources = scene.get_sections("ext_resource")
self.assertEqual(len(resources), 0)
Expand All @@ -203,10 +209,15 @@ def test_addremove_sub_res(self):
scene.add_section(resource)

s = scene.find_sub_resource(type="CircleShape2D")
assert s is not None

scene.remove_section(s)
scene.renumber_resource_ids()

s = scene.find_section("sub_resource")
assert s is not None
assert isinstance(s, GDSubResourceSection)

self.assertEqual(s.id, 1)
self.assertEqual(resource["shape"], s.reference)

Expand Down Expand Up @@ -241,5 +252,7 @@ def test_file_equality(self):
s2 = GDScene(GDResourceSection())
self.assertEqual(s1, s2)
resource = s1.find_section("resource")
assert resource is not None

resource["key"] = "value"
self.assertNotEqual(s1, s2)
4 changes: 3 additions & 1 deletion tests/test_parser.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import os
from typing import Optional, Union
import unittest

from pyparsing import ParseException

from godot_parser import GDFile, GDObject, GDSection, GDSectionHeader, Vector2, parse
from godot_parser.files import GDResource, GDScene

HERE = os.path.dirname(__file__)

Expand Down Expand Up @@ -114,11 +116,11 @@


class TestParser(unittest.TestCase):

""" """

def _run_test(self, string: str, expected):
"""Run a set of tests"""
parse_result: Optional[Union[GDScene, GDResource]] = None
try:
parse_result = parse(string)
if expected == "error":
Expand Down
61 changes: 54 additions & 7 deletions tests/test_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import unittest

from godot_parser import GDScene, Node, SubResource, TreeMutationException
from godot_parser.sections import GDNodeSection
from godot_parser.util import find_project_root, gdpath_to_filepath


Expand All @@ -29,20 +30,23 @@ def test_remove_node(self):

# Remove by name
with scene.use_tree() as tree:
assert tree.root is not None
tree.root.remove_child("Child")
node = scene.find_section("node", name="Child")
self.assertIsNone(node)

# Remove by index
scene.add_node("Child", parent=".")
with scene.use_tree() as tree:
assert tree.root is not None
tree.root.remove_child(0)
node = scene.find_section("node", name="Child")
self.assertIsNone(node)

# Remove by reference
scene.add_node("Child", parent=".")
with scene.use_tree() as tree:
assert tree.root is not None
node = tree.root.get_children()[0]
tree.root.remove_child(node)
node = scene.find_section("node", name="Child")
Expand All @@ -51,7 +55,9 @@ def test_remove_node(self):
# Remove child
scene.add_node("Child", parent=".")
with scene.use_tree() as tree:
assert tree.root is not None
node = tree.root.get_child(0)
assert node is not None
node.remove_from_parent()
node = scene.find_section("node", name="Child")
self.assertIsNone(node)
Expand All @@ -62,10 +68,15 @@ def test_insert_child(self):
scene.add_node("RootNode")
scene.add_node("Child1", parent=".")
with scene.use_tree() as tree:
assert tree.root is not None
child = Node("Child2", type="Node")
tree.root.insert_child(0, child)

child1 = scene.find_section("node", name="Child1")
child2 = scene.find_section("node", name="Child2")
assert child1 is not None
assert child2 is not None

idx1 = scene.get_sections().index(child1)
idx2 = scene.get_sections().index(child2)
self.assertLess(idx2, idx1)
Expand All @@ -89,13 +100,17 @@ def test_properties(self):
scene = GDScene()
scene.add_node("RootNode")
with scene.use_tree() as tree:
assert tree.root is not None
tree.root["vframes"] = 10
self.assertEqual(tree.root["vframes"], 10)
tree.root["hframes"] = 10
del tree.root["hframes"]
del tree.root["hframes"]
self.assertIsNone(tree.root.get("hframes"))

child = scene.find_section("node")
assert child is not None

self.assertEqual(child["vframes"], 10)

def test_dunder(self):
Expand Down Expand Up @@ -167,47 +182,67 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
super(TestInheritedScenes, cls).tearDownClass()
assert cls.project_dir is not None

if os.path.isdir(cls.project_dir):
shutil.rmtree(cls.project_dir)

def test_load_inherited(self):
"""Can load an inherited scene and read the nodes"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)

with scene.use_tree() as tree:
node = tree.get_node("Health/LifeBar")
self.assertIsNotNone(node)
assert node is not None

self.assertEqual(node.type, "TextureProgress")

def test_add_new_nodes(self):
"""Can add new nodes to an inherited scene"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)

with scene.use_tree() as tree:
assert tree.root is not None

tree.get_node("Health/LifeBar")
node = Node("NewChild", type="Control")
tree.root.add_child(node)
# Non-inherited node can change name, type, instance
node.instance = 2
node.type = "Node2D"
node.name = "NewChild2"

found = scene.find_section("node", name="NewChild2")
assert found is not None
assert isinstance(found, GDNodeSection)

self.assertIsNotNone(found)
self.assertEqual(found.type, "Node2D")
self.assertEqual(found.parent, ".")
self.assertEqual(found.index, 3)

def test_cannot_remove(self):
"""Cannot remove inherited nodes"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)

with scene.use_tree() as tree:
assert tree.root is not None
node = tree.get_node("Health")
self.assertRaises(TreeMutationException, node.remove_from_parent)
self.assertRaises(TreeMutationException, lambda: tree.root.remove_child(0))
self.assertRaises(
TreeMutationException, lambda: tree.root.remove_child("Health")
)
assert node is not None

with self.assertRaises(TreeMutationException):
node.remove_from_parent()
with self.assertRaises(TreeMutationException):
tree.root.remove_child(0)
with self.assertRaises(TreeMutationException):
tree.root.remove_child("Health")

def test_cannot_mutate(self):
"""Cannot change the name/type/instance of inherited nodes"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)

def change_name(x):
Expand All @@ -227,31 +262,39 @@ def change_instance(x):

def test_inherit_properties(self):
"""Inherited nodes inherit properties"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)
with scene.use_tree() as tree:
assert tree.root is not None
self.assertEqual(tree.root["shape"], SubResource(1))
self.assertEqual(tree.root["collision_layer"], 4)
self.assertEqual(tree.root.get("collision_layer"), 4)
self.assertEqual(tree.root.get("missing"), None)
self.assertRaises(KeyError, lambda: tree.root["missing"])

with self.assertRaises(KeyError):
tree.root["missing"]

def test_unchanged_sections(self):
"""Inherited nodes do not appear in sections"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)
num_nodes = len(scene.get_nodes())
self.assertEqual(num_nodes, 2)
with scene.use_tree() as tree:
sprite = tree.get_node("Sprite")
assert sprite is not None
sprite["flip_v"] = True
# No new nodes
num_nodes = len(scene.get_nodes())
self.assertEqual(num_nodes, 2)

def test_overwrite_sections(self):
"""Inherited nodes appear in sections if we change their configuration"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)
with scene.use_tree() as tree:
node = tree.get_node("Health/LifeBar")
assert node is not None
node["pause_mode"] = 2
num_nodes = len(scene.get_nodes())
self.assertEqual(num_nodes, 3)
Expand All @@ -260,16 +303,19 @@ def test_overwrite_sections(self):

def test_disappear_sections(self):
"""Inherited nodes are removed from sections if we change their configuration to match parent"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)
with scene.use_tree() as tree:
sprite = tree.get_node("Sprite")
assert sprite is not None
sprite["flip_h"] = False
# Sprite should match parent now, and not be in file
node = scene.find_section("node", name="Sprite")
self.assertIsNone(node)

def test_find_project_root(self):
"""Can find project root even if deep in folder"""
assert self.project_dir is not None
os.mkdir(os.path.join(self.project_dir, "Dir1"))
nested = os.path.join(self.project_dir, "Dir1", "Dir2")
os.mkdir(nested)
Expand All @@ -291,6 +337,7 @@ def test_missing_root(self):

def test_missing_ext_resource(self):
"""Raise exception when GDScene is inherited but ext_resource is missing"""
assert self.leaf_scene is not None
scene = GDScene.load(self.leaf_scene)
for section in scene.get_ext_resources():
scene.remove_section(section)
Expand Down

0 comments on commit b9e358b

Please sign in to comment.