Uh oh!
There was an error while loading. Please reload this page.
ARROW-16000: [C++][Python] Dataset: Added transcoding function option to CSV scanner - #13709
ARROW-16000: [C++][Python] Dataset: Added transcoding function option to CSV scanner#13709joosthooz wants to merge 27 commits into
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Thanks for opening a pull request! If this is not a minor PR. Could you open an issue for this pull request on JIRA? https://issues.apache.org/jira/browse/ARROW Opening JIRAs ahead of time contributes to the Openness of the Apache Arrow project. Then could you also rename pull request title in the following format? or See also: |
| # from io.pxi | ||
| class Transcoder: |
There was a problem hiding this comment.
Hmm, do we really want to jump back to python here instead of using C++ utilities for decoding? (I'm not sure if there are any good standard utilities so maybe the answer is yes).
There was a problem hiding this comment.
We mostly discussed this in the JIRA - you'd have to pull in a library like icu if you want to do it on the C++ side, and also Python (at least) has 'special' encodings like 'unicodereplace' that users may or may not expect to be able to use
There was a problem hiding this comment.
I hope to add that as a possibility in the future, but for now I wanted to mimic the behavior of read_csv as much as possible. We'll have to see how bad of a bottleneck this will create. But for scanning a single file it shouldn't matter, and that is good enough for my use case because I just want to be able to deal with files that are larger than memory (which pyarrow.dataset will allow me to do and read_csv will not)
joosthooz
commented
Jul 28, 2022
The current state is that it works, but it relies on the workaround of adding an |
westonpace
left a comment
There was a problem hiding this comment.
Is it possible to add the encoding option in CsvFileFormat? I think that is the entry point to "fragment scan options" for pyarrow datasets and it appears to be a thin wrapper around CsvFileFormatOptions:
l1_csv_format = ds.CsvFileFormat(read_options=..., parse_options=..., convert_options=..., encoding='latin-1')
my_dataset = ds.dataset([my_files], format=l1_csv_format)
| Parameters | ||
| ---------- | ||
| src_encoding : str | ||
| The codec to use when reading data data. |
There was a problem hiding this comment.
| The codec to use when reading data data. | |
| The codec to use when reading data. |
| Create a function that will add a transcoding transformation to a stream. | ||
| Data from that stream will be decoded according to ``src_encoding`` and | ||
| then re-encoded according to ``dest_encoding``. | ||
| The created function can be used to wrap streams once they are created. |
There was a problem hiding this comment.
| The created function can be used to wrap streams once they are created. | |
| The created function can be used to wrap streams. |
joosthooz
commented
Jul 29, 2022
Thank you for the comments and suggestions. |
Instead of duplicating the encoding field in the CsvFileFormat, we store the encoding in a private field in the CsvFragmentScanOptions. In that class, the read_options.encoding field gets lost when initializing it by using the C struct (which doesn't have the encoding field). So when the read_options are read, we restore it again.
I pushed an alternative way of passing the encoding in 22eff73. For the user it works the same way as in Edit: Hm, it is not working the way I want it yet. The value still gets lost when creating a |
westonpace
commented
Jul 29, 2022
I like this approach if you can get it working. Can you add this to the
That seems undesirable. The C++ csv reader doesn't have the field because it has no ability to handle encodings. So I'm not sure we want to add a field that is completely ignored. |
It needs to be stored in both CsvFileFormat and CsvFragmentScanOptions because if the user has a reference to these separate objects, they would otherwise become inconsistent. 1 would report the default 'utf8' (forgetting the user's encoding choice), while the other would still properly report the requested encoding. To the user it would be unclear which of these values would be eventually used by the transcoding.
joosthooz
commented
Aug 1, 2022
(4d819aa should be |
joosthooz
commented
Aug 1, 2022
Ok I pushed something completely different. I added encoding as a field in the C struct and some wrapper code that tries to |
lidavidm
commented
Aug 1, 2022
I think we're getting a bit far afield…Dynamic linking needs platform-specific code and usually we configure optional dependencies with build flags. What if we add the C++-side field, have it error in C++ if not set to the default, and in python, we can reset the value to the default and configure the transcoder? That leaves us the path to upgrade and should avoid excessive python-side hacks. If we decide it's valuable to have built-in C++ side transcoding, then we have the option there already. An alternative would be to have the Python wrappers for these structs no longer actually wrap the C++ structs, so that we aren't limited to the C++ fields. But that would lead to some code duplication/messiness as well. I'm not sure we can avoid some messiness: the fundamental issue is that we have a Python-only field but are trying to directly wrap the C++ structs. That extra field needs to be mirrored somewhere. Either we do work to pass it around on the Python side or we give in and add it in C++. |
…scoder was supplied
joosthooz
commented
Aug 2, 2022
That sums it up very nicely. Both alternatives are fine with me. I just pushed an update that aims to do what you suggest, which is adding an |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
lidavidm
commented
Aug 2, 2022
It feels like it shouldn't be publicly accessible? Or else it should mirror the C++ side option name 1:1
I guess we can only get pointer equality, but yes |
joosthooz
commented
Aug 4, 2022
I tried this, but it doesn't work, because in that case we would need to re-set the field back to This would be really strange if you ask me. And if we accept this strange behavior, we didn't need to add the |
lidavidm
commented
Aug 5, 2022
Ah, thanks for explaining. Wonder if we should/could pass a copy of the ReadOptions then? |
lidavidm
commented
Aug 5, 2022
But it's not a big deal, I think so long as the field is clearly documented |
pitrou
commented
Aug 8, 2022
@joosthooz Do you want reviewing at this point or are you looking to polish this PR first? |
joosthooz
commented
Aug 9, 2022
pitrou
commented
Aug 9, 2022
I would favor #13820, which pushes complexity into Python, over this one, which introduces a dummy option in C++ that has no effect. |
joosthooz
commented
Aug 9, 2022
Continuing here: #13820 |
…ding transcoding function option to CSV scanner (#13820) This is an alternative version of #13709, to compare what the best approach is. Instead of extending the C++ ReadOptions struct with an `encoding` field, this implementations adds a python version of the ReadOptions object to both `CsvFileFormat` and `CsvFragmentScanOptions`. The reason it is needed in both places, is to prevent these kinds of inconsistencies: ``` >>> import pyarrow.dataset as ds >>> import pyarrow.csv as csv >>> ro =csv.ReadOptions(encoding='iso8859') >>> fo = ds.CsvFileFormat(read_options=ro) >>> fo.default_fragment_scan_options.read_options.encoding 'utf8' ``` Authored-by: Joost Hoozemans <joosthooz@msn.com> Signed-off-by: David Li <li.davidm96@gmail.com>
…ding transcoding function option to CSV scanner (apache#13820) This is an alternative version of apache#13709, to compare what the best approach is. Instead of extending the C++ ReadOptions struct with an `encoding` field, this implementations adds a python version of the ReadOptions object to both `CsvFileFormat` and `CsvFragmentScanOptions`. The reason it is needed in both places, is to prevent these kinds of inconsistencies: ``` >>> import pyarrow.dataset as ds >>> import pyarrow.csv as csv >>> ro =csv.ReadOptions(encoding='iso8859') >>> fo = ds.CsvFileFormat(read_options=ro) >>> fo.default_fragment_scan_options.read_options.encoding 'utf8' ``` Authored-by: Joost Hoozemans <joosthooz@msn.com> Signed-off-by: David Li <li.davidm96@gmail.com>
WIP Adding an optional function that wraps all input streams with a user-supplied transcoding function.