@@ -480,21 +480,6 @@ def getall(
480480 FoamFile .SubDict (self , keywords ) if v is ... else deepcopy (v ) for v in ret
481481 ]
482482
483- def _normalize_and_validate_keywords (
484- self , keywords : str | tuple [str , ...] | None
485- ) -> tuple [str , ...]:
486- """Normalize keywords to tuple format and validate them."""
487- if keywords is None :
488- keywords = ()
489- elif not isinstance (keywords , tuple ):
490- keywords = (keywords ,)
491-
492- if keywords and not isinstance (normalize (keywords [- 1 ], bool_ok = False ), str ):
493- msg = f"Invalid keyword type: { keywords [- 1 ]} (type { type (keywords [- 1 ])} )"
494- raise TypeError (msg )
495-
496- return keywords
497-
498483 def _write_header_if_needed (self , keywords : tuple [str , ...]) -> None :
499484 """Write FoamFile header if needed."""
500485 try :
@@ -517,10 +502,10 @@ def _write_header_if_needed(self, keywords: tuple[str, ...]) -> None:
517502 def _update_class_for_field_if_needed (
518503 self ,
519504 keywords : tuple [str , ...],
520- data : DataLike | StandaloneDataLike | SubDictLike ,
505+ data : Data | StandaloneData | SubDict ,
521506 ) -> None :
522507 """Update class field to appropriate field type if this is a field entry."""
523- if (
508+ if isinstance ( data , ( float , int , np . ndarray )) and (
524509 keywords == ("internalField" ,)
525510 or (
526511 len (keywords ) == 3
@@ -532,26 +517,24 @@ def _update_class_for_field_if_needed(
532517 )
533518 )
534519 ) and self .class_ == "dictionary" :
535- try :
536- tensor_kind = _tensor_kind_for_field (data ) # ty: ignore[invalid-argument-type]
537- except ValueError :
538- pass
539- else :
540- self .class_ = "vol" + tensor_kind [0 ].upper () + tensor_kind [1 :] + "Field"
520+ tensor_kind = _tensor_kind_for_field (data )
521+ self .class_ = "vol" + tensor_kind [0 ].upper () + tensor_kind [1 :] + "Field"
541522
542523 def _calculate_spacing (
543524 self ,
544525 keywords : tuple [str , ...],
545526 start : int ,
546527 end : int ,
547- operation : Literal ["put" , "add" ],
528+ / ,
529+ * ,
530+ add : bool ,
548531 ) -> tuple [bytes , bytes ]:
549532 """Calculate before/after spacing for entry operations."""
550533 parsed = self ._get_parsed (missing_ok = True )
551534
552535 # For setitem operations, check if this is an update to an existing entry
553536 # and preserve existing spacing for sub-dictionary entries
554- if operation == "put" :
537+ if not add :
555538 is_update = keywords in parsed
556539 if is_update and len (keywords ) > 1 :
557540 # For existing sub-dictionary entries, preserve existing formatting
@@ -588,105 +571,99 @@ def _calculate_spacing(
588571
589572 def _perform_entry_operation (
590573 self ,
591- keywords : str | tuple [str , ...] | None ,
574+ keywords : tuple [str , ...],
592575 data : DataLike | StandaloneDataLike | SubDictLike ,
593- operation : Literal ["put" , "add" ],
576+ / ,
577+ * ,
578+ add : bool ,
594579 ) -> None :
595580 """Shared method for performing entry operations (setitem and add)."""
596- keywords = self ._normalize_and_validate_keywords (keywords )
581+ if keywords :
582+ keyword = normalize (keywords [- 1 ], bool_ok = False )
583+
584+ if not isinstance (keyword , str ):
585+ msg = (
586+ f"Invalid keyword type: { keywords [- 1 ]} (type { type (keywords [- 1 ])} )"
587+ )
588+ raise TypeError (msg )
589+
590+ if keyword != keywords [- 1 ]:
591+ msg = f"Invalid keyword: { keywords [- 1 ]} "
592+ raise ValueError (msg )
593+
594+ data = normalize (data , keywords = keywords )
595+
596+ indentation = b" " * (len (keywords ) - 1 )
597597
598598 with self :
599599 self ._write_header_if_needed (keywords )
600600 self ._update_class_for_field_if_needed (keywords , data )
601601
602602 parsed = self ._get_parsed (missing_ok = True )
603- start , end = parsed .entry_location (keywords , add = (operation == "add" ))
604- before , after = self ._calculate_spacing (keywords , start , end , operation )
605- self ._process_data_entry (keywords , data , before , after , operation )
603+ start , end = parsed .entry_location (keywords , add = add )
604+ before , after = self ._calculate_spacing (keywords , start , end , add = add )
606605
607- def _process_data_entry (
608- self ,
609- keywords : tuple [str , ...],
610- data : DataLike | StandaloneDataLike | SubDictLike ,
611- before : bytes ,
612- after : bytes ,
613- operation : Literal ["put" , "add" ],
614- ) -> None :
615- """Process and write a data entry using either put or add operation."""
616- parsed = self ._get_parsed (missing_ok = True )
617- indentation = b" " * (len (keywords ) - 1 )
606+ try :
607+ header = self ["FoamFile" ]
608+ assert isinstance (header , FoamFile .SubDict )
609+ except (KeyError , FileNotFoundError ):
610+ header = None
618611
619- if isinstance (data , Mapping ):
620- if not keywords :
621- msg = "Cannot set a mapping at the root level of a FoamFile\n Use update(), extend(), or merge() instead."
622- raise ValueError (msg )
612+ if isinstance (data , Mapping ):
613+ if not keywords :
614+ msg = "Cannot set a mapping at the root level of a FoamFile\n Use update(), extend(), or merge() instead."
615+ raise ValueError (msg )
623616
624- keyword = normalize (keywords [- 1 ], bool_ok = False )
617+ if keyword .startswith ("#" ):
618+ msg = f"Cannot set a directive as the keyword for a dictionary: { keyword } "
619+ raise ValueError (msg )
625620
626- if not isinstance (keyword , str ):
627- msg = (
628- f"Invalid keyword type: { keywords [- 1 ]} (type { type (keywords [- 1 ])} )"
629- )
630- raise TypeError (msg )
621+ if add and keywords in parsed :
622+ raise KeyError (keywords )
631623
632- if keyword .startswith ("#" ):
633- msg = (
634- f"Cannot set a directive as the keyword for a dictionary: { keyword } "
624+ empty_dict_content = (
625+ before
626+ + indentation
627+ + dumps (keyword )
628+ + b"\n "
629+ + indentation
630+ + b"{\n "
631+ + indentation
632+ + b"}"
633+ + after
635634 )
636- raise ValueError ( msg )
635+ parsed . put ( keywords , ..., empty_dict_content )
637636
638- data = normalize (data , keywords = keywords )
639-
640- content = (
641- before
642- + indentation
643- + dumps (keyword )
644- + b"\n "
645- + indentation
646- + b"{\n "
647- + indentation
648- + b"}"
649- + after
650- )
637+ for k , v in data .items ():
638+ self [(* keywords , k )] = v
651639
652- if operation == "add" and keywords in parsed :
653- raise KeyError (keywords )
654-
655- parsed .put (keywords , ..., content )
656- for k , v in data .items (): # ty: ignore[possibly-missing-attribute]
657- self [(* keywords , k )] = v
658-
659- elif keywords :
660- header = self .get ("FoamFile" , None )
661- assert header is None or isinstance (header , FoamFile .SubDict )
662- val = dumps (data , keywords = keywords , header = header )
663-
664- content = (
665- before
666- + indentation
667- + dumps (normalize (keywords [- 1 ], bool_ok = False ))
668- + ((b" " + val ) if val else b"" )
669- + (b";" if not keywords [- 1 ].startswith ("#" ) else b"" )
670- + after
671- )
640+ elif keywords :
641+ val = dumps (data , keywords = keywords , header = header )
672642
673- if operation == "put" :
674- parsed .put (keywords , normalize (data , keywords = keywords ), content )
675- else : # operation == "add"
676- if keywords in parsed and not keywords [- 1 ].startswith ("#" ):
677- raise KeyError (keywords )
678- parsed .add (keywords , normalize (data , keywords = keywords ), content )
643+ content = (
644+ before
645+ + indentation
646+ + dumps (keyword )
647+ + ((b" " + val ) if val else b"" )
648+ + (b";" if not keywords [- 1 ].startswith ("#" ) else b"" )
649+ + after
650+ )
679651
680- else :
681- if operation == "add" and () in parsed :
682- raise KeyError (())
652+ if add :
653+ if keywords in parsed and not keywords [- 1 ].startswith ("#" ):
654+ raise KeyError (keywords )
655+
656+ parsed .add (keywords , data , content )
657+ else :
658+ parsed .put (keywords , data , content )
683659
684- header = self .get ("FoamFile" , None )
685- assert header is None or isinstance (header , FoamFile .SubDict )
660+ else :
661+ if add and () in parsed :
662+ raise KeyError (None )
686663
687- content = before + dumps (data , keywords = (), header = header ) + after
664+ content = before + dumps (data , keywords = (), header = header ) + after
688665
689- parsed .put ((), normalize ( data , keywords = keywords ) , content )
666+ parsed .put ((), data , content )
690667
691668 @overload
692669 def __setitem__ (
@@ -709,15 +686,25 @@ def __setitem__( # ty: ignore[invalid-method-override]
709686 keywords : str | tuple [str , ...] | None ,
710687 data : DataLike | StandaloneDataLike | SubDictLike ,
711688 ) -> None :
712- self ._perform_entry_operation (keywords , data , "put" )
689+ if keywords is None :
690+ keywords = ()
691+ elif not isinstance (keywords , tuple ):
692+ keywords = (keywords ,)
693+
694+ self ._perform_entry_operation (keywords , data , add = False )
713695
714696 @override
715697 def add (
716698 self ,
717699 keywords : str | tuple [str , ...] | None ,
718700 data : DataLike | StandaloneDataLike | SubDictLike ,
719701 ) -> None :
720- self ._perform_entry_operation (keywords , data , "add" )
702+ if keywords is None :
703+ keywords = ()
704+ elif not isinstance (keywords , tuple ):
705+ keywords = (keywords ,)
706+
707+ self ._perform_entry_operation (keywords , data , add = True )
721708
722709 @with_default
723710 @override
@@ -969,11 +956,9 @@ def dumps(
969956 if header is None and ensure_header :
970957 class_ = "dictionary"
971958 if isinstance (file , Mapping ) and "internalField" in file :
972- try :
973- tensor_kind = _tensor_kind_for_field (file ["internalField" ])
974- except (ValueError , TypeError ):
975- pass
976- else :
959+ internal_field = file ["internalField" ]
960+ if isinstance (internal_field , (float , int , np .ndarray )):
961+ tensor_kind = _tensor_kind_for_field (internal_field )
977962 class_ = "vol" + tensor_kind [0 ].upper () + tensor_kind [1 :] + "Field"
978963
979964 header = {"version" : 2.0 , "format" : "ascii" , "class" : class_ }
0 commit comments