Skip to content

Commit 6a40ab4

Browse files
committed
refactor
1 parent ce90454 commit 6a40ab4

12 files changed

Lines changed: 275 additions & 229 deletions

File tree

hapiclient/cache.py

Lines changed: 73 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
def server2dirname(server):
22
"""Convert a server URL to a directory name."""
3+
34
import re
5+
46
urld = re.sub(r"https*://", "", server)
57
urld = re.sub(r'/', '_', urld)
68

@@ -14,6 +16,7 @@ def cachedir(*args):
1416
1517
cachedir(basedir, server) returns os.path.join(basedir, server2dirname(server))
1618
"""
19+
1720
import os
1821
import tempfile
1922

@@ -30,62 +33,64 @@ def cachedir(*args):
3033

3134

3235
def request2path(*args):
33-
# request2path(server, dataset, parameters, start, stop)
34-
# request2path(server, dataset, parameters, start, stop, cachedir)
35-
import os
36-
import re
37-
38-
if len(args) == 5:
39-
# Use default if cachedir not given.
40-
cachedirectory = cachedir()
41-
else:
42-
cachedirectory = args[5]
43-
44-
args = list(args)
45-
46-
# Replace forbidden characters in directory and filename
47-
# Replacements assume that there will be no name collisions,
48-
# e.g., one parameter named abc-< and another abc-@lt@.
49-
# This also introduces an incompatability between caches on Windows
50-
# Unix.
51-
import platform
52-
if platform.system() == 'Windows':
53-
# List and code from responses in
54-
# https://stackoverflow.com/q/1976007
55-
reps = (
56-
('<', '@lt@'),
57-
('>', '@gt@'),
58-
(':', '@colon@'),
59-
('"', '@doublequote@'),
60-
('/', '@forwardslash@'),
61-
('/', '@backslash@'),
62-
('\\|', '@pipe@'),
63-
('\\?', '@questionmark@'),
64-
('\\*', '@asterisk@')
65-
)
66-
67-
for element in reps:
68-
args[1] = re.sub(element[0], element[1], args[1])
69-
args[2] = re.sub(element[0], element[1], args[2])
70-
71-
else:
72-
args[1] = re.sub('/','@forwardslash@',args[1])
73-
args[2] = re.sub('/','@forwardslash@',args[2])
74-
75-
# To shorten filenames.
76-
args[3] = re.sub(r'-|:|\.|Z', '', args[3])
77-
args[4] = re.sub(r'-|:|\.|Z', '', args[4])
78-
79-
# URL subdirectory
80-
urldirectory = server2dirname(args[0])
81-
fname = '%s_%s_%s_%s' % (args[1], args[2], args[3], args[4])
82-
83-
return os.path.join(cachedirectory, urldirectory, fname)
36+
# request2path(server, dataset, parameters, start, stop)
37+
# request2path(server, dataset, parameters, start, stop, cachedir)
38+
import os
39+
import re
40+
import platform
41+
42+
if len(args) == 5:
43+
# Use default if cachedir not given.
44+
cachedirectory = cachedir()
45+
else:
46+
cachedirectory = args[5]
47+
48+
args = list(args)
49+
50+
# Replace forbidden characters in directory and filename
51+
# Replacements assume that there will be no name collisions,
52+
# e.g., one parameter named abc-< and another abc-@lt@.
53+
# This also introduces an incompatibility between caches on Windows
54+
# Unix.
55+
if platform.system() == 'Windows':
56+
# List and code from responses in
57+
# https://stackoverflow.com/q/1976007
58+
reps = (
59+
('<', '@lt@'),
60+
('>', '@gt@'),
61+
(':', '@colon@'),
62+
('"', '@doublequote@'),
63+
('/', '@forwardslash@'),
64+
('/', '@backslash@'),
65+
('\\|', '@pipe@'),
66+
('\\?', '@questionmark@'),
67+
('\\*', '@asterisk@')
68+
)
69+
70+
for element in reps:
71+
args[1] = re.sub(element[0], element[1], args[1])
72+
args[2] = re.sub(element[0], element[1], args[2])
73+
74+
else:
75+
args[1] = re.sub('/','@forwardslash@',args[1])
76+
args[2] = re.sub('/','@forwardslash@',args[2])
77+
78+
# To shorten filenames.
79+
args[3] = re.sub(r'-|:|\.|Z', '', args[3])
80+
args[4] = re.sub(r'-|:|\.|Z', '', args[4])
81+
82+
# URL subdirectory
83+
urldirectory = server2dirname(args[0])
84+
fname = '%s_%s_%s_%s' % (args[1], args[2], args[3], args[4])
85+
86+
return os.path.join(cachedirectory, urldirectory, fname)
8487

8588

8689
def meta_cache_paths(SERVER, DATASET, cachedir):
8790
"""Return dict with metadata cache directory and file names."""
91+
8892
fname_root = request2path(SERVER, DATASET, '', '', '', cachedir)
93+
8994
return {
9095
'json': fname_root + '.json',
9196
'pkl': fname_root + '.pkl'
@@ -94,9 +99,11 @@ def meta_cache_paths(SERVER, DATASET, cachedir):
9499

95100
def meta_cache_read(SERVER, DATASET, opts):
96101
"""Read metadata from PKL cache. Returns meta dict or None."""
102+
97103
import os
98104
import pickle
99-
from hapiclient.util import log
105+
106+
from hapiclient.log import log
100107

101108
if not opts["usecache"]:
102109
log('Not checking metadata cache because usecache is False.')
@@ -116,10 +123,12 @@ def meta_cache_read(SERVER, DATASET, opts):
116123

117124
def meta_cache_write(meta, SERVER, DATASET, opts):
118125
"""Write metadata to JSON and PKL cache files."""
126+
119127
import os
120128
import json
121129
import pickle
122-
from hapiclient.util import log
130+
131+
from hapiclient.log import log
123132

124133
if not opts["cache"]:
125134
return
@@ -142,7 +151,9 @@ def meta_cache_write(meta, SERVER, DATASET, opts):
142151

143152
def data_cache_paths(SERVER, DATASET, PARAMETERS, START, STOP, cachedir):
144153
"""Return dict with data cache file names."""
154+
145155
fname_root = request2path(SERVER, DATASET, PARAMETERS, START, STOP, cachedir)
156+
146157
return {
147158
'csv': fname_root + '.csv',
148159
'bin': fname_root + '.bin',
@@ -153,9 +164,11 @@ def data_cache_paths(SERVER, DATASET, PARAMETERS, START, STOP, cachedir):
153164

154165
def data_cache_read_metax(SERVER, DATASET, PARAMETERS, START, STOP, opts):
155166
"""Read extended request metadata from PKL cache. Returns meta dict or None."""
167+
156168
import os
157169
import pickle
158-
from hapiclient.util import log
170+
171+
from hapiclient.log import log
159172

160173
if not opts["usecache"]:
161174
log('Not checking data cache because usecache is False.')
@@ -174,9 +187,11 @@ def data_cache_read_metax(SERVER, DATASET, PARAMETERS, START, STOP, opts):
174187

175188
def data_cache_read_npy(SERVER, DATASET, PARAMETERS, START, STOP, opts):
176189
"""Read cached numpy data array. Returns None if not cached."""
190+
177191
import os
178192
import numpy as np
179-
from hapiclient.util import log
193+
194+
from hapiclient.log import log
180195

181196
if not opts["usecache"]:
182197
return None
@@ -198,11 +213,13 @@ def data_cache_write(data_result, meta, SERVER, DATASET, PARAMETERS, START, STOP
198213
199214
Also updates meta with file-related x_ fields before writing.
200215
"""
216+
201217
import os
202218
import pickle
203219
import warnings
204220
import numpy as np
205-
from hapiclient.util import log
221+
222+
from hapiclient.log import log
206223

207224
data_paths = data_cache_paths(SERVER, DATASET, PARAMETERS, START, STOP, opts['cachedir'])
208225
fnamecsv, fnamebin, fnamenpy, fnamepklx = data_paths['csv'], data_paths['bin'], data_paths['npy'], data_paths['pkl']
@@ -236,80 +253,3 @@ def data_cache_write(data_result, meta, SERVER, DATASET, PARAMETERS, START, STOP
236253
}
237254
warnings.filterwarnings("ignore", **kwargs)
238255
np.save(fnamenpy, data_result)
239-
240-
241-
def _compute_dt(meta, opts):
242-
import numpy as np
243-
244-
# Compute data type variable dt used to read HAPI response into
245-
# a data structure.
246-
pnames, psizes, ptypes, dt = [], [], [], []
247-
248-
# Each element of cols is an array with start/end column number of
249-
# parameter.
250-
cols = np.zeros([len(meta["parameters"]), 2], dtype=np.int32)
251-
ss = 0 # running sum of prod(size)
252-
253-
# Extract sizes and types of parameters.
254-
for i in range(0, len(meta["parameters"])):
255-
ptype = meta["parameters"][i]["type"]
256-
257-
ptypes.append(ptype)
258-
259-
pnames.append(str(meta["parameters"][i]["name"]))
260-
if 'size' in meta["parameters"][i]:
261-
psizes.append(meta["parameters"][i]['size'])
262-
else:
263-
psizes.append(1)
264-
265-
# For size = [N] case, readers want
266-
# dtype = ('name', type, N)
267-
# not
268-
# dtype = ('name', type, [N])
269-
if type(psizes[i]) is list and len(psizes[i]) == 1:
270-
psizes[i] = psizes[i][0]
271-
272-
if type(psizes[i]) is list and len(psizes[i]) > 1:
273-
# psizes[i] = list(reversed(psizes[i]))
274-
psizes[i] = list(psizes[i])
275-
276-
# First column of ith parameter.
277-
cols[i][0] = ss
278-
# Last column of ith parameter.
279-
cols[i][1] = ss + np.prod(psizes[i]) - 1
280-
# Running sum of columns.
281-
ss = cols[i][1] + 1
282-
283-
# HAPI numerical formats are 64-bit LE floating point and 32-bit LE
284-
# signed integers.
285-
if ptype == 'double':
286-
dtype = (pnames[i], '<d', psizes[i])
287-
if ptype == 'integer':
288-
dtype = (pnames[i], np.dtype('<i4'), psizes[i])
289-
290-
if ptype == 'string' or ptype == 'isotime':
291-
if 'length' in meta["parameters"][i]:
292-
# length is specified for parameter in metadata. Use it.
293-
if ptype == 'string':
294-
dtype = (pnames[i], 'U' + str(meta["parameters"][i]["length"]), psizes[i])
295-
if ptype == 'isotime':
296-
dtype = (pnames[i], 'S' + str(meta["parameters"][i]["length"]), psizes[i])
297-
else:
298-
# A string or isotime parameter did not have a length.
299-
# Will need to use slower CSV read method.
300-
if ptype == 'string' or ptype == 'isotime':
301-
dtype = (pnames[i], object, psizes[i])
302-
303-
# For testing reader. Force use of slow read method.
304-
if opts['format'] == 'csv':
305-
if opts['method'] == 'numpynolength' or opts['method'] == 'pandasnolength':
306-
if ptype == 'string' or ptype == 'isotime':
307-
dtype = (pnames[i], object, psizes[i])
308-
309-
# https://numpy.org/doc/stable/release/1.17.0-notes.html#shape-1-fields-in-dtypes-won-t-be-collapsed-to-scalars-in-a-future-version
310-
if dtype[2] == 1:
311-
dtype = dtype[0:2]
312-
313-
dt.append(dtype)
314-
315-
return dt, cols, psizes, pnames, ptypes

hapiclient/capabilities.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
def capabilities(SERVER):
3+
"""Return the capabilities of a HAPI server.
4+
5+
Args:
6+
SERVER (str): The base URL of the HAPI server.
7+
8+
Returns:
9+
dict: A dictionary containing the capabilities of the server.
10+
"""
11+
from hapiclient.util import urlopen
12+
13+
caps = urlopen(SERVER + '/capabilities', parse_json=True)
14+
15+
return caps
16+
17+
18+
def get_format(SERVER, format):
19+
"""Return the transport format to use, accounting for server capabilities.
20+
21+
If the requested format is not supported by the server, falls back to 'csv'
22+
with a warning.
23+
"""
24+
25+
from hapiclient.util import error, warning
26+
27+
cformats = ['csv', 'binary'] # client formats
28+
if format not in cformats:
29+
msg = 'This client does not handle transport format "%s". Available options: %s'
30+
error(msg % (format, ', '.join(cformats)))
31+
32+
if format != 'csv':
33+
caps = capabilities(SERVER)
34+
sformats = caps["outputFormats"] # Server formats
35+
if format not in sformats:
36+
msg = 'Requested transport format "%s" not avaiable from %s. Will use "csv". Available options: %s'
37+
warning(msg % (format, SERVER, ', '.join(sformats)))
38+
format = 'csv'
39+
if 'binary' not in sformats:
40+
format = 'csv'
41+
42+
return format

hapiclient/catalog.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from hapiclient.util import log, urlopen
1+
from hapiclient.log import log
2+
from hapiclient.util import urlopen
23

34

45
def catalog(SERVER):

0 commit comments

Comments
 (0)