@@ -89,7 +89,7 @@ def new_imports(self: DataModelFieldBaseT) -> tuple[Import, ...]:
8989 extra_imports .append (IMPORT_MSGSPEC_FIELD )
9090 if self .field and "lambda: convert" in self .field :
9191 extra_imports .append (IMPORT_MSGSPEC_CONVERT )
92- if self . annotated :
92+ if isinstance ( self , DataModelField ) and self . needs_meta_import :
9393 extra_imports .append (IMPORT_MSGSPEC_META )
9494 if self .extras .get ("is_classvar" ):
9595 extra_imports .append (IMPORT_CLASSVAR )
@@ -281,7 +281,7 @@ def __str__(self) -> str:
281281 def type_hint (self ) -> str :
282282 """Return the type hint, using UnsetType for non-required non-nullable fields."""
283283 type_hint = super ().type_hint
284- if self ._not_required and not self .nullable :
284+ if self ._not_required and not self .nullable and not self . data_type . is_optional :
285285 return get_neither_required_nor_nullable_type (type_hint , self .data_type .use_union_operator )
286286 return type_hint
287287
@@ -294,12 +294,8 @@ def fall_back_to_nullable(self) -> bool:
294294 """Return whether to fall back to nullable type instead of UnsetType."""
295295 return not self ._not_required
296296
297- @property
298- def annotated (self ) -> str | None :
299- """Get Annotated type hint with Meta constraints."""
300- if not self .use_annotated : # pragma: no cover
301- return None
302-
297+ def _get_meta_string (self ) -> str | None :
298+ """Compute Meta(...) string if there are any meta constraints."""
303299 data : dict [str , Any ] = {k : v for k , v in self .extras .items () if k in self ._META_FIELD_KEYS }
304300 has_type_constraints = self .data_type .kwargs is not None and len (self .data_type .kwargs ) > 0
305301 if (
@@ -317,21 +313,50 @@ def annotated(self) -> str | None:
317313 }
318314
319315 meta_arguments = sorted (f"{ k } ={ v !r} " for k , v in data .items () if v is not None )
320- if not meta_arguments :
316+ return f"Meta({ ', ' .join (meta_arguments )} )" if meta_arguments else None
317+
318+ @property
319+ def annotated (self ) -> str | None :
320+ """Get Annotated type hint with Meta constraints."""
321+ if not self .use_annotated : # pragma: no cover
321322 return None
322323
323- meta = f"Meta({ ', ' .join (meta_arguments )} )"
324+ meta = self ._get_meta_string ()
325+
326+ if not meta and not self .extras .get ("is_classvar" ):
327+ return None
324328
325329 if not self .required and not self .extras .get ("is_classvar" ):
326330 type_hint = self .data_type .type_hint
327331 annotated_type = f"Annotated[{ type_hint } , { meta } ]"
328332 return get_neither_required_nor_nullable_type (annotated_type , self .data_type .use_union_operator )
329333
330- annotated_type = f"Annotated[ { self . type_hint } , { meta } ]"
334+ # Handle ClassVar case (for discriminator fields in msgspec)
331335 if self .extras .get ("is_classvar" ):
332- annotated_type = f"ClassVar[{ annotated_type } ]"
336+ if meta :
337+ annotated_type = f"Annotated[{ self .type_hint } , { meta } ]"
338+ return f"ClassVar[{ annotated_type } ]"
339+ return f"ClassVar[{ self .type_hint } ]"
340+
341+ return f"Annotated[{ self .type_hint } , { meta } ]"
333342
334- return annotated_type
343+ @property
344+ def needs_annotated_import (self ) -> bool :
345+ """Check if this field requires the Annotated import.
346+
347+ ClassVar fields without Meta constraints don't need Annotated.
348+ """
349+ if not self .annotated :
350+ return False
351+ # ClassVar without Meta doesn't use Annotated
352+ if self .extras .get ("is_classvar" ):
353+ return self ._get_meta_string () is not None
354+ return True
355+
356+ @property
357+ def needs_meta_import (self ) -> bool :
358+ """Check if this field requires the Meta import."""
359+ return self ._get_meta_string () is not None
335360
336361 def _get_default_as_struct_model (self ) -> str | None :
337362 """Convert default value to Struct model using msgspec convert."""
0 commit comments