ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner - #13820

Merged
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions
Sep 6, 2022
Merged

ARROW-16000: [C++][Python] Dataset: Alternative implementation for adding transcoding function option to CSV scanner#13820
lidavidm merged 31 commits into
apache:masterfrom
joosthooz:ARROW-16000-duplicate-python-readoptions

Conversation

@joosthooz

Copy link
Copy Markdown
Contributor

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'

@github-actions

Copy link
Copy Markdown

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree with Antoine - this approach is cleaner.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: redundant parens

@joosthooz

joosthooz commented Aug 9, 2022

Copy link
Copy Markdown
ContributorAuthor

Ok, I'll close the other PR then, and let's focus on this. I'm seeing some errors in pytest, but I'm also getting those on the commit that I've branched off of. But more concerning is this error in a Windows build here https://ci.appveyor.com/project/ApacheSoftwareFoundation/arrow/builds/44413284/job/ic6l7a0ehrnpk21g#L2654 :

FAILED: lib.cp38-win_amd64.pyd cmd.exe /C "cd . && C:\Miniconda37-x64\envs\arrow\Library\bin\cmake.exe -E vs_link_dll --intdir=CMakeFiles\lib.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK: command "C:\PROGRA~2\MIB055~1\2017\COMMUN~1\VC\Tools\MSVC\1416~1.270\bin\Hostx64\x64\link.exe /nologo CMakeFiles\lib.dir\lib.cpp.obj /out:lib.cp38-win_amd64.pyd /implib:lib.lib /pdb:lib.pdb /dll /version:0.0 /machine:x64 /NODEFAULTLIB:LIBCMT /INCREMENTAL:NO C:\Miniconda37-x64\envs\arrow\libs\python38.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow.lib C:\Miniconda37-x64\envs\arrow\Library\lib\arrow_python.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:lib.cp38-win_amd64.pyd.manifest" failed (exit code 1120) with the following output:
Creating library lib.lib and object lib.exp
lib.cpp.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl arrow::py::MakeStreamTransformFunc(struct arrow::py::TransformInputStreamVTable,struct _object *)" (?MakeStreamTransformFunc@py@arrow@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@UTransformInputStreamVTable@12@PEAU_object@@@Z) referenced in function "class std::shared_ptr<class std::function<class arrow::Result<class std::shared_ptr<class arrow::io::InputStream> > __cdecl(class std::shared_ptr<class arrow::io::InputStream>)> > __cdecl __pyx_f_7pyarrow_3lib_make_streamwrap_func(struct _object *,struct _object *)" (?__pyx_f_7pyarrow_3lib_make_streamwrap_func@@YA?AV?$shared_ptr@V?$function@$$A6A?AV?$Result@V?$shared_ptr@VInputStream@io@arrow@@@std@@@arrow@@V?$shared_ptr@VInputStream@io@arrow@@@std@@@Z@std@@@std@@PEAU_object@@0@Z)
lib.cp38-win_amd64.pyd : fatal error LNK1120: 1 unresolved externals

It seems like the linker is not able to locate the added C++ function arrow::py::MakeStreamTransformFunc when called from the generated cython code. I do notice that the namespace in C++ is just arrow, not arrow::py, but the same goes for MakeTransformInputStream so I don't know why the discrepancy should cause problems for one but not the other.

Comment threadcpp/src/arrow/python/io.h Outdated
@joosthooz
joosthooz marked this pull request as ready for review August 10, 2022 10:07
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

There is 1 failure in the CI, it seems to have been a strange time-out.

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose what's left is adding a test of the transcoder?

@joosthooz

joosthooz commented Aug 15, 2022

Copy link
Copy Markdown
ContributorAuthor

I added a test by copying and modifying the one for the csv reader, but ran into the following problem:
The test for latin-1 encoding works fine. However, the UTF16 test fails because there seems to be something wrong with the schema detection. The equality test between the detected pyarrow.schema and the expected schema fails, even if they seem to be identical from a python point of view (the diffs that pytest prints are identical). Adding some printfs in the C++ code shows that the fields of dataset.schema seem to be empty:

Schema Equals():
this fp: 'S{Fn', other fp: 'S{Fna{@N};Fnb{@O};L}'
this field(0): '', other field(0): 'a: string'

But this looks to me like a different problem with detecting the schema of a UTF16 encoded file. Should I try to create a reproducible example and file a new JIRA? Or is this something we should address here?
In the meantime, I removed the test in 47a3462

@lidavidm

Copy link
Copy Markdown
Member

That sounds like dataset inspection is being done without the transcoder actually being set. I think we do need the test to work. I would expect latin-1 happens to work because it happens that the header row has identical encoding between UTF-8 and latin-1.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

I added a test with a non-utf8 character in the column name (latin-1). That works. Looks like something a bit more specific to utf16. I'll investigate further.

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

After having a better look, here's what seems to be happening:

  • The first part of the test checks if parsing the file as binary still works. But that doesn't work for utf16 because the column names are not utf8. So parsing the column names into the schema fails (silently!).
  • The second part tries to read the file, without specifying an encoding. It expects an exception. However, apparently the dataset reader has no problems with the null values every other character; it will just interpret it as a strange utf8 string.

I've removed those 2 additional checks, and just check if the data is transcoded properly. The 2nd check is still present in the new test_column_names_encoding test (that only tests latin-1)

@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Hi guys, I think the current state should be ok, the failures seem unrelated to me. Is there anything else left for me to do?

@lidavidmlidavidm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay. This looks good to me. @pitrou what do you think about the approach here?

@pitroupitrou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks fine to me. Just a couple comments.

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be visible to the user, how about renaming it to stress it's an internal detail?

Suggested change
public ReadOptions read_options_py
public ReadOptions _read_options_py

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in b9982c8

Comment threadpython/pyarrow/_dataset.pyx Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there can be aliases, for example:

>>>codecs.lookup('utf-8').name'utf-8'>>>codecs.lookup('utf8').name'utf-8'>>>codecs.lookup('UTF8').name'utf-8'

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a lookup to deal with this in 1e621fa

Comment threadpython/pyarrow/tests/test_dataset.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected_table here is unused.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it's been removed in b3ac697

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.
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.
…now we're setting the transcoder in the read_options setter
Schema detection does not seem to be working properly for UTF16.
This reverts commit 47a3462b756cf92594470cedcd0f56eaf6248016.
Testing if reading a utf16 file as binary works fails, because the column names are not utf8 causing issues parsing the schema.
Testing if reading a utf16 file without transcoder fails does not work, because the characters are not invalid utf8 (meaning no error is triggered)
@joosthooz
joosthoozforce-pushed the ARROW-16000-duplicate-python-readoptions branch from 3768fe1 to 1e621faCompareAugust 26, 2022 14:10
@joosthooz

Copy link
Copy Markdown
ContributorAuthor

Anything I can do to help move this forward? The failure seems unrelated to me (cat: r/check/arrow.Rcheck/00install.out: No such file or directory)

@lidavidm
lidavidm merged commit cbf0ec0 into apache:masterSep 6, 2022
@ursabot

Copy link
Copy Markdown

Benchmark runs are scheduled for baseline = a5ecb0f and contender = cbf0ec0. cbf0ec0 is a master commit associated with this PR. Results will be available as each benchmark for each run completes.
Conbench compare runs links:
[Finished ⬇️0.0% ⬆️0.0%] ec2-t3-xlarge-us-east-2
[Failed ⬇️0.61% ⬆️0.41%] test-mac-arm
[Failed ⬇️2.74% ⬆️0.0%] ursa-i9-9960x
[Finished ⬇️0.39% ⬆️0.0%] ursa-thinkcentre-m75q
Buildkite builds:
[Finished] cbf0ec0d ec2-t3-xlarge-us-east-2
[Failed] cbf0ec0d test-mac-arm
[Failed] cbf0ec0d ursa-i9-9960x
[Finished] cbf0ec0d ursa-thinkcentre-m75q
[Finished] a5ecb0ff ec2-t3-xlarge-us-east-2
[Failed] a5ecb0ff test-mac-arm
[Failed] a5ecb0ff ursa-i9-9960x
[Finished] a5ecb0ff ursa-thinkcentre-m75q
Supported benchmarks:
ec2-t3-xlarge-us-east-2: Supported benchmark langs: Python, R. Runs only benchmarks with cloud = True
test-mac-arm: Supported benchmark langs: C++, Python, R
ursa-i9-9960x: Supported benchmark langs: Python, R, JavaScript
ursa-thinkcentre-m75q: Supported benchmark langs: C++, Java

@ursabot

Copy link
Copy Markdown

['Python', 'R'] benchmarks have high level of regressions.
ursa-i9-9960x

zagto pushed a commit to zagto/arrow that referenced this pull request Oct 7, 2022
…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>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@joosthooz@lidavidm@ursabot@pitrou