Repository: microsoft/TypeChat
File: python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py (Python)

Code before:

            assert is_generic(py_type)
            return convert_to_type_reference_node(py_type)

        if is_python_type_or_alias(py_type):
            return convert_to_type_reference_node(py_type)

        if isinstance(py_type, TypeVar):
            return convert_to_type_reference_node(py_type)

        errors.append(f"'{py_type}' cannot be used as a type annotation.")
        return AnyTypeReferenceNode

    def declare_property(name: str, py_annotation: type | TypeAliasType, is_typeddict_attribute: bool, optionality_default: bool):
        """
        Declare a property for a given type.
        If 'optionality_default' is 
        """
        current_annotation: object = py_annotation
        origin: object
        optional: bool | None = None
        comment: str | None = None
        while origin := get_origin(current_annotation):
            if origin is Annotated and comment is None:
                current_annotation = cast(Annotatedish, current_annotation)
                annotation_metadata = current_annotation.__metadata__[0]
                if isinstance(annotation_metadata, Doc):
                    comment = annotation_metadata.documentation
                elif isinstance(annotation_metadata, str):
                    comment = annotation_metadata

                current_annotation = current_annotation.__origin__

            elif origin is Required or origin is NotRequired:
                if not is_typeddict_attribute:
                    errors.append(f"Optionality cannot be specified with {origin} outside of TypedDicts.")

                if optional is None:
                    optional = origin is NotRequired
                else:
                    errors.append(f"{origin} cannot be used within another optionality annotation.")

                current_annotation = get_args(current_annotation)[0]
            else:
                break

        if optional is None:
            optional = optionality_default
        
        type_annotation = convert_to_type_node(skip_annotations(current_annotation))
        return PropertyDeclarationNode(name, optional, comment or "", type_annotation)

Review comment (suggestion):
There could be multiple annotations on a type and the comment not guaranteed to be at index 0.  The previous implementation looked through all the metadata entries and found the first one that was either a Doc or a str. We probably want to keep that behavior

Code after:

            assert is_generic(py_type)
            return convert_to_type_reference_node(py_type)

        if is_python_type_or_alias(py_type):
            return convert_to_type_reference_node(py_type)

        if isinstance(py_type, TypeVar):
            return convert_to_type_reference_node(py_type)

        errors.append(f"'{py_type}' cannot be used as a type annotation.")
        return AnyTypeReferenceNode

    def declare_property(name: str, py_annotation: type | TypeAliasType, is_typeddict_attribute: bool, optionality_default: bool):
        """
        Declare a property for a given type.
        If 'optionality_default' is 
        """
        current_annotation: object = py_annotation
        origin: object
        optional: bool | None = None
        comment: str | None = None
        while origin := get_origin(current_annotation):
            if origin is Annotated and comment is None:
                current_annotation = cast(Annotatedish, current_annotation)

                for metadata in current_annotation.__metadata__:
                    if isinstance(metadata, Doc):
                        comment = metadata.documentation
                        break
                    if isinstance(metadata, str):
                        comment = metadata
                        break

                current_annotation = current_annotation.__origin__

            elif origin is Required or origin is NotRequired:
                if not is_typeddict_attribute:
                    errors.append(f"Optionality cannot be specified with {origin} outside of TypedDicts.")

                if optional is None:
                    optional = origin is NotRequired
                else:
                    errors.append(f"{origin} cannot be used within another optionality annotation.")

                current_annotation = get_args(current_annotation)[0]
            else:
                break

        if optional is None:
            optional = optionality_default