11"""Fixture output configuration for generated test fixtures.""" 
22
3+ import  shutil 
34import  tarfile 
45from  pathlib  import  Path 
56
@@ -22,6 +23,10 @@ class FixtureOutput(BaseModel):
2223            "write each fixture to its own file" 
2324        ),
2425    )
26+     clean : bool  =  Field (
27+         default = False ,
28+         description = "Clean (remove) the output directory before filling fixtures." ,
29+     )
2530
2631    @property  
2732    def  directory (self ) ->  Path :
@@ -53,11 +58,78 @@ def strip_tarball_suffix(path: Path) -> Path:
5358            return  path .with_suffix ("" ).with_suffix ("" )
5459        return  path 
5560
56-     def  create_directories (self ) ->  None :
57-         """Create output and metadata directories if needed.""" 
61+     def  is_directory_empty (self ) ->  bool :
62+         """Check if the output directory is empty.""" 
63+         if  not  self .directory .exists ():
64+             return  True 
65+ 
66+         return  not  any (self .directory .iterdir ())
67+ 
68+     def  get_directory_summary (self ) ->  str :
69+         """Return a summary of directory contents for error reporting.""" 
70+         if  not  self .directory .exists ():
71+             return  "directory does not exist" 
72+ 
73+         items  =  list (self .directory .iterdir ())
74+         if  not  items :
75+             return  "empty directory" 
76+ 
77+         dirs  =  [d .name  for  d  in  items  if  d .is_dir ()]
78+         files  =  [f .name  for  f  in  items  if  f .is_file ()]
79+ 
80+         max_dirs  =  4 
81+         summary_parts  =  []
82+         if  dirs :
83+             summary_parts .append (
84+                 f"{ len (dirs )}  
85+                 +  (
86+                     f" ({ ', ' .join (dirs [:max_dirs ])}  
87+                     +  (f"... and { len (dirs ) -  max_dirs }   if  len (dirs ) >  max_dirs  else  "" )
88+                     +  ")" 
89+                     if  dirs 
90+                     else  "" 
91+                 )
92+             )
93+         if  files :
94+             summary_parts .append (
95+                 f"{ len (files )}  
96+                 +  (
97+                     f" ({ ', ' .join (files [:3 ])}  
98+                     +  (f"... and { len (files ) -  3 }   if  len (files ) >  3  else  "" )
99+                     +  ")" 
100+                     if  files 
101+                     else  "" 
102+                 )
103+             )
104+ 
105+         return  " and " .join (summary_parts )
106+ 
107+     def  create_directories (self , is_master : bool ) ->  None :
108+         """ 
109+         Create output and metadata directories if needed. 
110+ 
111+         If clean flag is set, remove and recreate the directory. 
112+         Otherwise, verify the directory is empty before proceeding. 
113+         """ 
58114        if  self .is_stdout :
59115            return 
60116
117+         # Only the master process should delete/create directories if using pytest-xdist 
118+         if  not  is_master :
119+             return 
120+ 
121+         if  self .directory .exists () and  self .clean :
122+             shutil .rmtree (self .directory )
123+ 
124+         if  self .directory .exists () and  not  self .is_directory_empty ():
125+             summary  =  self .get_directory_summary ()
126+             raise  ValueError (
127+                 f"Output directory '{ self .directory }  
128+                 f"Contains: { summary }  
129+                 "or specify a different output directory." 
130+             )
131+ 
132+         # Create directories 
61133        self .directory .mkdir (parents = True , exist_ok = True )
62134        self .metadata_dir .mkdir (parents = True , exist_ok = True )
63135
@@ -72,22 +144,12 @@ def create_tarball(self) -> None:
72144                    arcname  =  Path ("fixtures" ) /  file .relative_to (self .directory )
73145                    tar .add (file , arcname = arcname )
74146
75-     @classmethod  
76-     def  from_options (
77-         cls , output_path : Path , flat_output : bool , single_fixture_per_file : bool 
78-     ) ->  "FixtureOutput" :
79-         """Create a FixtureOutput instance from pytest options.""" 
80-         return  cls (
81-             output_path = output_path ,
82-             flat_output = flat_output ,
83-             single_fixture_per_file = single_fixture_per_file ,
84-         )
85- 
86147    @classmethod  
87148    def  from_config (cls , config : pytest .Config ) ->  "FixtureOutput" :
88149        """Create a FixtureOutput instance from pytest configuration.""" 
89150        return  cls (
90151            output_path = config .getoption ("output" ),
91152            flat_output = config .getoption ("flat_output" ),
92153            single_fixture_per_file = config .getoption ("single_fixture_per_file" ),
154+             clean = config .getoption ("clean" ),
93155        )
0 commit comments