Template ModuleΒΆ
The template module, written by Madhura Parikh, shows how to build your own module for a new online service.
# Licensed under a 3-clause BSD style license - see LICENSE.rst
# put all imports organized as shown below
# 1. standard library imports
# 2. third party imports
import astropy.units as u
import astropy.coordinates as coord
import astropy.io.votable as votable
from astropy.table import Table
from astropy.io import fits
# 3. local imports - use relative imports
# commonly required local imports shown below as example
# all Query classes should inherit from BaseQuery.
from ..query import BaseQuery
# has common functions required by most modules
from ..utils import commons
# prepend_docstr is a way to copy docstrings between methods
from ..utils import prepend_docstr_nosections
# async_to_sync generates the relevant query tools from _async methods
from ..utils import async_to_sync
# import configurable items declared in __init__.py
from . import conf
# export all the public classes and methods
__all__ = ['Template', 'TemplateClass']
# declare global variables and constants if any
# Now begin your main class
# should be decorated with the async_to_sync imported previously
@async_to_sync
class TemplateClass(BaseQuery):
"""
Not all the methods below are necessary but these cover most of the common
cases, new methods may be added if necessary, follow the guidelines at
<http://astroquery.readthedocs.io/en/latest/api.html>
"""
# use the Configuration Items imported from __init__.py to set the URL,
# TIMEOUT, etc.
URL = conf.server
TIMEOUT = conf.timeout
# all query methods are implemented with an "async" method that handles
# making the actual HTTP request and returns the raw HTTP response, which
# should be parsed by a separate _parse_result method. The query_object
# method is created by async_to_sync automatically. It would look like
# this:
"""
def query_object(object_name, get_query_payload=False)
response = self.query_object_async(object_name,
get_query_payload=get_query_payload)
if get_query_payload:
return response
result = self._parse_result(response, verbose=verbose)
return result
"""
def query_object_async(self, object_name, get_query_payload=False,
cache=True):
"""
This method is for services that can parse object names. Otherwise
use :meth:`astroquery.template_module.TemplateClass.query_region`.
Put a brief description of what the class does here.
Parameters
----------
object_name : str
name of the identifier to query.
get_query_payload : bool, optional
This should default to False. When set to `True` the method
should return the HTTP request parameters as a dict.
verbose : bool, optional
This should default to `False`, when set to `True` it displays
VOTable warnings.
any_other_param : <param_type>
similarly list other parameters the method takes
Returns
-------
response : `requests.Response`
The HTTP response returned from the service.
All async methods should return the raw HTTP response.
Examples
--------
While this section is optional you may put in some examples that
show how to use the method. The examples are written similar to
standard doctests in python.
"""
# the async method should typically have the following steps:
# 1. First construct the dictionary of the HTTP request params.
# 2. If get_query_payload is `True` then simply return this dict.
# 3. Else make the actual HTTP request and return the corresponding
# HTTP response
# All HTTP requests are made via the `BaseQuery._request` method. This
# use a generic HTTP request method internally, similar to
# `requests.Session.request` of the Python Requests library, but
# with added caching-related tools.
# See below for an example:
# first initialize the dictionary of HTTP request parameters
request_payload = dict()
# Now fill up the dictionary. Here the dictionary key should match
# the exact parameter name as expected by the remote server. The
# corresponding dict value should also be in the same format as
# expected by the server. Additional parsing of the user passed
# value may be required to get it in the right units or format.
# All this parsing may be done in a separate private `_args_to_payload`
# method for cleaner code.
request_payload['object_name'] = object_name
# similarly fill up the rest of the dict ...
if get_query_payload:
return request_payload
# BaseQuery classes come with a _request method that includes a
# built-in caching system
response = self._request('GET', self.URL, params=request_payload,
timeout=self.TIMEOUT, cache=cache)
return response
# For services that can query coordinates, use the query_region method.
# The pattern is similar to the query_object method. The query_region
# method also has a 'radius' keyword for specifying the radius around
# the coordinates in which to search. If the region is a box, then
# the keywords 'width' and 'height' should be used instead. The coordinates
# may be accepted as an `astropy.coordinates` object or as a string, which
# may be further parsed.
# similarly we write a query_region_async method that makes the
# actual HTTP request and returns the HTTP response
def query_region_async(self, coordinates, radius, height, width,
get_query_payload=False, cache=True):
"""
Queries a region around the specified coordinates.
Parameters
----------
coordinates : str or `astropy.coordinates`.
coordinates around which to query
radius : str or `astropy.units.Quantity`.
the radius of the cone search
width : str or `astropy.units.Quantity`
the width for a box region
height : str or `astropy.units.Quantity`
the height for a box region
get_query_payload : bool, optional
Just return the dict of HTTP request parameters.
verbose : bool, optional
Display VOTable warnings or not.
Returns
-------
response : `requests.Response`
The HTTP response returned from the service.
All async methods should return the raw HTTP response.
"""
request_payload = self._args_to_payload(coordinates, radius, height,
width)
if get_query_payload:
return request_payload
response = self._request('GET', self.URL, params=request_payload,
timeout=self.TIMEOUT, cache=cache)
return response
# as we mentioned earlier use various python regular expressions, etc
# to create the dict of HTTP request parameters by parsing the user
# entered values. For cleaner code keep this as a separate private method:
def _args_to_payload(self, *args, **kwargs):
request_payload = dict()
# code to parse input and construct the dict
# goes here. Then return the dict to the caller
return request_payload
# the methods above call the private _parse_result method.
# This should parse the raw HTTP response and return it as
# an `astropy.table.Table`. Below is the skeleton:
def _parse_result(self, response, verbose=False):
# if verbose is False then suppress any VOTable related warnings
if not verbose:
commons.suppress_vo_warnings()
# try to parse the result into an astropy.Table, else
# return the raw result with an informative error message.
try:
# do something with regex to get the result into
# astropy.Table form. return the Table.
pass
except ValueError:
# catch common errors here, but never use bare excepts
# return raw result/ handle in some way
pass
return Table()
# Image queries do not use the async_to_sync approach: the "synchronous"
# version must be defined explicitly. The example below therefore presents
# a complete example of how to write your own synchronous query tools if
# you prefer to avoid the automatic approach.
#
# For image queries, the results should be returned as a
# list of `astropy.fits.HDUList` objects. Typically image queries
# have the following method family:
# 1. get_images - this is the high level method that interacts with
# the user. It reads in the user input and returns the final
# list of fits images to the user.
# 2. get_images_async - This is a lazier form of the get_images function,
# in that it returns just the list of handles to the image files
# instead of actually downloading them.
# 3. extract_image_urls - This takes in the raw HTTP response and scrapes
# it to get the downloadable list of image URLs.
# 4. get_image_list - this is similar to the get_images, but it simply
# takes in the list of URLs scrapped by extract_image_urls and
# returns this list rather than the actual FITS images
# NOTE : in future support may be added to allow the user to save
# the downloaded images to a preferred location. Here we look at the
# skeleton code for image services
def get_images(self, coordinates, radius, get_query_payload):
"""
A query function that searches for image cut-outs around coordinates
Parameters
----------
coordinates : str or `astropy.coordinates`.
coordinates around which to query
radius : str or `astropy.units.Quantity`.
the radius of the cone search
get_query_payload : bool, optional
If true than returns the dictionary of query parameters, posted to
remote server. Defaults to `False`.
Returns
-------
A list of `astropy.fits.HDUList` objects
"""
readable_objs = self.get_images_async(coordinates, radius,
get_query_payload=get_query_payload)
if get_query_payload:
return readable_objs # simply return the dict of HTTP request params
# otherwise return the images as a list of astropy.fits.HDUList
return [obj.get_fits() for obj in readable_objs]
@prepend_docstr_nosections(get_images.__doc__)
def get_images_async(self, coordinates, radius, get_query_payload=False):
"""
Returns
-------
A list of context-managers that yield readable file-like objects
"""
# As described earlier, this function should return just
# the handles to the remote image files. Use the utilities
# in commons.py for doing this:
# first get the links to the remote image files
image_urls = self.get_image_list(coordinates, radius,
get_query_payload=get_query_payload)
if get_query_payload: # if true then return the HTTP request params dict
return image_urls
# otherwise return just the handles to the image files.
return [commons.FileContainer(U) for U in image_urls]
# the get_image_list method, simply returns the download
# links for the images as a list
@prepend_docstr_nosections(get_images.__doc__)
def get_image_list(self, coordinates, radius, get_query_payload=False,
cache=True):
"""
Returns
-------
list of image urls
"""
# This method should implement steps as outlined below:
# 1. Construct the actual dict of HTTP request params.
# 2. Check if the get_query_payload is True, in which
# case it should just return this dict.
# 3. Otherwise make the HTTP request and receive the
# HTTP response.
# 4. Pass this response to the extract_image_urls
# which scrapes it to extract the image download links.
# 5. Return the download links as a list.
request_payload = self._args_to_payload(coordinates, radius)
if get_query_payload:
return request_payload
response = self._request(method="GET", url=self.URL,
data=request_payload,
timeout=self.TIMEOUT, cache=cache)
return self.extract_image_urls(response.text)
# the extract_image_urls method takes in the HTML page as a string
# and uses regexps, etc to scrape the image urls:
def extract_image_urls(self, html_str):
"""
Helper function that uses regex to extract the image urls from the
given HTML.
Parameters
----------
html_str : str
source from which the urls are to be extracted
Returns
-------
list of image URLs
"""
# do something with regex on the HTML
# return the list of image URLs
pass
# the default tool for users to interact with is an instance of the Class
Template = TemplateClass()
# once your class is done, tests should be written
# See ./tests for examples on this
# Next you should write the docs in astroquery/docs/module_name
# using Sphinx.