@@ -622,6 +622,41 @@ impl PyProjectTomlMut {
622622 Ok ( added)
623623 }
624624
625+ /// Ensure that an optional dependency group exists, creating an empty group if it doesn't.
626+ pub fn ensure_optional_dependency ( & mut self , extra : & ExtraName ) -> Result < ( ) , Error > {
627+ // Get or create `project.optional-dependencies`.
628+ let optional_dependencies = self
629+ . project ( ) ?
630+ . entry ( "optional-dependencies" )
631+ . or_insert ( Item :: Table ( Table :: new ( ) ) )
632+ . as_table_like_mut ( )
633+ . ok_or ( Error :: MalformedDependencies ) ?;
634+
635+ // Check if the extra already exists.
636+ let extra_exists = optional_dependencies
637+ . iter ( )
638+ . any ( |( key, _value) | ExtraName :: from_str ( key) . is_ok_and ( |e| e == * extra) ) ;
639+
640+ // If the extra doesn't exist, create it.
641+ if !extra_exists {
642+ optional_dependencies. insert ( extra. as_ref ( ) , Item :: Value ( Value :: Array ( Array :: new ( ) ) ) ) ;
643+ }
644+
645+ // If `project.optional-dependencies` is an inline table, reformat it.
646+ //
647+ // Reformatting can drop comments between keys, but you can't put comments
648+ // between items in an inline table anyway.
649+ if let Some ( optional_dependencies) = self
650+ . project ( ) ?
651+ . get_mut ( "optional-dependencies" )
652+ . and_then ( Item :: as_inline_table_mut)
653+ {
654+ optional_dependencies. fmt ( ) ;
655+ }
656+
657+ Ok ( ( ) )
658+ }
659+
625660 /// Adds a dependency to `dependency-groups`.
626661 ///
627662 /// Returns `true` if the dependency was added, `false` if it was updated.
@@ -693,6 +728,54 @@ impl PyProjectTomlMut {
693728 Ok ( added)
694729 }
695730
731+ /// Ensure that a dependency group exists, creating an empty group if it doesn't.
732+ pub fn ensure_dependency_group ( & mut self , group : & GroupName ) -> Result < ( ) , Error > {
733+ // Get or create `dependency-groups`.
734+ let dependency_groups = self
735+ . doc
736+ . entry ( "dependency-groups" )
737+ . or_insert ( Item :: Table ( Table :: new ( ) ) )
738+ . as_table_like_mut ( )
739+ . ok_or ( Error :: MalformedDependencies ) ?;
740+
741+ let was_sorted = dependency_groups
742+ . get_values ( )
743+ . iter ( )
744+ . filter_map ( |( dotted_ks, _) | dotted_ks. first ( ) )
745+ . map ( |k| k. get ( ) )
746+ . is_sorted ( ) ;
747+
748+ // Check if the group already exists.
749+ let group_exists = dependency_groups
750+ . iter ( )
751+ . any ( |( key, _value) | GroupName :: from_str ( key) . is_ok_and ( |g| g == * group) ) ;
752+
753+ // If the group doesn't exist, create it.
754+ if !group_exists {
755+ dependency_groups. insert ( group. as_ref ( ) , Item :: Value ( Value :: Array ( Array :: new ( ) ) ) ) ;
756+
757+ // To avoid churn in pyproject.toml, we only sort new group keys if the
758+ // existing keys were sorted.
759+ if was_sorted {
760+ dependency_groups. sort_values ( ) ;
761+ }
762+ }
763+
764+ // If `dependency-groups` is an inline table, reformat it.
765+ //
766+ // Reformatting can drop comments between keys, but you can't put comments
767+ // between items in an inline table anyway.
768+ if let Some ( dependency_groups) = self
769+ . doc
770+ . get_mut ( "dependency-groups" )
771+ . and_then ( Item :: as_inline_table_mut)
772+ {
773+ dependency_groups. fmt ( ) ;
774+ }
775+
776+ Ok ( ( ) )
777+ }
778+
696779 /// Set the constraint for a requirement for an existing dependency.
697780 pub fn set_dependency_bound (
698781 & mut self ,
0 commit comments