Skip to content

VO table no longer access char field as bytes - #9505

Merged
tomdonaldson merged 12 commits into
astropy:masterfrom
pllim:vo-char-no-bytes
Mar 3, 2020
Merged

tomdonaldson merged 12 commits into
astropy:masterfrom
pllim:vo-char-no-bytes

Conversation

@pllim

@pllim pllim commented Oct 29, 2019 •

Copy link
Copy Markdown
Member

Description

This pull request is to address the issue of VO table parsing char field as bytes unnecessarily.

This does change a subtle behavior, so it should only go into a major release. I suspect it would affect packages downstream, e.g., astroquery.vo_conesearch or whoever is parsing VO table using Table.read() and then decoding bytes to UTF-8 themselves.

So, the gist is that VO table parser was relying on bytes vs str to differentiate between char and unicodeChar fields. A quick glance at VO table standards did not seem to indicate that this is a rule from the standards. Since I can't ask Mike D anymore, I am wildly guessing that the behavior was something that was just convenient to do at the time in Python 2 but turned into a nuisance in Python 3.

Note to reviewers: Those who are familiar with the VO standards and use VO table a lot should really look at this carefully and think about the implications of merging this.

TODO:

  • Wait for in-depth discussions from VO table stakeholders.
  • Add change log.

Fixes #8716

Supersedes #8745

@pllim pllim added this to the v4.1 milestone Oct 29, 2019
@pllim
pllim requested review from tboch and tomdonaldson October 29, 2019 17:46
def _binparse_var(self, read):
length = self._parse_length(read)
return read(length), False
return read(length).decode('ascii'), False

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note: This no longer choose between 'ascii' and 'utf-8' based on 'verify' setting. This one is stricter, but I suspect it wouldn't be hard to add it back in, though the VO table standard did say char is ASCII. 🤷‍♀

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

With the updated commit, I changed this behavior to allow non-ASCII characters for FIELDs with datatype="char" in TABLEDATA serializations, but with a new warning message. Whether all ASCII or not, values for those FIELDs are stored in the astropy Table as strings.

@hugobuddel hugobuddel Dec 20, 2019 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It seems to me that @pllim's comment is still valid; but I'm not familiar with this code so maybe I'm misreading it. As in, putting utf-8 text in a char field and setting 'verify' to 'ignore' seems to raise an exception, but I've not actually tried this. I'm in favor of allowing utf-8, even if it is not in the official standard.
Edit to clarify: I meant trying to read non-utf-8 char fields, not writing them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @hugobuddel . Thanks for the taking a look at this.

The context here a is a little misleading. You are correct that in the _binparse_var() method shown above, non-ASCII characters will cause an exception. This occurs for the BINARY and BINARY2 serializations when any of the byte values are outside the range 0-127. I think that behavior is probably OK since values outside that range may just as likely represent corrupt binary data as opposed to deliberate characters in the extended ASCII range (128-255), but I could go either way. I should (and will) at least add a test case to demonstrate that as the expected behavior.

My comment above though was referring to the parse() method that is just above _binparse_var(). parse() handles the TABLEDATA serialization (in which non-ASCII unicode might most easily sneak in). In that case a try clause catches the decode exception and calls vo_warn() with a new warning message. vo_warn() looks at the verify value to decide whether to show the message.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the clarification @tomdonaldson . Adding tests would indeed help, because that's where I looked to see whether my interpretation of the code was correct, but it is clear now.

I'm personally not using the BINARY/BINARY2 serializations (only FITS or TABLEDATA), so I'll let others decide. (At least, I'm not writing them; no idea whether I read them from other sources.)

@tomdonaldson

Copy link
Copy Markdown
Contributor

This PR has been updated to ensure that it handles both fixed-length and variable length strings. The upshot is that for FIELDs with datatype="char", the values will always be stored in the astropy Table as str instead of bytes (e.g., 'myvalue' instead of b'myvalue').

That behavior was already the case for FIELDs with datatype="unicodeChar", so the general idea was to make the char handling more like unicodeChar, but with some differences depending on the serialization.

For TABLEDATA serialization:

  • char is read in just like unicodeChar except that if non-ASCII characters are present for char, a warning is issued. This behavior is similar to many existing VOTable parsers which also allow non-ASCII characters, often without a warning.
  • On write, an exception is raised if attempting to write non-ASCII characters to a char FIELD. The stricter enforcement here is to prevent astropy from writing a VOTable that is not compliant with the spec.

For BINARY and BINARY2:

  • The FIELD datatype determines the number of bytes per character, so char values are ASCII-encoded into strs, and unicodeChar values are UTF-16-BE -encoded into strs.

For all serializations, the astropy Table column dtype is O for variable length strings and U<length> for fixed length strings. This is consistent with the prior behavior except that fixed length char strings were S<length>.

@msdemlei

Copy link
Copy Markdown
Contributor

Without having looked at the patch as such – someone will have to poke me again for that –, I heartily support the intent of the PR. VOTable char[] most definitely is supposed to be an ASCII string (as opposed to unsignedByte), and so it shouldn't have been turned into bytes in the first place.

I kind-of also support interpreting content as utf-8; I've spoken for this ages ago (http://mail.ivoa.net/pipermail/apps/2014-October/001010.html is a thread from 2014), and if astropy establishes a quasi-standard here, that may actually push this along. It should be made clear somewhere that it's possible that behaviour with non-ASCII in char is actually undefined by the IVOA.

Of course, this will break some code that did something with strings from VOTable. But bytes coming out of VOTable are so painful that to me that seems worth it.

Hence: informally, +1.

@pllim pllim left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

At this point, the changes are beyond my field of expertise, so I heavily rely on @tomdonaldson and those whose work rely on VOTable. Nonetheless, I left some general comments. Thank you very much for taking this over, Tom D!

dtypes = [
(('string test', 'string_test'), '|O8'),
(('fixed string test', 'string_test_2'), '|S10'),
(('fixed string test', 'string_test_2'), '<U10'),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is this type change in the expected results still abide by the VOTable standards? (Asking as a person unfamiliar with those standards. 😬 )

Comment thread astropy/table/tests/test_showtable.py Resolved
Comment thread astropy/table/tests/test_showtable.py Resolved
@funbaker

funbaker commented Feb 5, 2020

Copy link
Copy Markdown
Contributor

Reading the ascii datatype as unicode char is fine, as long there is a warning emitted. This is necessary for writing a service compliance checker with astropy/pyvo (This idea came up somewhere).

I'm missing a vo_raise(E24) when trying to write out non ascii values in the output method of the char datatype.

@pllim

pllim commented Feb 20, 2020

Copy link
Copy Markdown
Member Author

@tomdonaldson , do you think this is ready for another round of review, or are you still waiting to add more code or hear back from VO stakeholders?

@tomdonaldson

Copy link
Copy Markdown
Contributor

I'm missing a vo_raise(E24) when trying to write out non ascii values in the output method of the char datatype.

@funbaker Thanks for noticing that! I've added code to the Char converter that warns if the value contains non-ASCII characters, and added corresponding test cases. Perhaps that check can be a little cleaner with isascii() once we require Python 3.7+.

@pllim With this last commit, this is ready for another round of review. Perhaps @funbaker and/or @hugobuddel could also have another look? It would be great to get this in for the upcoming 4.0.1 release.

To summarize, I believe we've changed the behavior to parse all char and unicodeChar VOTable FIELDs into str instead of bytes objects (allowing but warning on non-ASCII values for the char case). We've also added checks on outputting non-ASCII values to char FIELDs (warnings for TABLEDATA serialization and errors for BINARY and BINARY2 serializations). Outside of those changes, the previous behaviors, assumptions and test coverage should have been preserved.

@pllim

pllim commented Feb 24, 2020

Copy link
Copy Markdown
Member Author

Perhaps that check can be a little cleaner with isascii() once we require Python 3.7+

Note to self: Open a follow-up issue for this after this PR is merged.

@pllim pllim left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Just some minor comments. Thank you, all!

p.s. I am still technically the owner of this PR so I cannot approve this. But that also means you can, @tomdonaldson ! 😉

Comment thread astropy/io/votable/converters.py Resolved
Comment thread astropy/io/votable/exceptions.py Resolved
Comment thread astropy/io/votable/exceptions.py Resolved

@tomdonaldson tomdonaldson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

After a couple iterations of review on the intent and logic, and detailed code reviews by the maintainers, I think this is ready to be merged. Thanks to all contributors on this!

@tomdonaldson
tomdonaldson merged commit e78c72f into astropy:master Mar 3, 2020
@pllim
pllim deleted the vo-char-no-bytes branch March 3, 2020 16:01
@pllim

pllim commented Mar 3, 2020

Copy link
Copy Markdown
Member Author

Now I gotta clean up astroquery.vo_conesearch... 😅

bsipocz pushed a commit that referenced this pull request Mar 4, 2020
VO table no longer access char field as bytes
@pllim pllim added the API change PRs and issues that change an existing API, possibly requiring a deprecation period label Mar 5, 2020
@pllim pllim modified the milestones: v4.0.1, v4.1 Mar 5, 2020
@pllim

pllim commented Mar 5, 2020

Copy link
Copy Markdown
Member Author

@bsipocz and @tomdonaldson , as discussed on Slack, since this change is causing some downstream breakage (e.g., astroquery), I am remilestoning to 4.1 instead of 4.0.1. It is my understanding that there is no action needed on my part in terms of consolidating the change log. This PR needs to be backed out of the 4.0.x branch too. I apologize for any inconvenience caused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API change PRs and issues that change an existing API, possibly requiring a deprecation period Bug io.votable Ready-for-final-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extended Table unicode sandwich for varchar data access from votable

6 participants