astropy / astropy/pyvo

BUG: DALFormatError: Incorrect number of elements in array

Open
#361 3 comments 0 reactions 0 assignees View on GitHub
bug component: dal
Dominant language
Python
Stars
88
Forks
64
Avg merge
38m
Merged PRs (30d)
1

Description

[Edit:] - while the code snippet below doesn't produce the traceback any more, removing the skips in the narrative docs (reverting https://github.com/astropy/pyvo/commit/b3f4dc0f8446d12ca433a6d55e4563791df98f69) would still produces the error.

This error now pops up at two different places for the pyvo test suite, and we also see it downstream in astroquery. I'm fairly certain that this is pyvo or astropy related rather than upstream server issues as the two failing examples are using two different providers, one is gavo the other is cadc.

```
from pyvo.dal.sia2 import search, SIAService
CADC_SIA_URL = 'https://ws.cadc-ccda.hia-iha.nrc-cnrc.gc.ca/sia'
results = search(CADC_SIA_URL, pos=(2.8425, 74.4846, 10), maxrec=55)

for i in results.iter_datalinks():
print(i)
break
```

```
======================================================= FAILURES =======================================================
___________________________________________ TestSIACadc.test_datalink_batch ____________________________________________

self = {'ID': ['ivo://cadc.nrc.ca/CFHT?949070/949070s', 'ivo://cadc.nrc.ca/CFHT?950677/950677og', 'ivo://cadc.nrc.ca/CFHT?949...0161-s/tess2019289112926-s0017-3-3-0161-s-CALIBRATED'], 'RESPONSEFORMAT': 'application/x-votable+xml;content=datalink'}
post = True

def execute_votable(self, post=False):
"""
Submit the query and return the results as an AstroPy votable instance.
As this is the level where qualified error messages are available,
they are raised here instead of in the underlying execute_stream.

Returns
-------
astropy.io.votable.tree.Table
an Astropy votable Table instance

Raises
------
DALServiceError
for errors connecting to or communicating with the service
DALFormatError
for errors parsing the VOTable response

See Also
--------
astropy.io.votable
DALServiceError
DALFormatError
DALQueryError
"""
try:
> return votableparse(self.execute_stream(post=post).read)

pyvo/dal/query.py:243:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

args = (functools.partial(>, decode_content=True),)
kwargs = {}, i = 0, msg = '"pedantic" was deprecated in version 5.0 and will be removed in a future version. '

@functools.wraps(function)
def wrapper(*args, **kwargs):
for i in range(n):
msg = message[i] or (f'"{old_name[i]}" was deprecated in '
f'version {since[i]} and will be removed '
'in a future version. ')
# The only way to have oldkeyword inside the function is
# that it is passed as kwarg because the oldkeyword
# parameter was renamed to newkeyword.
if old_name[i] in kwargs:
value = kwargs.pop(old_name[i])
# Display the deprecation warning only when it's not
# pending.
if not pending[i]:
if not message[i]:
if new_name[i] is not None:
msg += f'Use argument "{new_name[i]}" instead.'
elif alternative:
msg += f'\n Use {alternative} instead.'
warnings.warn(msg, warning_type, stacklevel=2)

# Check if the newkeyword was given as well.
newarg_in_args = (position[i] is not None and
len(args) > position[i])
newarg_in_kwargs = new_name[i] in kwargs

if newarg_in_args or newarg_in_kwargs:
if not pending[i]:
# If both are given print a Warning if relax is
# True or raise an Exception is relax is False.
if relax[i]:
warnings.warn(
f'"{old_name[i]}" and "{new_name[i]}" '
'keywords were set. '
f'Using the value of "{new_name[i]}".',
AstropyUserWarning)
else:
raise TypeError(
f'cannot specify both "{old_name[i]}" and '
f'"{new_name[i]}".')
else:
# Pass the value of the old argument with the
# name of the new argument to the function
if new_name[i] is not None:
kwargs[new_name[i]] = value
# If old argument has no replacement, cast it back.
# https://github.com/astropy/astropy/issues/9914
else:
kwargs[old_name[i]] = value

# Deprecated keyword without replacement is given as
# positional argument.
elif (not pending[i] and not new_name[i] and position[i] and
len(args) > position[i]):
if alternative and not message[i]:
msg += f'\n Use {alternative} instead.'
warnings.warn(msg, warning_type, stacklevel=2)

> return function(*args, **kwargs)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/utils/decorators.py:545:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

source = functools.partial(>, decode_content=True)
columns = None, invalid = 'exception', verify = 'ignore', chunk_size = 256, table_number = None, table_id = None
filename = None, unit_format = None, datatype_mapping = {}, _debug_python_based_parser = False

@deprecated_renamed_argument('pedantic', 'verify', since='5.0')
def parse(source, columns=None, invalid='exception', verify=None,
chunk_size=tree.DEFAULT_CHUNK_SIZE, table_number=None,
table_id=None, filename=None, unit_format=None,
datatype_mapping=None, _debug_python_based_parser=False):
"""
Parses a VOTABLE_ xml file (or file-like object), and returns a
`~astropy.io.votable.tree.VOTableFile` object.

Parameters
----------
source : path-like or file-like
Path or file-like object containing a VOTABLE_ xml file.
If file, must be readable.

columns : sequence of str, optional
List of field names to include in the output. The default is
to include all fields.

invalid : str, optional
One of the following values:

- 'exception': throw an exception when an invalid value is
encountered (default)

- 'mask': mask out invalid values

verify : {'ignore', 'warn', 'exception'}, optional
When ``'exception'``, raise an error when the file violates the spec,
otherwise either issue a warning (``'warn'``) or silently continue
(``'ignore'``). Warnings may be controlled using the standard Python
mechanisms. See the `warnings` module in the Python standard library
for more information. When not provided, uses the configuration setting
``astropy.io.votable.verify``, which defaults to 'ignore'.

.. versionchanged:: 4.0
``verify`` replaces the ``pedantic`` argument, which will be
deprecated in future.
.. versionchanged:: 5.0
The ``pedantic`` argument is deprecated.

chunk_size : int, optional
The number of rows to read before converting to an array.
Higher numbers are likely to be faster, but will consume more
memory.

table_number : int, optional
The number of table in the file to read in. If `None`, all
tables will be read. If a number, 0 refers to the first table
in the file, and only that numbered table will be parsed and
read in. Should not be used with ``table_id``.

table_id : str, optional
The ID of the table in the file to read in. Should not be
used with ``table_number``.

filename : str, optional
A filename, URL or other identifier to use in error messages.
If *filename* is None and *source* is a string (i.e. a path),
then *source* will be used as a filename for error messages.
Therefore, *filename* is only required when source is a
file-like object.

unit_format : str, astropy.units.format.Base instance or None, optional
The unit format to use when parsing unit attributes. If a
string, must be the name of a unit formatter. The built-in
formats include ``generic``, ``fits``, ``cds``, and
``vounit``. A custom formatter may be provided by passing a
`~astropy.units.UnitBase` instance. If `None` (default),
the unit format to use will be the one specified by the
VOTable specification (which is ``cds`` up to version 1.3 of
VOTable, and ``vounit`` in more recent versions of the spec).

datatype_mapping : dict, optional
A mapping of datatype names (`str`) to valid VOTable datatype names
(str). For example, if the file being read contains the datatype
"unsignedInt" (an invalid datatype in VOTable), include the mapping
``{"unsignedInt": "long"}``.

Returns
-------
votable : `~astropy.io.votable.tree.VOTableFile` object

See Also
--------
astropy.io.votable.exceptions : The exceptions this function may raise.
"""
from . import conf

invalid = invalid.lower()
if invalid not in ('exception', 'mask'):
raise ValueError("accepted values of ``invalid`` are: "
"``'exception'`` or ``'mask'``.")

if verify is None:

conf_verify_lowercase = conf.verify.lower()

# We need to allow verify to be booleans as strings since the
# configuration framework doesn't make it easy/possible to have mixed
# types.
if conf_verify_lowercase in ['false', 'true']:
verify = conf_verify_lowercase == 'true'
else:
verify = conf_verify_lowercase

if isinstance(verify, bool):
verify = 'exception' if verify else 'warn'
elif verify not in VERIFY_OPTIONS:
raise ValueError(f"verify should be one of {'/'.join(VERIFY_OPTIONS)}")

if datatype_mapping is None:
datatype_mapping = {}

config = {
'columns': columns,
'invalid': invalid,
'verify': verify,
'chunk_size': chunk_size,
'table_number': table_number,
'filename': filename,
'unit_format': unit_format,
'datatype_mapping': datatype_mapping
}

if filename is None and isinstance(source, str):
config['filename'] = source

with iterparser.get_xml_iterator(
source,
_debug_python_based_parser=_debug_python_based_parser) as iterator:
return tree.VOTableFile(
> config=config, pos=(1, 1)).parse(iterator, config)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/table.py:160:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = ... 1 tables ..., iterator =
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}

def parse(self, iterator, config):
config['_current_table_number'] = 0

for start, tag, data, pos in iterator:
if start:
if tag == 'xml':
pass
elif tag == 'VOTABLE':
if 'version' not in data:
warn_or_raise(W20, W20, self.version, config, pos)
config['version'] = self.version
else:
config['version'] = self._version = data['version']
if config['version'].lower().startswith('v'):
warn_or_raise(
W29, W29, config['version'], config, pos)
self._version = config['version'] = \
config['version'][1:]
if config['version'] not in self._version_namespace_map:
vo_warn(W21, config['version'], config, pos)

if 'xmlns' in data:
ns_info = self._version_namespace_map.get(config['version'], {})
correct_ns = ns_info.get('namespace_uri')
if data['xmlns'] != correct_ns:
vo_warn(W41, (correct_ns, data['xmlns']), config, pos)
else:
vo_warn(W42, (), config, pos)

break
else:
vo_raise(E19, (), config, pos)
config.update(self._get_version_checks())

tag_mapping = {
'PARAM': self._add_param,
'RESOURCE': self._add_resource,
'COOSYS': self._add_coosys,
'TIMESYS': self._add_timesys,
'INFO': self._add_info,
'DEFINITIONS': self._add_definitions,
'DESCRIPTION': self._ignore_add,
'GROUP': self._add_group}

for start, tag, data, pos in iterator:
if start:
> tag_mapping.get(tag, self._add_unknown_tag)(

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:3596:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = ... 1 tables ..., iterator =
tag = 'RESOURCE', data = {'ID': 'soda-4f70ffe7-506c-43d2-9f1e-30dda6cf4e89', 'type': 'meta', 'utype': 'adhoc:service'}
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}, pos = (1650, 2)

def _add_resource(self, iterator, tag, data, config, pos):
resource = Resource(config=config, pos=pos, **data)
self.resources.append(resource)
> resource.parse(self, iterator, config)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:3475:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = , votable = ... 1 tables ...
iterator =
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}

def parse(self, votable, iterator, config):
self._votable = votable

tag_mapping = {
'TABLE': self._add_table,
'INFO': self._add_info,
'PARAM': self._add_param,
'GROUP': self._add_group,
'COOSYS': self._add_coosys,
'TIMESYS': self._add_timesys,
'RESOURCE': self._add_resource,
'LINK': self._add_link,
'DESCRIPTION': self._ignore_add
}

for start, tag, data, pos in iterator:
if start:
> tag_mapping.get(tag, self._add_unknown_tag)(

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:3285:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = , iterator = , tag = 'GROUP'
data = {'name': 'inputParams'}
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}, pos = (1654, 4)

def _add_group(self, iterator, tag, data, config, pos):
group = Group(self, config=config, pos=pos, **data)
self.groups.append(group)
> group.parse(iterator, config)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:3241:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = ... 5 entries ..., iterator =
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}

def parse(self, iterator, config):
tag_mapping = {
'FIELDref': self._add_fieldref,
'PARAMref': self._add_paramref,
'PARAM': self._add_param,
'GROUP': self._add_group,
'DESCRIPTION': self._ignore_add}

for start, tag, data, pos in iterator:
if start:
> tag_mapping.get(tag, self._add_unknown_tag)(
iterator, tag, data, config, pos)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:2086:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = ... 5 entries ..., iterator =
tag = 'PARAM', data = {'arraysize': '2', 'datatype': 'double', 'name': 'BAND', 'ucd': 'em.wl;stat.interval', ...}
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}, pos = (1667, 6)

def _add_param(self, iterator, tag, data, config, pos):
if isinstance(self._table, VOTableFile):
votable = self._table
else:
votable = self._table._votable
param = Param(votable, config=config, pos=pos, **data)
self.entries.append(param)
> param.parse(iterator, config)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:2069:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self =
iterator =
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}

def parse(self, iterator, config):
for start, tag, data, pos in iterator:
if start:
if tag == 'VALUES':
self.values.__init__(
self._votable, self, config=config, pos=pos, **data)
> self.values.parse(iterator, config)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:1480:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = , iterator =
config = {'_current_table_number': 1, 'chunk_size': 256, 'columns': None, 'datatype_mapping': {}, ...}

def parse(self, iterator, config):
if self.ref is not None:
for start, tag, data, pos in iterator:
if start:
warn_or_raise(W44, W44, tag, config, pos)
else:
if tag != 'VALUES':
warn_or_raise(W44, W44, tag, config, pos)
break
else:
for start, tag, data, pos in iterator:
if start:
if tag == 'MIN':
if 'value' not in data:
vo_raise(E09, 'MIN', config, pos)
> self.min = data['value']

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:1024:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = , min = '9.099660043187688E-4'

@min.setter
def min(self, min):
if hasattr(self._field, 'converter') and min is not None:
> self._min = self._field.converter.parse(min)[0]

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/tree.py:938:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = , value = '9.099660043187688E-4'
config = {'_warning_counts': {: 1}}, pos = None

def parse(self, value, config=None, pos=None):
if config is None:
config = {}
elif config['version_1_3_or_later'] and value == '':
return np.zeros(self._arraysize, dtype=self._base.format), True
parts = self._splitter(value, config, pos)
if len(parts) != self._items:
> warn_or_raise(E02, E02, (self._items, len(parts)), config, pos)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/converters.py:607:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

warning_class =
exception_class = , args = (2, 1)
config = {'_warning_counts': {: 1}}, pos = None, stacklevel = 1

def warn_or_raise(warning_class, exception_class=None, args=(), config=None,
pos=None, stacklevel=1):
"""
Warn or raise an exception, depending on the verify setting.
"""
if config is None:
config = {}
# NOTE: the default here is deliberately warn rather than ignore, since
# one would expect that calling warn_or_raise without config should not
# silence the warnings.
config_value = config.get('verify', 'warn')
if config_value == 'exception':
if exception_class is None:
exception_class = warning_class
vo_raise(exception_class, args, config, pos)
elif config_value == 'warn':
> vo_warn(warning_class, args, config, pos, stacklevel=stacklevel+1)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/exceptions.py:102:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

warning_class = , args = (2, 1)
config = {'_warning_counts': {: 1}}, pos = None, stacklevel = 2

def vo_warn(warning_class, args=(), config=None, pos=None, stacklevel=1):
"""
Warn, with proper position information if available.
"""
if config is None:
config = {}
# NOTE: the default here is deliberately warn rather than ignore, since
# one would expect that calling warn_or_raise without config should not
# silence the warnings.
if config.get('verify', 'warn') != 'ignore':
warning = warning_class(args, config, pos)
> _suppressed_warning(warning, config, stacklevel=stacklevel+1)

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/exceptions.py:143:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

warning = E02('?:?:?: E02: Incorrect number of elements in array. Expected multiple of 2, got 1')
config = {'_warning_counts': {: 1}}, stacklevel = 3

def _suppressed_warning(warning, config, stacklevel=2):
warning_class = type(warning)
config.setdefault('_warning_counts', dict()).setdefault(warning_class, 0)
config['_warning_counts'][warning_class] += 1
message_count = config['_warning_counts'][warning_class]
if message_count <= conf.max_warnings:
if message_count == conf.max_warnings:
warning.formatted_message += \
' (suppressing further warnings of this type...)'
> warn(warning, stacklevel=stacklevel+1)
E astropy.io.votable.exceptions.E02: ?:?:?: E02: Incorrect number of elements in array. Expected multiple of 2, got 1

../../../.pyenv/versions/3.10.0/lib/python3.10/site-packages/astropy/io/votable/exceptions.py:83: E02

During handling of the above exception, another exception occurred:

self =

@pytest.mark.filterwarnings("ignore::pyvo.dal.exceptions.DALOverflowWarning")
def test_datalink_batch(self):
# Maximum batch size in CADC SIA is around 25
# Test whether multiple batches can be retrieved
results = search(CADC_SIA_URL, pos=(2.8425, 74.4846, 10), maxrec=55)
ids = []
> for i in results.iter_datalinks():

pyvo/dal/tests/test_sia2_remote.py:34:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
pyvo/dal/adhoc.py:212: in iter_datalinks
current_batch = self.query.execute(post=True)
pyvo/dal/adhoc.py:443: in execute
return DatalinkResults(self.execute_votable(post),
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = {'ID': ['ivo://cadc.nrc.ca/CFHT?949070/949070s', 'ivo://cadc.nrc.ca/CFHT?950677/950677og', 'ivo://cadc.nrc.ca/CFHT?949...0161-s/tess2019289112926-s0017-3-3-0161-s-CALIBRATED'], 'RESPONSEFORMAT': 'application/x-votable+xml;content=datalink'}
post = True

def execute_votable(self, post=False):
"""
Submit the query and return the results as an AstroPy votable instance.
As this is the level where qualified error messages are available,
they are raised here instead of in the underlying execute_stream.

Returns
-------
astropy.io.votable.tree.Table
an Astropy votable Table instance

Raises
------
DALServiceError
for errors connecting to or communicating with the service
DALFormatError
for errors parsing the VOTable response

See Also
--------
astropy.io.votable
DALServiceError
DALFormatError
DALQueryError
"""
try:
return votableparse(self.execute_stream(post=post).read)
except Exception as e:
self.raise_if_error()
> raise DALFormatError(e, self.queryurl)
E pyvo.dal.exceptions.DALFormatError: E02: ?:?:?: E02: Incorrect number of elements in array. Expected multiple of 2, got 1

pyvo/dal/query.py:246: DALFormatError
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with pyvo/dal/query.py at execute_votable and reproduce the failure in TestSIACadc.test_datalink_batch using the CADC SIA URL and results.iter_datalinks(). Compare the failing VOTable parsing path with the GAVO and CADC examples and the narrative documentation skips; done means the pyvo suite no longer raises DALFormatError in these cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.