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.