Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature MIVOT (Model Instance in VOTable) #15390

Merged
merged 15 commits into from Oct 9, 2023
Merged

Conversation

somilia
Copy link
Contributor

@somilia somilia commented Sep 26, 2023

Reading and Writing VO Model Annotations

Introduction

Model Instances in VOTables (MIVOT) defines a syntax for mapping VOTable data to any model serialized in VO-DML (Virtual Observatory Data Modeling Language).

This annotation schema acts as a bridge between data and models. It associates both column/parameter metadata and VOTable data with data model elements (such as classes, attributes, types, etc.). It also complements VOTable data or metadata that may be missing from the table, e.g., coordinate system descriptions or curation tracing.

The data model elements are organized in an independent annotation block complying with the MIVOT XML schema, which is added as an extra resource above the TABLE element. The MIVOT syntax allows a data structure to be described as a hierarchy of classes. It can also represent relationships and compositions between them. Furthermore, it can construct data model objects by aggregating instances from different tables of the VOTable.

Astropy Implementation

The purpose of Astropy is not to process VO annotations. Instead, it allows related packages to get and set Mivot blocks from/into VOTables. Therefore, in this implementation, MIVOT annotations are both imported and exported as strings. The current implementation prevents client code from injecting into VOTables strings that are not Mivot serializations.

MivotBlock Class:

  • Mivot blocks are handled by the MivotBlock class in tree.py.
  • A MivotBlock instance can only be carried by a resource with "type=meta."
  • This instance holds the XML mapping block as a string.
  • MivotBlock objects are instantiated by the Resource parser.
  • The MivotBlock class has its logic that operates both parsing and I/O functionalities.

Affected Modules:

  • tree.py: Creation of the MivotBlock, addition of a MivotBlock instance to the Resource class as a property, and inclusion of a call to the MIVOT parser when the Resource parser encounters a VODML tag.
  • writer.py: The function string_element() has been added to indent the MivotBlock XML before pushing it in the writer.
  • test_7_votable_pointer.py: Some usage examples are described in examples/mivotblock.
  • exceptions.py: Addition of warning (ModelMappingSpecWarning) and error (E26) classes related to MIVOT.

Description

This pull request is for a new feature that allows the reading and the writing of model annotations in VOTable

Fixes #12153

  • By checking this box, the PR author has requested that maintainers do NOT use the "Squash and Merge" button. Maintainers should respect this when possible; however, the final decision is at the discretion of the maintainer that merges the PR.

@github-actions
Copy link

Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • Is a milestone set? Milestone must be set but we cannot check for it on Actions; do not let the green checkmark fool you.
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

@pllim pllim added this to the v6.0 milestone Sep 26, 2023
@somilia
Copy link
Contributor Author

somilia commented Sep 26, 2023

pre-commit.ci autofix

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Welcome to Astropy 👋 and congratulations on your first pull request! 🎉

A project member will respond to you as soon as possible; in the meantime, please have a look over the Checklist for Contributed Code and make sure you've addressed as many of the questions there as possible.

If you feel that this pull request has not been responded to in a timely manner, please send a message directly to the development mailing list. If the issue is urgent or sensitive in nature (e.g., a security vulnerability) please send an e-mail directly to the private e-mail feedback@astropy.org.

@lmichel
Copy link
Contributor

lmichel commented Sep 26, 2023

pllim added this to the v6.0 milestone

I'm a new comer in the Astropy dev workflow and I do not get the meaning of this action.
Does it mean that the merge will occur with v6 and not before or that this PR will be regularly merged an then reported on the v6 check points?

@pllim
Copy link
Member

pllim commented Sep 26, 2023

Re: #15390 (comment)

Not exactly. The milestone means this feature is currently slated for v6.0 release. We move the milestone if this PR does not get merged before feature freeze (also see https://github.com/astropy/astropy/wiki/Release-Calendar).

Copy link
Member

@pllim pllim left a comment

Choose a reason for hiding this comment

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

Thanks! It is my understanding that PyVO is a stakeholder and its developers would review this in depth later. I am just leaving a general review.

Unless you are willing to let maintainer use "squash and merge" button for this PR, all the merge commits in your commit history here need to go (a rebase should do it in most cases).

Probably needs a What's New entry too.

If the API or behavior is expected to change after this code is released, please also add a warning box in the user documentation saying that the API or behavior is unstable and please be specific.

For the documentation that gets rendered to be read by users, please be consistent in capitalization (mivot vs MIVOT vs Mivot, VOtable vs VOTable, and so on). If different capitalization signifies different things (e.g., astropy vs Astropy), that is fine but you might have to explain.

When this PR is ready for final review, might want to add the "Extra CI" label too, just in case.

astropy/io/votable/exceptions.py Outdated Show resolved Hide resolved
@@ -175,3 +177,333 @@ def test_votable_tag():
assert 'xmlns="http://www.ivoa.net/xml/VOTable/v1.3"' in xml
assert 'xsi:schemaLocation="http://www.ivoa.net/xml/VOTable/v1.3 ' in xml
assert 'http://www.ivoa.net/xml/VOTable/VOTable-1.4.xsd"' in xml


def squash_xml(data):
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, no better way to do this? If not, is this function better in astropy/utils/xml? Or is this function not useful beyond this test module?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We think this function won't be useful beyond the test module.
We tried to do it in other way: using etree.XMLParser() or using re.sub() with regexep, in the end this function was more convenient and simpler.

# a mivot block must be with "type=meta"
mivot_resource.mivot_block = mivot_block
raise AssertionError()
except E26:
Copy link
Member

Choose a reason for hiding this comment

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

Why not use pytest.raises here?

assert True


def setup_function(test_mivot_order):
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand why setup/teardown is needed here. Why not use tmp_path for your test?

astropy/io/votable/tree.py Outdated Show resolved Hide resolved
docs/io/votable/index.rst Outdated Show resolved Hide resolved
docs/io/votable/index.rst Outdated Show resolved Hide resolved
docs/io/votable/index.rst Outdated Show resolved Hide resolved
docs/io/votable/index.rst Outdated Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

I have always wanted to kill off the example gallery. I see it as a failed experiment that had since been superseded by Learn Astropy efforts outside the core library. I'd rather we not add more stuff to it now. Also see #7242 (comment)

Can this example be integrated into docs/io/votable somewhere instead?

@pllim
Copy link
Member

pllim commented Sep 27, 2023

Also, please note that v6.0 feature freeze is planned for Oct 2023 in about a month (https://github.com/astropy/astropy/wiki/Release-Calendar). So your collaborators and stakeholders should start reviewing this sooner than later, or it might not make the cut.

@pllim
Copy link
Member

pllim commented Sep 27, 2023

p.s. For future reference, you could accept suggested changes in batch mode.

@somilia
Copy link
Contributor Author

somilia commented Sep 27, 2023

Thanks! It is my understanding that PyVO is a stakeholder and its developers would review this in depth later. I am just leaving a general review.

Unless you are willing to let maintainer use "squash and merge" button for this PR, all the merge commits in your commit history here need to go (a rebase should do it in most cases).

Probably needs a What's New entry too.

If the API or behavior is expected to change after this code is released, please also add a warning box in the user documentation saying that the API or behavior is unstable and please be specific.

For the documentation that gets rendered to be read by users, please be consistent in capitalization (mivot vs MIVOT vs Mivot, VOtable vs VOTable, and so on). If different capitalization signifies different things (e.g., astropy vs Astropy), that is fine but you might have to explain.

When this PR is ready for final review, might want to add the "Extra CI" label too, just in case.

Thank you for the general review, changes will be coming soon.

Regarding the messy commit history, we would like a squash and merge if possible.


data_path = os.path.dirname(os.path.realpath(__file__))
vpath = os.path.join(data_path, "data/test.order.xml")
vpath_out = os.path.join(data_path, tmp_path / "test.order.out.xml")
Copy link
Member

Choose a reason for hiding this comment

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

You want to limit writing out test file only to the temporary test dir.

Suggested change
vpath_out = os.path.join(data_path, tmp_path / "test.order.out.xml")
vpath_out = str(tmp_path / "test.order.out.xml")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the precision, changes have been made

Comment on lines 477 to 478
data_path = os.path.dirname(os.path.realpath(__file__))
vpath = os.path.join(data_path, "data/test.order.xml")
Copy link
Member

Choose a reason for hiding this comment

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

I think you can just use our built-in get_pkg_data_filename() from astropy.utils.data.

Suggested change
data_path = os.path.dirname(os.path.realpath(__file__))
vpath = os.path.join(data_path, "data/test.order.xml")
vpath = get_pkg_data_filename("data/test.order.xml")

@pllim
Copy link
Member

pllim commented Sep 28, 2023

You might not want to enable doctest on your What's New entry since it is trying to read a non-existent file. If you do want to use a test file you added, then you can do something like this instead:

>>> from astropy.utils.data import get_pkg_data_filename

>>> filename = get_pkg_data_filename("data/votable.xml", package="astropy.io.votable.tests")  # If this file is in io/votable/tests/data, for example
# Then you can run the rest of the doctest with this filename

@pllim pllim mentioned this pull request Sep 30, 2023
1 task
@msdemlei
Copy link
Contributor

msdemlei commented Oct 2, 2023

Hm... while I agree that astropy-core probably shouldn't know too much about DMs as such, I'd say that the API implemented here is too poor to make much sense, for all I can see not even as a basis for later, more refined APIs: On reading, what do you expect me to do with your MivotBlock? On writing, how do I come up with it?

For an alternative DM annotation, I once have implemented a somewhat more concrete API in https://github.com/msdemlei/astropy, where you could say things like

annotations = column.get_annotations("prefix:ClassName")

and then get back objects that would support attribute access for the various class members somewhat like this:

annotations[0].space.frame.orientation

I give you that because MIVOT is a lot more complex than the annotation scheme proposed in msdemlei/astropy, perhaps a less straightforward API may be called for – but then, having something like get_annotations even just for a subset of MIVOT would, I think, help people doing actual stuff once we have DM annotations out in the wild a lot more than the fairly opaque MivotBlock.

@lmichel
Copy link
Contributor

lmichel commented Oct 2, 2023

Hm... while I agree that astropy-core probably shouldn't know too much about DMs as such, I'd say that the API implemented here is too poor to make much sense, for all I can see not even as a basis for later, more refined APIs: On reading, what do you expect me to do with your MivotBlock? On writing, how do I come up with it?

For an alternative DM annotation, I once have implemented a somewhat more concrete API in https://github.com/msdemlei/astropy, where you could say things like

annotations = column.get_annotations("prefix:ClassName")

and then get back objects that would support attribute access for the various class members somewhat like this:

annotations[0].space.frame.orientation

I give you that because MIVOT is a lot more complex than the annotation scheme proposed in msdemlei/astropy, perhaps a less straightforward API may be called for – but then, having something like get_annotations even just for a subset of MIVOT would, I think, help people doing actual stuff once we have DM annotations out in the wild a lot more than the fairly opaque MivotBlock.

Markus,

You are right our API is very poor. Actually it does nothing except reading and writing XML strings and nothing in the ASTROPY code can work with that, this is true. These IO operations are implemented in AStropy just because the VOTable parser is here.
We chose this option last year after a few DM running meetings because we would like to avoid introducing any sort of model knowledge into Astropy. This is not the right place for this.
Actually the code you refer to is being implemented in PyVO.
Our roadmap is to open PyVO PRs just after this PR has been merged.

@pllim
Copy link
Member

pllim commented Oct 2, 2023

Anyone interested in this PR has any issue with changes to tree.py over at #15406 ?

@somilia
Copy link
Contributor Author

somilia commented Oct 3, 2023

Anyone interested in this PR has any issue with changes to tree.py over at #15406 ?

After checking the lines, this PR shouldn't conflict or impact MIVOT PR.

Copy link
Contributor

@tomdonaldson tomdonaldson left a comment

Choose a reason for hiding this comment

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

Thanks @lmichel and @somilia! This MIVOT support seems very complete to me, and the tests and documentation look good. Other than a couple minor comments from me above, I think this is ready to go.

@somilia
Copy link
Contributor Author

somilia commented Oct 6, 2023

Thanks @lmichel and @somilia! This MIVOT support seems very complete to me, and the tests and documentation look good. Other than a couple minor comments from me above, I think this is ready to go.

Thank you for your review and comments, changes have been made.

@pllim
Copy link
Member

pllim commented Oct 6, 2023

p.s. Once VO reviewers approved, I would still go through the code one more time to make sure it adheres to Astropy standards. So please do not merge yet. But let me know if I can merge when I am happy with it. Thanks!

Copy link
Member

@pllim pllim left a comment

Choose a reason for hiding this comment

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

Just need a little more minor clean-ups, otherwise LGTM. Thanks!

astropy/io/votable/tree.py Outdated Show resolved Hide resolved
astropy/io/votable/tree.py Outdated Show resolved Hide resolved
astropy/io/votable/tree.py Outdated Show resolved Hide resolved
astropy/io/votable/tree.py Outdated Show resolved Hide resolved
docs/changes/io.votable/15390.feature.rst Outdated Show resolved Hide resolved
docs/io/votable/index.rst Outdated Show resolved Hide resolved
docs/io/votable/index.rst Show resolved Hide resolved
docs/whatsnew/6.0.rst Show resolved Hide resolved
docs/whatsnew/6.0.rst Outdated Show resolved Hide resolved
@pllim
Copy link
Member

pllim commented Oct 9, 2023

Something went wrong with the commit history. I thought @bsipocz squashed the stuff into 14 commits last Friday. So with 2 new commits, it should have been 16. But I see 56 commits now. And the CI failure does not make sense. So something is wrong.

@bsipocz
Copy link
Member

bsipocz commented Oct 9, 2023

I suspect the old branch got force-pushed back rather than the rebased version pulled and updated. I still have the 14-commit version around locally and can push it back. As I recall that one was passing CI and was waiting for your review., but was otherwise ready to go.

@bsipocz
Copy link
Member

bsipocz commented Oct 9, 2023

Ahh, looking at the branch structure, the second to last commit is still correct, the problem is with the last one only, as I suspect that was done locally on the outdated branch while second to last was done directly on github.

Co-authored-by: P. L. Lim <2090236+pllim@users.noreply.github.com>
@pllim
Copy link
Member

pllim commented Oct 9, 2023

devdeps failure is unrelated (numpy-dev again), so merging. Thanks for your patience and contribution, all!

@pllim pllim merged commit 186a955 into astropy:main Oct 9, 2023
24 of 26 checks passed
@lmichel
Copy link
Contributor

lmichel commented Oct 10, 2023

Great, tanks to all of the reviewers.

Sign up for free to 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.

Accessing to the raw content of astropy.io.votable.tree.Resource(s)
6 participants