pyvo
Advanced tools
| <?xml version="1.0"?> | ||
| <?xml-stylesheet href='/static/xsl/vosi.xsl' type='text/xsl'?> | ||
| <cap:capabilities xmlns:cap="http://www.ivoa.net/xml/VOSICapabilities/v1.0" xmlns:tr="http://www.ivoa.net/xml/TAPRegExt/v1.0" xmlns:vg="http://www.ivoa.net/xml/VORegistry/v1.0" xmlns:vr="http://www.ivoa.net/xml/VOResource/v1.0" xmlns:vs="http://www.ivoa.net/xml/VODataService/v1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ivoa.net/xml/VOSICapabilities/v1.0 http://vo.ari.uni-heidelberg.de/docs/schemata/VOSICapabilities-v1.0.xsd http://www.ivoa.net/xml/TAPRegExt/v1.0 http://vo.ari.uni-heidelberg.de/docs/schemata/TAPRegExt-v1.0.xsd http://www.ivoa.net/xml/VORegistry/v1.0 http://vo.ari.uni-heidelberg.de/docs/schemata/VORegistry-v1.0.xsd http://www.ivoa.net/xml/VOResource/v1.0 http://vo.ari.uni-heidelberg.de/docs/schemata/VOResource-v1.1.xsd http://www.ivoa.net/xml/VODataService/v1.1 http://vo.ari.uni-heidelberg.de/docs/schemata/VODataService-v1.1.xsd"> | ||
| <capability standardID="ivo://ivoa.net/std/VOSI#capabilities"> | ||
| <interface xsi:type="vs:ParamHTTP"> | ||
| <accessURL use="full">http://example.org/tap/capabilities</accessURL> | ||
| </interface> | ||
| </capability> | ||
| <capability standardID="ivo://ivoa.net/std/VOSI#tables"> | ||
| <interface xsi:type="vs:ParamHTTP"> | ||
| <accessURL use="full">http://example.org/tap/tables</accessURL> | ||
| </interface> | ||
| </capability> | ||
| <capability standardID="ivo://ivoa.net/std/TAP" xsi:type="tr:TableAccess"> | ||
| <interface role="std" xsi:type="vs:ParamHTTP"> | ||
| <accessURL use="base">http://example.org/tap</accessURL> | ||
| </interface> | ||
| <language> | ||
| <name>ADQL</name> | ||
| <version ivo-id="ivo://ivoa.net/std/ADQL#v2.0">2.0</version> | ||
| <description>ADQL 2.0</description> | ||
| </language> | ||
| <outputFormat ivo-id="ivo://ivoa.net/std/TAPRegExt#output-votable-binary"> | ||
| <mime>text/xml</mime> | ||
| </outputFormat> | ||
| </capability> | ||
| </cap:capabilities> |
@@ -26,3 +26,3 @@ # This test job is separated out into its own workflow to be able to trigger separately | ||
| - name: Set up Python 3.12 | ||
| uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 | ||
| uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 | ||
| with: | ||
@@ -36,3 +36,3 @@ python-version: "3.12" | ||
| - name: Upload coverage to codecov | ||
| uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 | ||
| uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0 | ||
| with: | ||
@@ -47,3 +47,3 @@ file: ./coverage.xml | ||
| - name: Set up Python 3.13 | ||
| uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 | ||
| uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 | ||
| with: | ||
@@ -50,0 +50,0 @@ python-version: "3.13-dev" |
@@ -43,3 +43,3 @@ # Developer version testing is in separate workflow | ||
| - name: Set up Python | ||
| uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 | ||
| uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 | ||
| with: | ||
@@ -65,3 +65,3 @@ python-version: ${{ matrix.python-version }} | ||
| - name: Set up Python | ||
| uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 | ||
| uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 | ||
| with: | ||
@@ -82,3 +82,3 @@ python-version: '3.10' | ||
| - name: Set up Python 3.8 | ||
| uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 | ||
| uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 | ||
| with: | ||
@@ -99,3 +99,3 @@ python-version: 3.8 | ||
| - name: Set up Python 3.10 | ||
| uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 | ||
| uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 | ||
| with: | ||
@@ -102,0 +102,0 @@ python-version: '3.10' |
+11
-0
@@ -0,1 +1,12 @@ | ||
| 1.6.2 (2025-04-07) | ||
| ================== | ||
| Bug Fixes | ||
| --------- | ||
| - Fix performance issues with datalink results. [#654] | ||
| - More careful NULL value handling in tapregext data limits. [#659] | ||
| 1.6.1 (2025-02-12) | ||
@@ -2,0 +13,0 @@ ================== |
@@ -409,3 +409,3 @@ .. _pyvo-data-access: | ||
| >>> # authenticate. For ex: auth_session.credentials.set_client_certificate('<cert_file>') | ||
| >>> tap_service = vo.dal.TAPService("https://ws-cadc.canfar.net/youcat", auth_session) | ||
| >>> tap_service = vo.dal.TAPService("https://ws-cadc.canfar.net/youcat", session=auth_session) | ||
| >>> | ||
@@ -820,3 +820,3 @@ >>> table_definition = ''' | ||
| ... ).run_sync("select top 5 * from califadr3.cubes order by califaid") | ||
| >>> for dl in rows.iter_datalinks(): # doctest: +IGNORE_WARNINGS | ||
| >>> for dl in rows.iter_datalinks(preserve_order=True): # doctest: +IGNORE_WARNINGS | ||
| ... print(next(dl.bysemantics("#preview"))["access_url"]) | ||
@@ -823,0 +823,0 @@ http://dc.g-vo.org/getproduct/califa/datadr3/V1200/IC5376.V1200.rscube.fits?preview=True |
@@ -584,3 +584,3 @@ .. _pyvo-registry: | ||
| export IVOA_REGISTRY="http://vao.stsci.edu/RegTAP/TapService.aspx" | ||
| export IVOA_REGISTRY="https://mast.stsci.edu/vo-tap/api/v0.1/registry" | ||
@@ -607,3 +607,4 @@ before starting python (or the notebook processor). | ||
| http://voparis-rr.obspm.fr/tap | ||
| https://vao.stsci.edu/RegTAP/TapService.aspx | ||
| https://mast.stsci.edu/vo-tap/api/v0.1/registry | ||
| https://registry.euro-vo.org/regtap/tap | ||
@@ -610,0 +611,0 @@ |
+4
-5
@@ -1,11 +0,10 @@ | ||
| Metadata-Version: 2.2 | ||
| Metadata-Version: 2.4 | ||
| Name: pyvo | ||
| Version: 1.6.1 | ||
| Version: 1.6.2 | ||
| Summary: Astropy affiliated package for accessing Virtual Observatory data and services | ||
| Author: the PyVO Developers | ||
| License: BSD | ||
| License: BSD-3-Clause | ||
| Project-URL: Source, https://github.com/astropy/pyvo | ||
| Project-URL: Documentation, https:/pyvo.readthedocs.io | ||
| Classifier: Intended Audience :: Science/Research | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Classifier: Operating System :: OS Independent | ||
@@ -17,3 +16,2 @@ Classifier: Programming Language :: Python | ||
| Classifier: Topic :: Software Development :: Libraries | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Requires-Python: >=3.8 | ||
@@ -32,2 +30,3 @@ License-File: LICENSE.rst | ||
| Requires-Dist: sphinx-astropy; extra == "docs" | ||
| Dynamic: license-file | ||
@@ -34,0 +33,0 @@ PyVO |
@@ -1,11 +0,10 @@ | ||
| Metadata-Version: 2.2 | ||
| Metadata-Version: 2.4 | ||
| Name: pyvo | ||
| Version: 1.6.1 | ||
| Version: 1.6.2 | ||
| Summary: Astropy affiliated package for accessing Virtual Observatory data and services | ||
| Author: the PyVO Developers | ||
| License: BSD | ||
| License: BSD-3-Clause | ||
| Project-URL: Source, https://github.com/astropy/pyvo | ||
| Project-URL: Documentation, https:/pyvo.readthedocs.io | ||
| Classifier: Intended Audience :: Science/Research | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Classifier: Operating System :: OS Independent | ||
@@ -17,3 +16,2 @@ Classifier: Programming Language :: Python | ||
| Classifier: Topic :: Software Development :: Libraries | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Requires-Python: >=3.8 | ||
@@ -32,2 +30,3 @@ License-File: LICENSE.rst | ||
| Requires-Dist: sphinx-astropy; extra == "docs" | ||
| Dynamic: license-file | ||
@@ -34,0 +33,0 @@ PyVO |
@@ -158,2 +158,3 @@ .gitignore | ||
| pyvo/io/vosi/tests/data/tables.xml | ||
| pyvo/io/vosi/tests/data/capabilities/minimal-tapregext.xml | ||
| pyvo/io/vosi/tests/data/capabilities/multiple_capa_descriptions.xml | ||
@@ -160,0 +161,0 @@ pyvo/io/vosi/tests/data/tables/datatypes_tap.xml |
+137
-52
@@ -9,3 +9,2 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
| import requests | ||
| from collections import OrderedDict | ||
@@ -22,3 +21,7 @@ from .query import DALResults, DALQuery, DALService, Record | ||
| from astropy.io.votable.tree import Resource, Group | ||
| from astropy.io.votable.tree import Resource, Group, VOTableFile | ||
| try: | ||
| from astropy.io.votable.tree import TableElement | ||
| except ImportError: | ||
| from astropy.io.votable.tree import Table as TableElement | ||
| from astropy.utils.collections import HomogeneousList | ||
@@ -128,3 +131,3 @@ | ||
| ---------- | ||
| ivoid : str | ||
| ivo_id : str | ||
| the ivoid of the service we want to have. | ||
@@ -173,45 +176,123 @@ | ||
| """ | ||
| Mixin for datalink functionallity for results classes. | ||
| Mixin for datalink functionality for results classes. | ||
| """ | ||
| def _iter_datalinks_from_dlblock(self, datalink_service): | ||
| def _iter_datalinks_from_dlblock(self, preserve_order=False): | ||
| """yields datalinks from the current rows using a datalink | ||
| service RESOURCE. | ||
| Parameters | ||
| ---------- | ||
| preserve_order : bool | ||
| True to return the datalinks keeping the order of the current rows. | ||
| NOTE: There might be a performance penalty for keeping the order as | ||
| one request per row is sent to the service. When the order of the | ||
| datalinks is not important, the execution is optimized to query the | ||
| service in batches. | ||
| """ | ||
| remaining_ids = [] # remaining IDs to processed | ||
| current_batch = None # retrieved but not returned yet | ||
| current_ids = [] # retrieved but not returned | ||
| processed_ids = [] # retrived and returned IDs | ||
| batch_size = None # size of the batch | ||
| def _get_results_tb(rows, dl_batch_tb): | ||
| # Creates a new DL result table with the given rows as the results data | ||
| # and the dl_batch_tb as the template for both table fields and other resources | ||
| tb = VOTableFile() | ||
| new_table = TableElement(tb) | ||
| new_table.fields.extend(dl_batch_tb.get_first_table().fields) | ||
| new_table.create_arrays(len(rows)) | ||
| for index, row in enumerate(rows): | ||
| new_table.array[index] = row | ||
| results_resource = Resource() | ||
| results_resource.type = "results" | ||
| results_resource.tables.append(new_table) | ||
| tb.resources.append(results_resource) | ||
| # now add all the other resources from the dl_batch_tb | ||
| for resource in dl_batch_tb.resources: | ||
| if resource.type == "meta": | ||
| tb.resources.append(resource) | ||
| return tb | ||
| for row in self: | ||
| if not current_ids: | ||
| if batch_size is None: | ||
| # first call. | ||
| self.query = DatalinkQuery.from_resource( | ||
| [_ for _ in self], | ||
| self._datalink, | ||
| session=self._session, | ||
| original_row=row) | ||
| remaining_ids = self.query['ID'] | ||
| if not remaining_ids: | ||
| # we are done | ||
| return | ||
| if batch_size: | ||
| # subsequent calls are limitted to batch size | ||
| self.query['ID'] = remaining_ids[:batch_size] | ||
| current_batch = self.query.execute(post=True) | ||
| current_ids = list(OrderedDict.fromkeys( | ||
| [_ for _ in current_batch.to_table()['ID']])) | ||
| if not current_ids: | ||
| raise DALServiceError( | ||
| 'Could not retrieve datalinks for: {}'.format( | ||
| ', '.join([_ for _ in remaining_ids]))) | ||
| batch_size = len(current_ids) | ||
| id1 = current_ids.pop(0) | ||
| processed_ids.append(id1) | ||
| remaining_ids.remove(id1) | ||
| yield current_batch.clone_byid( | ||
| id1, | ||
| original_row=row) | ||
| if preserve_order: | ||
| # one request at the time to preserve order | ||
| for row in self: | ||
| self.query = DatalinkQuery.from_resource( | ||
| row, self._datalink, session=self._session, original_row=None) | ||
| yield DatalinkResults( | ||
| self.query.execute(post=True).votable, | ||
| original_row=row) | ||
| return | ||
| # results from batch calls are not guaranteed to be in the same order with | ||
| # the results rows. To map the links back to the original result rows, | ||
| # create a dictionary of IDs to result rows, where ID is the value of | ||
| # the column given by the ref field of the service descriptor (input parameter) | ||
| original_rows = {} | ||
| input_params = _get_input_params_from_resource(self._datalink) | ||
| for name, input_param in input_params.items(): | ||
| if input_param.ref: | ||
| for row in self: | ||
| original_rows[row[input_param.ref]] = row | ||
| self.query = DatalinkQuery.from_resource( | ||
| [_ for _ in self], | ||
| self._datalink, | ||
| session=self._session, | ||
| original_row=None) | ||
| remaining_ids = self.query['ID'] | ||
| if not remaining_ids: | ||
| # we are done before starting | ||
| return | ||
| current_batch = self.query.execute(post=True) | ||
| if len(current_batch) == 0: | ||
| raise DALServiceError( | ||
| 'Could not retrieve datalinks for: {}'.format( | ||
| ', '.join([_ for _ in remaining_ids]))) | ||
| batch_size = 0 # unknown yet | ||
| batch_size_determined = False # apparent only after first returned batch | ||
| while remaining_ids: | ||
| start_remaining_ids = len(remaining_ids) | ||
| res_votable = current_batch.votable.get_first_table() | ||
| id_index = 0 # Datalink spec: ID is the first column | ||
| # Datalink spec: "... all links for a single ID value must be served in | ||
| # consecutive rows in the output" | ||
| # Accordingly, the line below should not be necessary but in | ||
| # practice it might be needed | ||
| np.ma.MaskedArray.sort(res_votable.array, id_index) | ||
| last_id = res_votable.array[id_index][0] | ||
| rows = [] | ||
| for index, row in enumerate(res_votable.array): | ||
| if row[id_index] == last_id: | ||
| rows.append(row) | ||
| else: | ||
| yield DatalinkResults(_get_results_tb(rows, current_batch.votable), | ||
| original_row=original_rows.get(last_id, None)) | ||
| if not batch_size_determined: | ||
| batch_size += 1 | ||
| if last_id in remaining_ids: | ||
| remaining_ids.remove(last_id) | ||
| # proceed to the next ID | ||
| last_id = row[id_index] | ||
| rows = [row] | ||
| if last_id in remaining_ids: | ||
| remaining_ids.remove(last_id) | ||
| if not batch_size_determined: | ||
| batch_size += 1 | ||
| batch_size_determined = True | ||
| yield DatalinkResults(_get_results_tb(rows, current_batch.votable), | ||
| original_row=original_rows.get(last_id, None)) | ||
| if not remaining_ids: | ||
| return # we are done | ||
| if len(remaining_ids) == start_remaining_ids: | ||
| # no progress | ||
| raise DALServiceError( | ||
| 'Could not retrieve datalinks for: {}'.format( | ||
| ', '.join([_ for _ in remaining_ids]))) | ||
| self.query['ID'] = remaining_ids[:batch_size] | ||
| current_batch = self.query.execute(post=True) | ||
| if not current_batch: | ||
| raise DALServiceError( | ||
| 'Could not retrieve datalinks for: {}'.format( | ||
| ', '.join([_ for _ in remaining_ids]))) | ||
| @staticmethod | ||
@@ -293,13 +374,18 @@ def _guess_access_format(row): | ||
| def iter_datalinks(self): | ||
| def iter_datalinks(self, preserve_order=False): | ||
| """ | ||
| Iterates over all datalinks in a DALResult. | ||
| Parameters | ||
| ---------- | ||
| preserve_order : bool | ||
| True to return the datalinks keeping the order of the current rows. | ||
| NOTE: There might be a performance penalty for keeping the order as | ||
| one request per row is sent to the service. When the order of the | ||
| datalinks is not important, the execution is optimized to query the | ||
| service in batches. | ||
| """ | ||
| # To reduce the number of calls to the Datalink service, multiple | ||
| # IDs are sent in batches. The appropriate batch size is not available | ||
| # as each service has its own criteria for determining the maximum | ||
| # number of IDs processed per request. To overcome this limitation, | ||
| # this method initially sends all the available IDs and if the | ||
| # response is partial, uses the size of the response as the batch | ||
| # size. | ||
| if not hasattr(self, '_datalink'): | ||
@@ -315,3 +401,4 @@ try: | ||
| else: | ||
| yield from self._iter_datalinks_from_dlblock(self._datalink) | ||
| yield from self._iter_datalinks_from_dlblock( | ||
| preserve_order=preserve_order) | ||
@@ -426,4 +513,2 @@ | ||
| ---------- | ||
| baseurl : str | ||
| the base URL for the Datalink service | ||
| id : str | ||
@@ -946,3 +1031,3 @@ the dataset identifier | ||
| class AxisParamMixin(): | ||
| class AxisParamMixin: | ||
| """ | ||
@@ -949,0 +1034,0 @@ Stores the axis parameters (pos, band, time and pol) used in SODA |
@@ -87,5 +87,3 @@ """ | ||
| a message describing the cause of the error | ||
| code : int | ||
| the HTTP error code (as an integer) | ||
| cause : str | ||
| cause : Exception | ||
| an exception issued as the underlying cause. A value | ||
@@ -121,3 +119,3 @@ of None indicates that no underlying exception was | ||
| ---------- | ||
| cause : str | ||
| cause : Exception | ||
| an exception issued as the underlying cause. A value | ||
@@ -155,3 +153,3 @@ of None indicates that no underlying exception was caught. | ||
| the HTTP error code (as an integer) | ||
| cause : str | ||
| cause : Exception | ||
| an exception issued as the underlying cause. A value | ||
@@ -158,0 +156,0 @@ of None indicates that no underlying exception was |
@@ -24,3 +24,3 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
| __all__ = ["DALService", "DALServiceError", "DALQuery", "DALQueryError", | ||
| "DALResults", "Record"] | ||
| "DALResults", "Record", "UploadList"] | ||
@@ -315,3 +315,3 @@ import os | ||
| ---------- | ||
| votable : str | ||
| votable : astropy.io.votable.tree.VOTableFile | ||
| the service response parsed into an | ||
@@ -318,0 +318,0 @@ astropy.io.votable.tree.VOTableFile instance. |
+3
-3
@@ -189,3 +189,3 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
| io.BytesIO(response.content)).getroot() | ||
| exampleElements = root.findall('.//*[@property="query"]') | ||
| example_elements = root.findall('.//*[@property="query"]') | ||
| except Exception as ex: | ||
@@ -195,3 +195,3 @@ raise DALServiceError.from_except(ex, examples_uri) | ||
| examples = [TAPQuery(self.baseurl, example.text) | ||
| for example in exampleElements] | ||
| for example in example_elements] | ||
@@ -469,3 +469,3 @@ for continuation in root.findall('.//*[@property="continuation"]'): | ||
| This includes the interface capabilities, and the content description | ||
| if it doesn't contains multiple data collections (in other words, it is | ||
| if it doesn't contain multiple data collections (in other words, it is | ||
| not a TAP service). | ||
@@ -472,0 +472,0 @@ """ |
@@ -147,19 +147,13 @@ #!/usr/bin/env python | ||
| datalinks = next(results.iter_datalinks()) | ||
| for preserve_order in [False, True]: | ||
| dl_res = set(results.iter_datalinks(preserve_order=preserve_order)) | ||
| assert len(dl_res) == 1 | ||
| datalinks = dl_res.pop() | ||
| assert datalinks.original_row["accsize"] == 100800 | ||
| assert datalinks.original_row["accsize"] == 100800 | ||
| assert 4 == len(datalinks) | ||
| for dl in datalinks: | ||
| assert dl.semantics in ['#this', '#preview', '#progenitor', '#proc'] | ||
| row = datalinks[0] | ||
| assert row.semantics == "#progenitor" | ||
| row = datalinks[1] | ||
| assert row.semantics == "#proc" | ||
| row = datalinks[2] | ||
| assert row.semantics == "#this" | ||
| row = datalinks[3] | ||
| assert row.semantics == "#preview" | ||
| @pytest.mark.usefixtures('obscore_datalink', 'res_datalink') | ||
@@ -174,5 +168,6 @@ @pytest.mark.filterwarnings("ignore::astropy.io.votable.exceptions.W27") | ||
| dls = list(results.iter_datalinks()) | ||
| assert len(dls) == 3 | ||
| assert dls[0].original_row["obs_collection"] == "MACHO" | ||
| for preserve_order in [False, True]: | ||
| dls = list(results.iter_datalinks(preserve_order=preserve_order)) | ||
| assert len(dls) == 3 | ||
| assert dls[0].original_row["obs_collection"] == "MACHO" | ||
@@ -179,0 +174,0 @@ |
+1
-1
@@ -20,3 +20,3 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
| class EndpointMixin(): | ||
| class EndpointMixin: | ||
| def _get_endpoint_candidates(self, endpoint): | ||
@@ -23,0 +23,0 @@ """Construct endpoint URLs from base URL and endpoint""" |
@@ -372,2 +372,5 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
| def __str__(self): | ||
| return f"{self.unit}:{self.content}" | ||
| @xmlattribute | ||
@@ -404,8 +407,8 @@ def unit(self): | ||
| def __repr__(self): | ||
| return '<DataLimits default={}:{} hard={}:{}/>'.format( | ||
| self.default.unit, | ||
| self.default.content, | ||
| self.hard.unit, | ||
| self.hard.content | ||
| ) | ||
| parts = [] | ||
| if self.default is not None: | ||
| parts.append("default={self.default}") | ||
| if self.hard is not None: | ||
| parts.append("hard={self.hard}") | ||
| return '<DataLimits {}/>'.format(" ".join(parts)) | ||
@@ -499,3 +502,3 @@ @xmlelement(cls=DataLimit, multiple_exc=W29) | ||
| if self.uploadlimit: | ||
| if self.uploadlimit and self.uploadlimit.hard: | ||
| print("Maximal size of uploads") | ||
@@ -502,0 +505,0 @@ print(indent("Maximum {} {}".format( |
@@ -6,2 +6,4 @@ #!/usr/bin/env python | ||
| """ | ||
| import contextlib | ||
| import io | ||
@@ -26,2 +28,8 @@ from operator import eq as equals | ||
| @pytest.fixture(name='parsed_minimal_tapregext') | ||
| def _minimal_tapregext(): | ||
| return vosi.parse_capabilities(get_pkg_data_filename( | ||
| "data/capabilities/minimal-tapregext.xml")) | ||
| @pytest.mark.usefixtures("parsed_caps") | ||
@@ -156,2 +164,30 @@ class TestCapabilities: | ||
| @pytest.mark.usefixtures("parsed_minimal_tapregext") | ||
| class TestMinimalTAPRegExt: | ||
| def test_standard_id(self, parsed_minimal_tapregext): | ||
| assert isinstance(parsed_minimal_tapregext[2], tr.TableAccess) | ||
| def test_limits(self, parsed_minimal_tapregext): | ||
| assert parsed_minimal_tapregext[2]._uploadlimit is None | ||
| assert parsed_minimal_tapregext[2]._outputlimit is None | ||
| def test_adql(self, parsed_minimal_tapregext): | ||
| assert parsed_minimal_tapregext[2].get_adql().name == "ADQL" | ||
| def test_udfs(self, parsed_minimal_tapregext): | ||
| assert parsed_minimal_tapregext[2].get_adql( | ||
| ).languagefeaturelists == [] | ||
| def test_uploadmethods(self, parsed_minimal_tapregext): | ||
| assert parsed_minimal_tapregext[2].uploadmethods == [] | ||
| def test_describe(self, parsed_minimal_tapregext): | ||
| with io.StringIO() as buf, contextlib.redirect_stdout(buf): | ||
| parsed_minimal_tapregext[2].describe() | ||
| output = buf.getvalue() | ||
| assert "http://example.org/tap" in output | ||
| assert "Output format text/xml" in output | ||
| assert "Maximal size" not in output | ||
| @pytest.mark.usefixtures("parsed_caps") | ||
@@ -158,0 +194,0 @@ class TestInterface: |
@@ -933,3 +933,3 @@ #!/usr/bin/env python | ||
| def test_endpoint_switching(): | ||
| alt_svc = "http://vao.stsci.edu/RegTAP/TapService.aspx" | ||
| alt_svc = "https://mast.stsci.edu/vo-tap/api/v0.1/registry" | ||
| previous_url = regtap.REGISTRY_BASEURL | ||
@@ -936,0 +936,0 @@ try: |
+1
-1
@@ -8,2 +8,2 @@ # Note that we need to fall back to the hard-coded version if either | ||
| except Exception: | ||
| version = '1.6.1' | ||
| version = '1.6.2' |
+1
-3
@@ -37,3 +37,3 @@ [tool:pytest] | ||
| author = the PyVO Developers | ||
| license = BSD | ||
| license = BSD-3-Clause | ||
| license_file = LICENSE.rst | ||
@@ -47,3 +47,2 @@ edit_on_github = False | ||
| Intended Audience :: Science/Research | ||
| License :: OSI Approved :: BSD License | ||
| Operating System :: OS Independent | ||
@@ -55,3 +54,2 @@ Programming Language :: Python | ||
| Topic :: Software Development :: Libraries | ||
| License :: OSI Approved :: BSD License | ||
@@ -58,0 +56,0 @@ [options] |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
2479683
0.3%269
0.37%23213
0.42%