How to type-annotate object returned by csv.writer?

I ran into problems with the typeshed defs and ended up using the following:

class Writer(Protocol):
    def writerow(self, row: Iterable[Any]) -> Any:
        ...

    def writerows(self, rows: Iterable[Iterable[Any]]) -> None:
        ...


Reader = Iterator[Any]
writer: Writer = csv.writer(open('outfile.csv', 'w'))
reader: Reader = csv.writer(open('outfile.csv', 'w'))

Often when things are acting weird it's a sign typeshed doesn't exactly map to runtime. If you look at _csv in typeshed you will see the type is named _writer. So you should can the annotation to _csv._writer.


Short answer is there is no way to directly access the type. Reading the C source of the _csv module will show that the types of reader and writer are not exposed. Even in Pypy where the _csv module is implemented in Python, doesn't expose the types.

Therefore, if you need to use it, you will need to use a work-around, by instantiating a temporary instance of the writer and getting it's type.

import csv
# We'll need a temporary file-like object, so use a tempfile
from tempfile import TemporaryFile

with TemporaryFile() as t:
    CSVReader = type(csv.reader(t))
    CSVWriter = type(csv.writer(t))

w: CSVWriter = csv.writer('path/to/data.csv')

If you want keep this logic separate, I would suggest creating the types in a separate module

from csv_types import CSVReader, CSVWriter

The other solution (which also involves writing your own module of types), is to follow the example of the typing module in it's definition of types for io and re.


One solution is to write an abstract class that represents the type. This is also how it is done for some classes in the typing module. For the csv.writer() function that is the following:

class _CSVWriter:

    @abstractmethod
    def writerow(self, row: List[str]) -> None:
        pass

    @abstractmethod
    def writerows(self, rows: List[List[str]]) -> None:
        pass

    @abstractproperty
    def dialect(self) -> csv.Dialect:
        pass

Now this class can be used in the type-annotation for the writer object. As the typename of the returned object is still _csv.writer you will still get a type error. To avoid that you need to cast it to a _CSVWriter object.

from typing import cast
writer: _CSVWriter = cast(_CSVWriter, csv.writer(open('test', 'w'))

That solution is a bit verbose but it does the job.

Tags:

Python

Mypy