@@ -276,6 +276,25 @@ def __init__(self):
276276 # warnings.warn('skiplines is deprecated, use header instead', DeprecationWarning)
277277 self .header = self .skiplines
278278
279+ def open (self , filepath , encoding ):
280+ """Open filepath and return an iterable yielding lines of CSV-formatted text.
281+
282+ This method can be overridden in subclasses to customize file reading, for
283+ example to pre-proceess text lines before import. To simply skip a fixed
284+ number of lines at the beginning or end, consider setting the ``header``
285+ or ``footer`` class variables instead.
286+
287+ Args:
288+ filepath: Filesystem path to the input file.
289+ encoding: File encoding as provided to the ``open()`` built-in
290+ function.
291+
292+ Returns:
293+ An iterable providing lines of CSV-formatted text.
294+ """
295+ with open (filepath , encoding = encoding ) as fd :
296+ yield from fd
297+
279298 def read (self , filepath ):
280299 """Read CSV file according to class defined columns specification.
281300
@@ -292,34 +311,35 @@ def read(self, filepath):
292311
293312 """
294313
295- with open (filepath , encoding = self .encoding ) as fd :
296- # Skip header and footer lines.
297- lines = _chomp (fd , self .header , self .footer )
298-
299- # Filter out comment lines.
300- if self .comments :
301- lines = filter (lambda x : not x .startswith (self .comments ), lines )
302-
303- reader = csv .reader (lines , dialect = self .dialect )
304-
305- # Map column names to column indices.
306- names = None
307- if self .names :
308- headers = next (reader , None )
309- if headers is None :
310- raise IndexError ("The input file does not contain an header line" )
311- names = {name .strip (): index for index , name in enumerate (headers )}
312-
313- # Construct a class with attribute accessors for the
314- # configured columns that works similarly to a namedtuple.
315- attrs = {}
316- for name , column in self .columns .items ():
317- attrs [name ] = property (column .getter (names ))
318- row = type ("Row" , (tuple ,), attrs )
319-
320- # Return data rows.
321- for x in reader :
322- yield row (x )
314+ lines = self .open (filepath , self .encoding )
315+
316+ # Skip header and footer lines.
317+ lines = _chomp (lines , self .header , self .footer )
318+
319+ # Filter out comment lines.
320+ if self .comments :
321+ lines = filter (lambda x : not x .startswith (self .comments ), lines )
322+
323+ reader = csv .reader (lines , dialect = self .dialect )
324+
325+ # Map column names to column indices.
326+ names = None
327+ if self .names :
328+ headers = next (reader , None )
329+ if headers is None :
330+ raise IndexError ("The input file does not contain an header line" )
331+ names = {name .strip (): index for index , name in enumerate (headers )}
332+
333+ # Construct a class with attribute accessors for the
334+ # configured columns that works similarly to a namedtuple.
335+ attrs = {}
336+ for name , column in self .columns .items ():
337+ attrs [name ] = property (column .getter (names ))
338+ row = type ("Row" , (tuple ,), attrs )
339+
340+ # Return data rows.
341+ for x in reader :
342+ yield row (x )
323343
324344
325345class Importer (beangulp .Importer , CSVReader ):
0 commit comments