Skip to content

feat: add Identifier type for Python bindings#373

Draft
wu-vincent wants to merge 3 commits intomainfrom
feat/py-identifier
Draft

feat: add Identifier type for Python bindings#373
wu-vincent wants to merge 3 commits intomainfrom
feat/py-identifier

Conversation

@wu-vincent
Copy link
Copy Markdown
Member

@wu-vincent wu-vincent commented Apr 9, 2026

Motivation

Identifier<T> is used throughout the API (dimensions, block types, actor types, enchantments, etc.) but was exposed to Python as a plain str. This had two problems:

  1. No structured access — plugin developers had to manually parse "namespace:key" strings to extract components.
  2. Weak type safety — type checkers couldn't distinguish between a DimensionId and an ActorTypeId since both were just str. You could pass Dimension.OVERWORLD to spawn_actor(type=...) without any type error.

With Identifier[T], type checkers can now catch misuse:

# Type checker ERROR: Expected Identifier[ActorType], got Identifier[Dimension]
dimension.spawn_actor(location, Dimension.OVERWORLD)

# Type checker OK:
dimension.spawn_actor(location, ActorType.ZOMBIE)

Before

dim_id = dimension.id          # "minecraft:overworld" (str)
type(dim_id)                   # <class 'str'>
# To get namespace/key, you had to parse manually:
ns, key = dim_id.split(":")    # "minecraft", "overworld"

After

dim_id = dimension.id          # Identifier(minecraft:overworld)
type(dim_id)                   # <class 'Identifier'>
dim_id.namespace               # "minecraft"
dim_id.key                     # "overworld"

# Backward compatible — still compares with str:
dim_id == "minecraft:overworld"  # True
str(dim_id)                      # "minecraft:overworld"

# Accepts str input everywhere:
level.get_dimension("overworld")              # still works
level.get_dimension(Dimension.OVERWORLD)      # also works

Summary

  • Add PyIdentifier struct to expose Identifier<T> as a proper Python object with namespace and key properties
  • Update Identifier<T> type caster to return Identifier objects instead of str, while still accepting both str and Identifier as input
  • Fix __str__ methods on BlockType, Enchantment, and ItemType to return str (not Identifier)
  • Add Identifier generic facade in stubs and stubgen for Identifier[T] type hints
  • Validate that namespace and key are non-empty in PyIdentifier constructors

Test plan

  • Build and verify compilation
  • Run Python tests (pytest tests/endstone_test)
  • Verify Dimension.OVERWORLD.namespace == "minecraft" and .key == "overworld"
  • Verify Dimension.OVERWORLD == "minecraft:overworld" backward compat
  • Run stubgen and verify generated stubs

Replace transparent str conversion with a proper Identifier Python
object that exposes namespace and key properties. The type caster
accepts both str and Identifier inputs and returns Identifier objects.
Change PyRegistry::get, getOrThrow, and contains to accept
PyIdentifier instead of raw std::string. Update Registry stubs
to accept Identifier[_T] | str.
Without this, passing str to PyRegistry methods (get, contains, etc.)
would raise TypeError since pybind11 doesn't auto-convert.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant