Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

pyvo

Package Overview
Dependencies
Maintainers
5
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pyvo - npm Package Compare versions

Comparing version
1.8
to
1.8.1
+5
-5
.github/workflows/ci_devtests.yml

@@ -25,5 +25,5 @@ # This test job is separated out into its own workflow to be able to trigger separately

steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python 3.13
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:

@@ -37,3 +37,3 @@ python-version: "3.13"

- name: Upload coverage to codecov
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with:

@@ -47,5 +47,5 @@ file: ./coverage.xml

steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python 3.14
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:

@@ -52,0 +52,0 @@ python-version: "3.14-dev"

@@ -39,7 +39,7 @@ # Developer version testing is in separate workflow

- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:

@@ -61,7 +61,7 @@ python-version: ${{ matrix.python-version }}

- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:

@@ -80,5 +80,5 @@ python-version: '3.12'

steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python 3.12
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:

@@ -97,5 +97,5 @@ python-version: '3.12'

steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Python 3.10
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:

@@ -102,0 +102,0 @@ python-version: '3.10'

@@ -0,1 +1,20 @@

1.8.1 (2026-02-12)
==================
Bug Fixes
---------
- Pass session through to DatalinkService requests. [#716]
- Add DALRateLimitError for HTTP 429 responses with retry timing info. [#718]
- Fix a bug in the space frame equinox processing. [#710]
- Add DEFAULT_JOB_POLL_TIMEOUT constant. [#721]
- Fix SIA2 overflow warnings. [#727]
- Add timeout parameter to run_async method in tap module. [#730]
1.8 (2025-11-13)

@@ -2,0 +21,0 @@ ================

@@ -671,2 +671,16 @@ .. _pyvo-data-access:

Job polling timeout
-------------------
When polling for job status, pyVO uses a default timeout of 10 seconds for
each request. Some services may occasionally respond slowly, causing
``ReadTimeout`` errors during polling. If you experience intermittent timeout
failures when polling job status, you can try increasing this timeout::
import pyvo.dal.tap
pyvo.dal.tap.DEFAULT_JOB_POLL_TIMEOUT = 30 # seconds
This is a module-level setting that affects all follow-up async job operations.
Note that a response time of 10 or more seconds for a status request
would likely indicate that the service may be experiencing issues.
Also, :py:class:`pyvo.dal.AsyncTAPJob` works as a context manager which

@@ -673,0 +687,0 @@ takes care of this automatically:

@@ -53,2 +53,3 @@ ******************************************************

>>> import astropy.units as u
>>> from astropy.io.votable import parse
>>> from astropy.coordinates import SkyCoord

@@ -59,3 +60,3 @@ >>> from pyvo.dal.scs import SCSService

>>>
>>> scs_srv = SCSService("https://vizier.cds.unistra.fr/viz-bin/conesearch/V1.5/I/239/hip_main")
>>> scs_srv = SCSService("https://vizier.cds.unistra.fr/viz-bin/conesearch/V1.5/I/311/hip2")
>>> m_viewer = MivotViewer(

@@ -100,3 +101,3 @@ ... scs_srv.search(

>>> print(mivot_instance.spaceSys.frame.spaceRefFrame.value)
ICRS
FK5

@@ -107,3 +108,4 @@ .. doctest-skip::

>>> print(f"position: {mivot_instance.latitude.value} {mivot_instance.longitude.value}")
position: 59.94033461 52.26722684
position: 59.90665631 52.12106214
position: 59.94033468 52.26722736
....

@@ -132,15 +134,18 @@

{
"dmtype": "coords:SpaceSys",
"dmid": "SpaceFrame_ICRS",
"frame": {
"dmtype": "coords:SpaceSys",
"dmid": "SpaceFrame_ICRS",
"frame": {
"dmrole": "coords:PhysicalCoordSys.frame",
"dmtype": "coords:SpaceFrame",
"spaceRefFrame": {
"dmtype": "ivoa:string",
"value": "ICRS"
}
}
"dmtype": "ivoa:string",
"value": "FK5"
},
"equinox": {
"dmtype": "coords:Epoch",
"value": "J2000"
}
}
}
As you can see from the previous examples, model leaves (class attributes) are complex types.

@@ -147,0 +152,0 @@ This is because they contain additional metadata as well as values:

Metadata-Version: 2.4
Name: pyvo
Version: 1.8
Version: 1.8.1
Summary: Astropy affiliated package for accessing Virtual Observatory data and services

@@ -5,0 +5,0 @@ Author: the PyVO Developers

Metadata-Version: 2.4
Name: pyvo
Version: 1.8
Version: 1.8.1
Summary: Astropy affiliated package for accessing Virtual Observatory data and services

@@ -5,0 +5,0 @@ Author: the PyVO Developers

@@ -42,2 +42,2 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst

DALAccessError, DALProtocolError, DALFormatError, DALServiceError,
DALQueryError)
DALQueryError, DALRateLimitError)

@@ -21,3 +21,3 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst

DALAccessError, DALProtocolError, DALFormatError, DALServiceError,
DALQueryError, DALOverflowWarning)
DALQueryError, DALOverflowWarning, DALRateLimitError)

@@ -35,2 +35,2 @@ __all__ = [

"DALAccessError", "DALProtocolError", "DALFormatError", "DALServiceError",
"DALQueryError", "DALOverflowWarning"]
"DALQueryError", "DALOverflowWarning", "DALRateLimitError"]

@@ -513,3 +513,3 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst

return DatalinkQuery(
self.baseurl, id=id, responseformat=responseformat, **keywords)
self.baseurl, id=id, responseformat=responseformat, session=self._session, **keywords)

@@ -516,0 +516,0 @@

@@ -7,5 +7,7 @@ """

"DALAccessError", "DALProtocolError", "DALFormatError", "DALServiceError",
"DALQueryError", "DALOverflowWarning"]
"DALQueryError", "DALOverflowWarning", "DALRateLimitError"]
import re
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime

@@ -188,2 +190,6 @@ import requests

code = response.status_code
if code == 429:
return DALRateLimitError.from_response(response, exc, url)
content_type = response.headers.get('content-type', None)

@@ -203,2 +209,150 @@ if content_type and 'text/plain' in content_type:

class DALRateLimitError(DALServiceError):
"""
Exception for HTTP 429 Too Many Requests responses.
This exception is raised when a DAL service returns a 429 status code,
indicating that the client has exceeded a rate limit. It provides
structured access to retry timing information from the Retry-After header
via the ``retry_after_seconds``, ``retry_after_raw``, and ``retry_after_date``
properties.
"""
_defreason = "Rate limit exceeded"
def __init__(self, reason=None, *, code=429, cause=None, url=None,
retry_after_seconds=None, retry_after_raw=None,
retry_after_date=None):
"""
Initialize the rate limit exception.
Parameters
----------
reason : str
A message describing the error.
code : int
The HTTP status code (default 429).
cause : Exception
The underlying exception that caused this error.
url : str
The query URL that produced the error.
retry_after_seconds : int or None
Seconds to wait before retrying.
retry_after_raw : str or None
Raw Retry-After header value.
retry_after_date : datetime or None
Parsed datetime if header was HTTP-date format.
"""
super().__init__(reason, code, cause, url)
self._retry_after_seconds = retry_after_seconds
self._retry_after_raw = retry_after_raw
self._retry_after_date = retry_after_date
@property
def retry_after_seconds(self):
"""
Seconds to wait before retrying, or None if not specified.
"""
return self._retry_after_seconds
@property
def retry_after_raw(self):
"""
The raw Retry-After header value, or None if not provided.
"""
return self._retry_after_raw
@property
def retry_after_date(self):
"""
If Retry-After was an HTTP-date, the parsed datetime.
None if it was an integer or not provided.
"""
return self._retry_after_date
@classmethod
def from_response(cls, response, cause=None, url=None):
"""
Create a DALRateLimitError from an HTTP response.
Parameters
----------
response : requests.Response
The HTTP response object with status code 429.
cause : Exception
The underlying exception that caused this error.
url : str
The query URL that produced the error.
Returns
-------
DALRateLimitError
A new exception instance with parsed retry information.
"""
retry_after_raw = None
for header_name in response.headers:
if header_name.lower() == 'retry-after':
retry_after_raw = response.headers[header_name]
break
retry_after_seconds = None
retry_after_date = None
if retry_after_raw is not None:
retry_after_seconds, retry_after_date = cls._parse_retry_after(
retry_after_raw)
if url:
message = f"Rate limit exceeded (HTTP 429) for {url}"
else:
message = "Rate limit exceeded (HTTP 429)"
if retry_after_seconds is not None:
message += f". Retry after {retry_after_seconds} seconds"
if retry_after_date:
message += f" (at {retry_after_raw})"
return cls(
reason=message,
code=429,
cause=cause,
url=url,
retry_after_seconds=retry_after_seconds,
retry_after_raw=retry_after_raw,
retry_after_date=retry_after_date
)
@staticmethod
def _parse_retry_after(value):
"""
Parse a Retry-After header value.
Parameters
----------
value : str
The Retry-After header value (integer seconds or date).
Returns
-------
tuple
(seconds, date) where seconds is an int and date is a datetime.
For integer format, date is None. For a date format, both are set.
Returns (None, None) if parsing fails.
"""
try:
seconds = int(value)
return max(0, seconds), None
except ValueError:
pass
try:
date = parsedate_to_datetime(value)
if date.tzinfo is None:
date = date.replace(tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
seconds = max(0, int((date - now).total_seconds()))
return seconds, date
except ValueError:
return None, None
class DALQueryError(DALAccessError):

@@ -205,0 +359,0 @@ """

@@ -453,5 +453,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst

def maxrec(self, val):
if not isinstance(val, int):
if not val:
return
if val is None:
self._maxrec = None
return
if not isinstance(val, int) or val < 0:
raise ValueError(f'maxrec {val} must be non-negative int')

@@ -475,3 +476,5 @@ self._maxrec = val

"""
return SIA2Results(self.execute_votable(), url=self.queryurl, session=self._session)
result = SIA2Results(self.execute_votable(), url=self.queryurl, session=self._session)
result.check_overflow_warning(self._maxrec)
return result

@@ -521,2 +524,12 @@

def _handle_overflow_warning(self, client_set_maxrec=None):
"""
SIA2 overflow warning handling.
For SIA2 results we suppress the default overflow warning during
initialization because SIA2Query.execute() will call
check_overflow_warning() after the results are initialized.
"""
pass
def getrecord(self, index):

@@ -523,0 +536,0 @@ """

@@ -32,3 +32,4 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst

__all__ = [
"search", "escape", "TAPService", "TAPQuery", "AsyncTAPJob", "TAPResults"]
"search", "escape", "TAPService", "TAPQuery", "AsyncTAPJob", "TAPResults",
"DEFAULT_JOB_POLL_TIMEOUT", "DEFAULT_JOB_WAIT_TIMEOUT"]

@@ -49,3 +50,9 @@ IVOA_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"

# Default timeout (in seconds) for job status polling requests.
DEFAULT_JOB_POLL_TIMEOUT = 10
# Default timeout (in seconds) for overall job wait.
DEFAULT_JOB_WAIT_TIMEOUT = 600.
def _from_ivoa_format(datetime_str):

@@ -297,3 +304,3 @@ """

self, query, *, language="ADQL", maxrec=None, uploads=None,
delete=True, **keywords):
delete=True, timeout=DEFAULT_JOB_WAIT_TIMEOUT, **keywords):
"""

@@ -315,2 +322,4 @@ runs async query and returns its result

delete the job after fetching the results
timeout : float
maximum time to wait for job completion in seconds. Default is 600.

@@ -339,3 +348,3 @@ Returns

session=self._session, **keywords)
job = job.run().wait()
job = job.run().wait(timeout=timeout)

@@ -667,2 +676,6 @@ try:

response = tapquery.submit()
try:
response.raise_for_status()
except requests.RequestException as ex:
raise DALServiceError.from_except(ex, tapquery.queryurl)
job = cls(response.url, session=session)

@@ -709,6 +722,8 @@ job._client_set_maxrec = maxrec

def _update(self, wait_for_statechange=False, timeout=10.):
def _update(self, wait_for_statechange=False, timeout=None):
"""
updates local job infos with remote values
"""
if timeout is None:
timeout = DEFAULT_JOB_POLL_TIMEOUT
try:

@@ -970,3 +985,3 @@ if wait_for_statechange:

def wait(self, *, phases=None, timeout=600.):
def wait(self, *, phases=None, timeout=DEFAULT_JOB_WAIT_TIMEOUT):
"""

@@ -973,0 +988,0 @@ waits for the job to reach the given phases.

@@ -10,2 +10,3 @@ #!/usr/bin/env python

import requests_mock
import warnings

@@ -15,3 +16,3 @@ import pytest

from pyvo.dal.sia2 import search, SIA2Service, SIA2Query, SIAService, SIAQuery
from pyvo.dal.exceptions import DALServiceError
from pyvo.dal.exceptions import DALServiceError, DALOverflowWarning

@@ -270,1 +271,68 @@ import astropy.units as u

SIA2Service('http://example.com/sia')
@pytest.fixture()
def sia2_overflow_fixture(mocker):
"""Mock SIA2 service that returns overflow status with exactly 10 records"""
def callback(request, context):
votable_content = '''<?xml version="1.0" encoding="UTF-8"?>
<VOTABLE version="1.3" xmlns="http://www.ivoa.net/xml/VOTable/v1.3">
<RESOURCE type="results">
<INFO name="QUERY_STATUS" value="OVERFLOW">Result truncated</INFO>
<TABLE>
<FIELD name="obs_collection" datatype="char" arraysize="128*"/>
<FIELD name="obs_id" datatype="char" arraysize="128*"/>
<FIELD name="facility_name" datatype="char" arraysize="128*"/>
<FIELD name="instrument_name" datatype="char" arraysize="128*"/>
<DATA>
<TABLEDATA>''' + ''.join(
f'<TR><TD>TEST</TD><TD>obs{i}</TD><TD>TEST</TD><TD>TEST</TD></TR>'
for i in range(10)) + '''
</TABLEDATA>
</DATA>
</TABLE>
</RESOURCE>
</VOTABLE>'''
return votable_content.encode('utf-8')
with mocker.register_uri(
'GET', 'https://example.com/sia/capabilities',
content=get_pkg_data_contents('data/sia2/capabilities.xml')
):
with mocker.register_uri(
'GET', sia_re, content=callback
):
yield mocker
@pytest.mark.usefixtures('sia2_overflow_fixture')
@pytest.mark.filterwarnings("ignore::astropy.io.votable.exceptions.W06")
class TestSIA2OverflowWarnings:
"""Test SIA2 overflow warning behavior"""
def test_no_maxrec_overflow_warning(self):
service = SIA2Service('https://example.com/sia')
with pytest.warns(DALOverflowWarning, match="Results truncated due to server limits"):
results = service.search(pos=(33.3, 4.2, 0.1))
assert len(results) == 10
def test_maxrec_exact_match_no_warning(self):
service = SIA2Service('https://example.com/sia')
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
results = service.search(pos=(33.3, 4.2, 0.1), maxrec=10)
overflow_warnings = [warning for warning in w
if issubclass(warning.category, DALOverflowWarning)]
assert len(overflow_warnings) == 0, f"Unexpected overflow warnings: {overflow_warnings}"
assert len(results) == 10
def test_maxrec_service_truncation_warning(self):
service = SIA2Service('https://example.com/sia')
with pytest.warns(DALOverflowWarning,
match=r"Results truncated at 10 records by service limits"
r".*you requested maxrec=100"):
results = service.search(pos=(33.3, 4.2, 0.1), maxrec=100)
assert len(results) == 10

@@ -11,3 +11,3 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst

from .exceptions import DALServiceError
from .exceptions import DALServiceError, DALRateLimitError
from ..io import vosi

@@ -66,2 +66,5 @@ from ..utils.url import url_sibling

if error.response.status_code == 429:
raise DALRateLimitError.from_response(error.response, error, url)
if error.response.status_code == 503:

@@ -201,2 +204,7 @@ # Handle Retry-After header, if present

break
except requests.HTTPError as ex:
if ex.response.status_code == 429:
raise DALRateLimitError.from_response(ex.response, ex,
tables_url)
continue
except requests.RequestException:

@@ -203,0 +211,0 @@ continue

@@ -92,6 +92,10 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
# Process complex type "mango:DateTime
# Process complex type "mango:DateTime"
if hk_field['dmtype'] == "mango:DateTime":
representation = hk_field['representation']['value']
timestamp = hk_field['dateTime']['value']
# Process complex type "coords:epoch" used for the space frame equinox
elif hk_field['dmtype'] == "coords:Epoch":
representation = 'yr' if "unit" not in hk_field else hk_field.get("unit")
timestamp = hk_field['value']
# Process simple attribute

@@ -106,5 +110,6 @@ else:

time_instance = self. _build_time_instance(timestamp, representation, besselian)
time_instance = self._build_time_instance(timestamp, representation, besselian)
if not time_instance:
raise MappingError(f"Cannot build a Time instance from {hk_field}")
mode = "besselian" if besselian else "julian"
raise MappingError(f"Cannot build a Time instance from {hk_field} ({mode} date)")

@@ -180,3 +185,2 @@ return time_instance

frame = coo_sys["spaceRefFrame"]["value"].lower()
if frame == 'fk4':

@@ -183,0 +187,0 @@ self._map_coord_names = SkyCoordMapping.default_params

@@ -12,7 +12,6 @@ '''

from astropy.utils.data import get_pkg_data_filename
from astropy import units as u
from pyvo.mivot.version_checker import check_astropy_version
from pyvo.mivot.viewer.mivot_instance import MivotInstance
from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder
from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError
from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError, MappingError
from pyvo.mivot.viewer.mivot_viewer import MivotViewer

@@ -122,5 +121,4 @@ from pyvo.utils import activate_features

"equinox": {
"dmtype": "coords:SpaceFrame.equinox",
"dmtype": "coords:Epoch",
"value": "2012",
"unit": "yr",
}

@@ -181,3 +179,3 @@ }

"equinox": {
"dmtype": "coords:SpaceFrame.equinox",
"dmtype": "coords:Epoch",
"value": "2012",

@@ -190,2 +188,23 @@ "unit": "yr",

def check_skycoo(scoo, ra, dec, distance, pm_ra_cosdec, pm_dec, obstime):
"""
Check the SkyCoord instance against the constant values given as parameters
"""
try:
assert scoo.ra.degree == pytest.approx(ra)
assert scoo.dec.degree == pytest.approx(dec)
if distance:
assert scoo.distance.pc == pytest.approx(distance)
if pm_ra_cosdec:
assert scoo.pm_ra_cosdec.value == pytest.approx(pm_ra_cosdec)
if pm_dec:
assert scoo.pm_dec.value == pytest.approx(pm_dec)
except AttributeError:
assert scoo.galactic.l.degree == pytest.approx(ra)
assert scoo.galactic.b.degree == pytest.approx(dec)
if obstime:
assert str(scoo.obstime) == obstime
def test_no_matching_mapping():

@@ -208,9 +227,5 @@ """

scoo = scb.build_sky_coord()
assert (str(scoo).replace("\n", "").replace(" ", "")
== "<SkyCoord (ICRS): (ra, dec) in deg(52.26722684, 59.94033461) "
"(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")
scoo = mivot_instance.get_SkyCoord()
assert (str(scoo).replace("\n", "").replace(" ", "")
== "<SkyCoord (ICRS): (ra, dec) in deg(52.26722684, 59.94033461) "
"(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")
check_skycoo(scoo, 52.26722684, 59.94033461, None,
-0.82, -1.85,
None)

@@ -223,2 +238,5 @@ vizier_dict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "Galactic"

"(pm_l_cosb, pm_b) in mas / yr(-0.82, -1.85)>")
check_skycoo(scoo, 52.26722684, 59.94033461, None,
-0.82, -1.85,
None)

@@ -228,5 +246,5 @@ vizier_dict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "QWERTY"

scoo = mivot_instance.get_SkyCoord()
assert (str(scoo).replace("\n", "").replace(" ", "")
== "<SkyCoord (ICRS): (ra, dec) in deg(52.26722684, 59.94033461) "
"(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")
check_skycoo(scoo, 52.26722684, 59.94033461, None,
-0.82, -1.85,
"J1991.250")

@@ -242,6 +260,5 @@

scoo = scb.build_sky_coord()
assert (str(scoo).replace("\n", "").replace(" ", "")
== "<SkyCoord (FK5: equinox=J2012.000): (ra, dec, distance) in "
"(deg, deg, pc)(52.26722684, 59.94033461, 1666.66666667) "
"(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")
check_skycoo(scoo, 52.26722684, 59.94033461, 1666.66666667,
-0.82, -1.85,
"J1991.250")

@@ -252,8 +269,14 @@ mydict = deepcopy(vizier_equin_dict)

scoo = mivot_instance.get_SkyCoord()
assert (str(scoo).replace("\n", "").replace(" ", "")
== "<SkyCoord (FK4: equinox=B2012.000, obstime=B1991.250): (ra, dec, distance) in "
"(deg, deg, pc)(52.26722684, 59.94033461, 1666.66666667) "
"(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")
check_skycoo(scoo, 52.26722684, 59.94033461, 1666.66666667,
-0.82, -1.85,
"B1991.250")
mydict = deepcopy(vizier_equin_dict)
mydict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "FK4"
mydict["spaceSys"]["frame"]["equinox"]["value"] = "J2012"
with pytest.raises(MappingError, match=r".*besselian date.*"):
mivot_instance = MivotInstance(**mydict)
scoo = mivot_instance.get_SkyCoord()
@pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+")

@@ -269,12 +292,5 @@ def test_simad_cs_output():

assert scoo.ra.degree == pytest.approx(269.45207696)
assert scoo.dec.degree == pytest.approx(4.69336497)
assert scoo.distance.pc == pytest.approx(1.82823411)
x = scoo.pm_ra_cosdec.value
y = (-801.551 * u.mas/u.yr).value
assert x == pytest.approx(y)
x = scoo.pm_dec.value
y = (10362.394 * u.mas/u.yr).value
assert x == pytest.approx(y)
assert str(scoo.obstime) == "J2000.000"
check_skycoo(scoo, 269.45207696, 4.69336497, 1.82823411,
-801.551, 10362.394,
"J2000.000")

@@ -281,0 +297,0 @@

@@ -8,2 +8,2 @@ # Note that we need to fall back to the hard-coded version if either

except Exception:
version = '1.8'
version = '1.8.1'

@@ -18,2 +18,3 @@ [tool:pytest]

ignore:pyvo.discover:pyvo.utils.prototype.PrototypeWarning
ignore:leap-second auto-update failed::astropy

@@ -76,2 +77,3 @@ [flake8]

pyvo.registry.tests = data/*.xml, data/*.desise
pyvo.mivot.writer = *.xsd
pyvo.mivot.tests = data/*.xml, data/input/*.xml, data/output/*.xml, data/reference/*json, data/reference/*xml

@@ -78,0 +80,0 @@ pyvo.dal.tests = data/*.xml, data/*/*

Sorry, the diff of this file is too big to display