3
3
Simbad query class for accessing the Simbad Service
4
4
"""
5
5
6
- import copy
7
6
import re
8
7
import requests
9
8
import json
12
11
from io import BytesIO
13
12
import warnings
14
13
import astropy .units as u
14
+ from astropy .utils import isiterable
15
15
from astropy .utils .data import get_pkg_data_filename
16
16
import astropy .coordinates as coord
17
17
from astropy .table import Table
@@ -202,25 +202,20 @@ class SimbadBibcodeResult(SimbadResult):
202
202
"""Bibliography-type Simbad result"""
203
203
@property
204
204
def table (self ):
205
- bibcode_match = bibcode_regex .search (self .script )
206
- splitter = bibcode_match .group (2 )
207
- ref_list = [splitter + ref for ref in self .data .split (splitter )][1 :]
208
- max_len = max ([len (r ) for r in ref_list ])
209
- table = Table (names = ['References' ], dtype = ['U%i' % max_len ])
210
- for ref in ref_list :
211
- table .add_row ([ref ])
212
- return table
205
+ splitter = bibcode_regex .search (self .script ).group (2 )
206
+ ref_list = [[splitter + ref ] for ref in self .data .split (splitter )[1 :]]
207
+ max_len = max (len (r [0 ]) for r in ref_list )
208
+ return Table (rows = ref_list , names = ['References' ], dtype = [f"U{ max_len } " ])
213
209
214
210
215
211
class SimbadObjectIDsResult (SimbadResult ):
216
212
"""Object identifier list Simbad result"""
217
213
@property
218
214
def table (self ):
219
- max_len = max ([len (i ) for i in self .data .splitlines ()])
220
- table = Table (names = ['ID' ], dtype = ['S%i' % max_len ])
221
- for id in self .data .splitlines ():
222
- table .add_row ([id .strip ()])
223
- return table
215
+ split_lines = self .data .splitlines ()
216
+ ids = [[id .strip ()] for id in split_lines ]
217
+ max_len = max (map (len , split_lines ))
218
+ return Table (rows = ids , names = ['ID' ], dtype = [f"S{ max_len } " ])
224
219
225
220
226
221
class SimbadBaseQuery (BaseQuery ):
@@ -281,8 +276,6 @@ class SimbadClass(SimbadBaseQuery):
281
276
),
282
277
'[^0-9]' : 'Any (one) character not in the list.' }
283
278
284
- _ORDERED_WILDCARDS = ['*' , '?' , '[abc]' , '[^0-9]' ]
285
-
286
279
# query around not included since this is a subcase of query_region
287
280
_function_to_command = {
288
281
'query_object_async' : 'query id' ,
@@ -303,7 +296,7 @@ class SimbadClass(SimbadBaseQuery):
303
296
304
297
def __init__ (self ):
305
298
super ().__init__ ()
306
- self ._VOTABLE_FIELDS = copy . copy ( self ._VOTABLE_FIELDS )
299
+ self ._VOTABLE_FIELDS = self ._VOTABLE_FIELDS . copy ( )
307
300
308
301
def list_wildcards (self ):
309
302
"""
@@ -323,10 +316,7 @@ def list_wildcards(self):
323
316
[abc] : Exactly one character taken in the list.
324
317
Can also be defined by a range of characters: [A-Z]
325
318
"""
326
- for key in self ._ORDERED_WILDCARDS :
327
- print ("{key} : {value}\n " .format (key = key ,
328
- value = self .WILDCARDS [key ]))
329
- return
319
+ print ("\n \n " .join (f"{ k } : { v } " for k , v in self .WILDCARDS .items ()))
330
320
331
321
def list_votable_fields (self ):
332
322
"""
@@ -353,8 +343,8 @@ def list_votable_fields(self):
353
343
fields_dict = json .load (f )
354
344
355
345
print ("Available VOTABLE fields:\n " )
356
- for field in list ( sorted (fields_dict .keys () )):
357
- print ("{}" . format (field ))
346
+ for field in sorted (fields_dict .keys ()):
347
+ print (str (field ))
358
348
print ("For more information on a field:\n "
359
349
"Simbad.get_field_description ('field_name') \n "
360
350
"Currently active VOTABLE fields:\n {0}"
@@ -415,10 +405,7 @@ def add_votable_fields(self, *args):
415
405
os .path .join ('data' , 'votable_fields_dict.json' ))
416
406
417
407
with open (dict_file , "r" ) as f :
418
- fields_dict = json .load (f )
419
- fields_dict = dict (
420
- ((strip_field (ff ), fields_dict [ff ])
421
- for ff in fields_dict ))
408
+ fields_dict = {strip_field (k ): v for k , v in json .load (f ).items ()}
422
409
423
410
for field in args :
424
411
sf = strip_field (field )
@@ -427,34 +414,30 @@ def add_votable_fields(self, *args):
427
414
else :
428
415
self ._VOTABLE_FIELDS .append (field )
429
416
430
- def remove_votable_fields (self , * args , ** kwargs ):
417
+ def remove_votable_fields (self , * args , strip_params = False ):
431
418
"""
432
419
Removes the specified field names from ``SimbadClass._VOTABLE_FIELDS``
433
420
434
421
Parameters
435
422
----------
436
423
list of field_names to be removed
437
- strip_params: bool
424
+ strip_params: bool, optional
438
425
If true, strip the specified keywords before removing them:
439
426
e.g., ra(foo) would remove ra(bar) if this is True
440
427
"""
441
- strip_params = kwargs .pop ('strip_params' , False )
442
-
443
428
if strip_params :
444
- sargs = [ strip_field (a ) for a in args ]
429
+ sargs = { strip_field (a ) for a in args }
445
430
sfields = [strip_field (a ) for a in self ._VOTABLE_FIELDS ]
446
431
else :
447
- sargs = args
432
+ sargs = set ( args )
448
433
sfields = self ._VOTABLE_FIELDS
449
- absent_fields = set (sargs ) - set (sfields )
450
-
451
- for b , f in list (zip (sfields , self ._VOTABLE_FIELDS )):
452
- if b in sargs :
453
- self ._VOTABLE_FIELDS .remove (f )
454
434
455
- for field in absent_fields :
435
+ for field in sargs . difference ( sfields ) :
456
436
warnings .warn ("{field}: this field is not set" .format (field = field ))
457
437
438
+ zipped_fields = zip (sfields , self ._VOTABLE_FIELDS )
439
+ self ._VOTABLE_FIELDS = [f for b , f in zipped_fields if b not in sargs ]
440
+
458
441
# check if all fields are removed
459
442
if not self ._VOTABLE_FIELDS :
460
443
warnings .warn ("All fields have been removed. "
@@ -678,8 +661,6 @@ def query_region_async(self, coordinates, radius=2*u.arcmin,
678
661
679
662
# handle the vector case
680
663
if isinstance (ra , list ):
681
- vector = True
682
-
683
664
if len (ra ) > 10000 :
684
665
warnings .warn ("For very large queries, you may receive a "
685
666
"timeout error. SIMBAD suggests splitting "
@@ -689,21 +670,19 @@ def query_region_async(self, coordinates, radius=2*u.arcmin,
689
670
if len (set (frame )) > 1 :
690
671
raise ValueError ("Coordinates have different frames" )
691
672
else :
692
- frame = set ( frame ). pop ()
673
+ frame = frame [ 0 ]
693
674
694
- if vector and _has_length (radius ) and len (radius ) == len (ra ):
695
- # all good, continue
696
- pass
697
- elif vector and _has_length (radius ) and len (radius ) != len (ra ):
698
- raise ValueError ("Mismatch between radii and coordinates" )
699
- elif vector and not _has_length (radius ):
675
+ # `radius` as `str` is iterable, but contains only one value.
676
+ if isiterable (radius ) and not isinstance (radius , str ):
677
+ if len (radius ) != len (ra ):
678
+ raise ValueError ("Mismatch between radii and coordinates" )
679
+ else :
700
680
radius = [_parse_radius (radius )] * len (ra )
701
681
702
- if vector :
703
- query_str = "\n " .join ([base_query_str
704
- .format (ra = ra_ , dec = dec_ , rad = rad_ ,
705
- frame = frame , equinox = equinox )
706
- for ra_ , dec_ , rad_ in zip (ra , dec , radius )])
682
+ query_str = "\n " .join (base_query_str
683
+ .format (ra = ra_ , dec = dec_ , rad = rad_ ,
684
+ frame = frame , equinox = equinox )
685
+ for ra_ , dec_ , rad_ in zip (ra , dec , radius ))
707
686
708
687
else :
709
688
radius = _parse_radius (radius )
@@ -956,20 +935,13 @@ def query_objectids_async(self, object_name, cache=True,
956
935
return response
957
936
958
937
def _get_query_header (self , get_raw = False ):
959
- votable_fields = ',' .join (self .get_votable_fields ())
960
938
# if get_raw is set then don't fetch as votable
961
939
if get_raw :
962
940
return ""
963
- votable_def = "votable {" + votable_fields + "}"
964
- votable_open = "votable open"
965
- return "\n " .join ([votable_def , votable_open ])
941
+ return "votable {" + ',' .join (self .get_votable_fields ()) + "}\n votable open"
966
942
967
943
def _get_query_footer (self , get_raw = False ):
968
- if get_raw :
969
- return ""
970
- votable_close = "votable close"
971
-
972
- return votable_close
944
+ return "" if get_raw else "votable close"
973
945
974
946
@validate_epoch_decorator
975
947
@validate_equinox_decorator
@@ -1004,25 +976,19 @@ def _args_to_payload(self, *args, **kwargs):
1004
976
kwargs ['equi' ] = kwargs ['equinox' ]
1005
977
del kwargs ['equinox' ]
1006
978
# remove default None from kwargs
1007
- # be compatible with python3
1008
- for key in list (kwargs ):
1009
- if not kwargs [key ]:
1010
- del kwargs [key ]
979
+ kwargs = {key : value for key , value in kwargs .items () if value is not None }
1011
980
# join in the order specified otherwise results in error
1012
981
all_keys = ['radius' , 'frame' , 'equi' , 'epoch' ]
1013
982
present_keys = [key for key in all_keys if key in kwargs ]
1014
983
if caller == 'query_criteria_async' :
1015
- for k in kwargs :
1016
- present_keys .append (k )
984
+ present_keys .extend (kwargs )
1017
985
# need ampersands to join args
1018
986
args_str = '&' .join ([str (val ) for val in args ])
1019
- if len ( args ) > 0 and len ( present_keys ) > 0 :
987
+ if args and present_keys :
1020
988
args_str += " & "
1021
989
else :
1022
990
args_str = ' ' .join ([str (val ) for val in args ])
1023
- kwargs_str = ' ' .join ("{key}={value}" .format (key = key ,
1024
- value = kwargs [key ])
1025
- for key in present_keys )
991
+ kwargs_str = ' ' .join (f"{ key } ={ kwargs [key ]} " for key in present_keys )
1026
992
1027
993
# For the record, I feel dirty for writing this wildcard-case hack.
1028
994
# This entire function should be refactored when someone has time.
@@ -1081,17 +1047,8 @@ def _parse_coordinates(coordinates):
1081
1047
raise ValueError ("Coordinates not specified correctly" )
1082
1048
1083
1049
1084
- def _has_length (x ):
1085
- # some objects have '__len__' attributes but have no len()
1086
- try :
1087
- len (x )
1088
- return True
1089
- except (TypeError , AttributeError ):
1090
- return False
1091
-
1092
-
1093
1050
def _get_frame_coords (coordinates ):
1094
- if _has_length (coordinates ):
1051
+ if isiterable (coordinates ):
1095
1052
# deal with vectors differently
1096
1053
parsed = [_get_frame_coords (cc ) for cc in coordinates ]
1097
1054
return ([ra for ra , dec , frame in parsed ],
0 commit comments