@@ -54,6 +54,19 @@ class ColumnsMultiParam(MultiParam):
5454CSV_DELIMITER_CHOICES = {"," : "," , ";" : ";" , "|" : "|" , "TAB" : "\t " , "SPACE" : " " }
5555
5656
57+ def _openfile (filename , mode ):
58+ if filename .endswith (".gz" ):
59+ return gzip .open (filename , mode )
60+ elif filename .endswith (".bz2" ):
61+ return bz2 .open (filename , mode )
62+ elif filename .endswith (".xz" ):
63+ return lzma .open (filename , mode )
64+ elif "b" in mode :
65+ return open (filename , mode ) # pylint: disable=unspecified-encoding
66+ else :
67+ return open (filename , mode , encoding = "utf-8" )
68+
69+
5770class LoadCsvPlugin (LoadFileDeGlobMixin , LoadFileWithFilenameMixin , DuckdbLoadFilePlugin ):
5871 """Load CSV files"""
5972
@@ -70,29 +83,19 @@ class LoadCsvPlugin(LoadFileDeGlobMixin, LoadFileWithFilenameMixin, DuckdbLoadFi
7083 def load_file (
7184 self , cursor : duckdb .DuckDBPyConnection , filename : str , file_param : BaseParam , row_limit : Optional [int ] = None
7285 ) -> duckdb .DuckDBPyRelation :
73- options = {
74- "sep" : CSV_DELIMITER_CHOICES [self .delimiter .value ],
75- "null_padding" : True ,
76- "strict_mode" : False ,
77- }
78-
79- if len (self .columns ):
80- options ["all_varchar" ] = True
81- options ["skiprows" ] = 1 if self .header else 0
82- options ["header" ] = False
83- else :
84- options ["header" ] = self .header .value
85-
86- if filename .endswith (".xz" ):
87- logger .debug ("Reading file %s with LZMA" , filename )
88- with lzma .open (filename ) as fh :
89- rel = duckdb_source_to_table (cursor , cursor .read_csv (fh , ** options ))
90- if filename .endswith (".bz2" ):
91- logger .debug ("Reading file %s with BZ2" , filename )
92- with bz2 .open (filename ) as fh :
93- rel = duckdb_source_to_table (cursor , cursor .read_csv (fh , ** options ))
94- else :
95- rel = duckdb_source_to_view (cursor , cursor .read_csv (filename , ** options ))
86+ with _openfile (filename , "rb" ) as fh :
87+ rel = duckdb_source_to_table (
88+ cursor ,
89+ cursor .read_csv (
90+ fh ,
91+ sep = CSV_DELIMITER_CHOICES [self .delimiter .value ],
92+ null_padding = True ,
93+ strict_mode = False ,
94+ all_varchar = len (self .columns ) > 0 ,
95+ skiprows = 1 if self .header and len (self .columns ) > 0 else 0 ,
96+ header = self .header .value and len (self .columns ) == 0 ,
97+ ),
98+ )
9699
97100 if row_limit is not None :
98101 rel = rel .limit (row_limit )
@@ -119,7 +122,6 @@ def load_file(
119122 if cp is None or cp .type .is_not_none ()
120123 )
121124
122-
123125 logger .debug ("LoadCsvPlugin.load_file proj %s" , proj )
124126 rel = rel .project (proj )
125127
@@ -174,7 +176,6 @@ class SaveCsvPlugin(DuckdbSaveFilePlugin):
174176 def execute (
175177 self , ddbc : DuckDBPyConnection , source : Optional [DuckDBPyRelation ], row_limit : Optional [int ] = None
176178 ) -> None :
177-
178179 if source is None or row_limit is not None or not self .filename :
179180 return
180181
@@ -185,7 +186,6 @@ def execute(
185186 logger .debug ("SaveCsvPlugin.execute order_by %s" , order_by )
186187 source = source .order (order_by )
187188
188-
189189 options = {
190190 "index" : False ,
191191 "sep" : self .SEPARATORS [self .delimiter .value ],
@@ -195,20 +195,12 @@ def execute(
195195 filename = self .filename .get_file_path ()
196196 Path (filename ).parent .mkdir (parents = True , exist_ok = True )
197197
198- def _openfile (filename ):
199- if filename .endswith (".gz" ):
200- return gzip .open (filename , "wb" )
201- elif filename .endswith (".bz2" ):
202- return bz2 .open (filename , "wb" )
203- else :
204- return open (filename , "wb" )
205-
206198 # the type check supression is because the first parameter to
207199 # to_csv is called path_or_buf and takes either a filename path
208200 # or a file-like buffer, but is declared as str|None.
209201
210202 logger .debug ("writing file %s" , filename )
211- with _openfile (filename ) as fh :
203+ with _openfile (filename , "wb" ) as fh :
212204 chunk = source .fetch_df_chunk ()
213205 chunk .to_csv (fh , header = True , ** options ) # type: ignore
214206 while len (chunk ) > 0 :
0 commit comments