ENH: Add option to use nullable dtypes in read_csv
State: closed

- [x] closes #36712 (Replace xxxx with the Github issue number)
- [x] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [x] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [x] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [x] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.


Labels:
name: IO CSV
description: read_csv, to_csv
name: NA - MaskedArrays
description: Related to pd.NA and nullable extension arrays

Comments:
Probably worth discussing in the issue, but I want to mention here since this will be the first instance of `use_nullable_dtypes`.

**Motivation**: It would be cool to see a state where `read_*(engine="pyarrow")` can result in a DataFrame that is backed by `ArrowExtensionArray` (trying to avoid the conversion to numpy)

Understandably `engine="pyarrow"` doesn't do that today and may be fairly difficult to change (deprecate) that behavior in the future.

An alternative and easier path to my goal would be to have `use_nullable_dtypes="pandas"|"pyarrow"` (default "pandas") to allow the user to pick the "nullable representation". Thoughts?
Wouldn’t it be easier to do this via an option like we are currently inferring for string? I guess this should also apply to our constructors etc? 

Similar to what the final state of nullable is supposed to be. Provide a global option to opt into them 
> Wouldn’t it be easier to do this via an option like we are currently inferring for string?

Hmm so the end state idea could be have like a global option like `mode.nullable=None|"pandas"|"pyarrow"` where dtype/array representation is either numpy/pd.array & pd.NA/ pa.array & pd.NA more or less consistently?

I am taking a particular focus on IO methods since I am hoping to avoid the jump from pa.Table -> np.array -> pa.ChunkedArray (in theory) with `engine="pyarrow"` and just have pa.Table -> pa.ChunkedArray 
Currently the idea is to make a global option to opt into nullable dtypes, yes. I think we can most certainly make this into a three way option to allow arrow too.

But wouldn’t this cause problems on the first operation done with a object backed by a numpy array? 
> But wouldn’t this cause problems on the first operation done with a object backed by a numpy array?

Are you referring to the IO conversion I mentioned?
Ah no, sorry. More like if you get a pyarrow backed object from IO but a NDArray backed object from a constructor and you want to combine them somehow (concat, merge, ...)

Basically what I wanted to ask: Wouldn't it make more sense if everything could be backed by arrow if a single flag is set to avoid these inconsistencies?
> Wouldn't it make more sense if everything could be backed by arrow if a single flag is set to avoid these inconsistencies?

Ah yeah, most definitely. I haven't really encountered/thought too hard about the `op(arrow-backed,  ndarray-backed)` outcomes yet, but a global option would hopefully avoid this.

As long as `use_nullable_types=True` + a global config can lead to maintaining arrow objects from parsing, that would satisfy my goal.  
Thanks @phofl