Skip to content

Conversation

apoorvkh
Copy link

Re-opening PR for #843 and #844. Copying @nightsailer's initial PR message below:


Expose and Enable Inheritance of StructMeta Metaclass

Changes

  • Exposed StructMeta metaclass to Python for direct use
  • Added Py_TPFLAGS_BASETYPE flag to enable Python inheritance of StructMeta
  • Created tests to verify functionality

Why

This allows users to:

  • Use StructMeta directly without subclassing Struct
  • Create custom metaclasses that extend StructMeta behavior
  • Implement reusable patterns across multiple struct classes

This PR resolves:

Example

from msgspec import StructMeta

# Custom metaclass with kw_only_default parameter
class KwOnlyMeta(StructMeta):
    _kw_only_default_settings = {}
    
    def __new__(mcls, name, bases, namespace, **kwargs):
        # Store and inherit kw_only_default
        kw_only_default = kwargs.pop('kw_only_default', None)
        if kw_only_default is None:
            for base in bases:
                if base.__name__ in mcls._kw_only_default_settings:
                    kw_only_default = mcls._kw_only_default_settings[base.__name__]
                    break
        else:
            mcls._kw_only_default_settings[name] = kw_only_default
            
        # Apply kw_only_default if kw_only not specified
        if 'kw_only' not in kwargs and kw_only_default is not None:
            kwargs['kw_only'] = kw_only_default
            
        return super().__new__(mcls, name, bases, namespace, **kwargs)

# Base model requiring keyword arguments
class BaseModel(metaclass=KwOnlyMeta, kw_only_default=True):
    pass

# Child class inherits kw_only=True
class User(BaseModel):
    name: str
    age: int

# Must use keyword arguments
user = User(name="John", age=30)

Technical Details

Added StructMeta to module exports in _core.c Added Py_TPFLAGS_BASETYPE flag to StructMetaType Updated init.py to import and expose StructMeta Added type hints in init.pyi

Compatibility

These changes are backward compatible and only add new capabilities without modifying existing behavior.

Related Issues

@ofek
Copy link
Collaborator

ofek commented Oct 20, 2025

Thanks! Just to confirm, you pretty much used this comparison right? main...nightsailer:msgspec-x:f9bd49fc907fa4b683f582257a463f3470e1a359

@apoorvkh
Copy link
Author

apoorvkh commented Oct 20, 2025

Yes, that's right!

@jacopoabramo
Copy link

jacopoabramo commented Oct 20, 2025

It would be worthwhile to document these changes as well, maybe with some use cases as examples

EDIT: @ofek ahah, synchronized comments

@ofek
Copy link
Collaborator

ofek commented Oct 20, 2025

I'll review later tonight but at a quick glance I noticed there is no information about how to use the new capabilities. Do you think you could take a crack at writing some very basic docs and then I can expand or touch up as need be?

@apoorvkh
Copy link
Author

Yeah, good point. No problem, will add something simple!

@apoorvkh
Copy link
Author

How's this, @ofek?

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.

4 participants