Skip to content

Conversation

khellste
Copy link

@khellste khellste commented Oct 6, 2025

Closes godotengine/godot-proposals#1316

This PR implements a new @meta annotation similar to the one described in #102516 (comment).

The @meta(name: StringName, value: Variant = true) annotation allows script authors to tag classes and class members in their scripts with StringName-keyed arbitrary metadata. This metadata can then be inspected at runtime by calling one of the following new Script methods:

  • Array[StringName] Script.get_script_meta_list() const - gets a list of all @meta names appearing in the script.
  • Array[Dictionary] Script.get_script_meta(name: StringName) const - gets a list of dictionaries describing each appearance of @meta having the given name in the script.

Usage example

@meta("meta1", [0, 1, 2, 3])
class_name MyOuterClass
extends Node


@meta("meta1")
var outer_prop: String


func _init() -> void:
    # This script reference could also be gotten from another script.
    var self_script: Script = get_script()

    # Prints:  ["meta1", "meta2"]
    print(self_script.get_script_meta_list())

    # Prints: [{ &"target_container": &"", &"target_name": &"MyOuterClass", &"target_type": 0, &"value": [0, 1, 2, 3] }, { &"target_container": &"MyOuterClass", &"target_name": &"outer_prop", &"target_type": 1, &"value": true }]
    print(self_script.get_script_meta("meta1"))

    # Prints: [{ &"target_container": &"MyOuterClass", &"target_name": &"MyInnerClass", &"target_type": 0, &"value": { &"key": "value" } }, { &"target_container": &"MyOuterClass.MyInnerClass.MyInnerInnerClass", &"target_name": &"inner_inner_prop", &"target_type": 1, &"value": false }]
    print(self_script.get_script_meta("meta2"))


@meta("meta2", { key = "value" })
class MyInnerClass:
    extends RefCounted

    class MyInnerInnerClass:
        extends RefCounted

        @meta("meta2", false)
        var inner_inner_prop: String

Specifications

  • The @meta annotation can target classes, constants, signals, properties, and methods. This includes arbitrarily deep inner classes and their members.
  • @meta can annotate a given target multiple times, even with the same name.
  • The second argument to @meta must be a constant expression. If omitted, it defaults to true.
  • Script.get_script_meta(name) returns a list of dictionaries, where each dictionary describes an instance of @meta(name, ...) in the script. The format of the dictionary is described in the docs added in bc46b32. It includes the target name, target type, container class name (if any), and metadata value.

Notes

  • @meta is a new builtin annotation and doesn't add support for custom user-defined annotations. However, I believe it should unblock most of the use cases in Allow custom GDScript annotations which can be read at runtime godot-proposals#1316.
  • @meta is for constant key-value metadata. The goal was not to implement something like Python decorators in Godot.
  • This PR only implements @meta for GDScript, but support for (e.g.) a companion C# [Meta(...)] attribute hooked up to the same getter interface on Script should be feasible.

@khellste khellste requested review from a team as code owners October 6, 2025 05:37
@arkology
Copy link
Contributor

arkology commented Oct 6, 2025

Having the uid as a separate file, but at the same time the metadata as an annotation inside the script looks really confusing.

@Mickeon
Copy link
Member

Mickeon commented Oct 6, 2025

In the attempt to solve godotengine/godot-proposals#1316 , I don't think it ever crossed most contributor's minds to utilize a single @meta tag as a custom tag. It does sound quite clever, but it's far too limiting in many occasions where implementing supplementary code is necessary.

@arkology
Copy link
Contributor

arkology commented Oct 6, 2025

Btw in your commits you ping user with @meta name, be careful.

@khellste
Copy link
Author

khellste commented Oct 6, 2025

Having the uid as a separate file, but at the same time the metadata as an annotation inside the script looks really confusing.

Unlike UIDs, the keys/values here are user-defined at the class and class-member level, so I think the text of the script is an appropriate place for them.

That said, I understand that calling this "metadata" could be confusing given that other kinds of script metadata (like UIDs) already exist and live elsewhere. The name was inspired by Object.get_meta but I have no objection to renaming this new annotation to @tag or similar if people prefer.

@khellste
Copy link
Author

khellste commented Oct 6, 2025

In the attempt to solve godotengine/godot-proposals#1316 , I don't think it ever crossed most contributor's minds to utilize a single @meta tag as a custom tag. It does sound quite clever, but it's far too limiting in many occasions where implementing supplementary code is necessary.

What application of custom annotations from the original proposal did you have in mind? I think this PR unblocks the original @Serialize case from that proposal. My intuition is that a good chunk of the more complex use cases ("I want to run some code when a certain annotation is present") can be accomplished by running the desired code in whatever script does the @meta introspecting.

@khellste
Copy link
Author

+@TokageItLab , since you took some interest in PR #102516.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow custom GDScript annotations which can be read at runtime

5 participants