Skip to content

Commit 838caf8

Browse files
committed
added connection error handling to client; tests for parquet and raster; support for raster sampling
1 parent a74d292 commit 838caf8

File tree

5 files changed

+81
-41
lines changed

5 files changed

+81
-41
lines changed

sliderule/icesat2.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,7 @@ def atl06p(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, callb
796796
columns = {}
797797
elevation_records = []
798798
num_elevations = 0
799-
field_dictionary = {} # ['field_name'] = {"extent_id": [], field_name: []}
799+
field_dictionary = {} # [<field_name>] = {"extent_id": [], <field_name>: []}
800800
if len(rsps) > 0:
801801
# Sort Records
802802
for rsp in rsps:
@@ -806,7 +806,7 @@ def atl06p(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, callb
806806
elif 'extrec' == rsp['__rectype']:
807807
field_name = parm['atl03_geo_fields'][rsp['field_index']]
808808
if field_name not in field_dictionary:
809-
field_dictionary[field_name] = {"extent_id": [], field_name: []}
809+
field_dictionary[field_name] = {'extent_id': [], field_name: []}
810810
# Parse Ancillary Data
811811
data = __get_values(rsp['data'], rsp['datatype'], len(rsp['data']))
812812
# Add Left Pair Track Entry
@@ -815,6 +815,14 @@ def atl06p(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, callb
815815
# Add Right Pair Track Entry
816816
field_dictionary[field_name]['extent_id'] += rsp['extent_id'] | 0x3,
817817
field_dictionary[field_name][field_name] += data[RIGHT_PAIR],
818+
elif 'rsrec' == rsp['__rectype']:
819+
for sample in rsp["samples"]:
820+
time_str = sliderule.gps2utc(sample["time"])
821+
field_name = parm['samples'][rsp['raster_index']] + "-" + time_str.split(" ")[0].strip()
822+
if field_name not in field_dictionary:
823+
field_dictionary[field_name] = {'extent_id': [], field_name: []}
824+
field_dictionary[field_name]['extent_id'] += rsp['extent_id'],
825+
field_dictionary[field_name][field_name] += sample['value'],
818826
# Build Elevation Columns
819827
if num_elevations > 0:
820828
# Initialize Columns

sliderule/sliderule.py

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -455,44 +455,53 @@ def source (api, parm={}, stream=False, callbacks={}, path="/source"):
455455
if c not in callbacks:
456456
callbacks[c] = __callbacks[c]
457457
# Attempt Request
458-
try:
459-
# Construct Request URL and Authorization
460-
if service_org:
461-
url = 'https://%s.%s%s/%s' % (service_org, service_url, path, api)
462-
headers = __build_auth_header()
463-
else:
464-
url = 'http://%s%s/%s' % (service_url, path, api)
465-
# Perform Request
466-
if not stream:
467-
data = session.get(url, data=rqst, headers=headers, timeout=request_timeout)
468-
else:
469-
data = session.post(url, data=rqst, headers=headers, timeout=request_timeout, stream=True)
470-
data.raise_for_status()
471-
# Parse Response
472-
format = data.headers['Content-Type']
473-
if format == 'text/plain':
474-
rsps = __parse_json(data)
475-
elif format == 'application/json':
476-
rsps = __parse_json(data)
477-
elif format == 'application/octet-stream':
478-
rsps = __parse_native(data, callbacks)
479-
else:
480-
raise FatalError('unsupported content type: %s' % (format))
481-
except requests.exceptions.SSLError as e:
482-
raise FatalError("Unable to verify SSL certificate: {}".format(e))
483-
except requests.ConnectionError as e:
484-
raise FatalError("Connection error to endpoint {}".format(url))
485-
except requests.Timeout as e:
486-
raise TransientError("Timed-out waiting for response from endpoint {}".format(url))
487-
except requests.exceptions.ChunkedEncodingError as e:
488-
raise RuntimeError("Unexpected termination of response from endpoint {}".format(url))
489-
except requests.HTTPError as e:
490-
if e.response.status_code == 503:
491-
raise TransientError("Server experiencing heavy load, stalling on request to {}".format(url))
492-
else:
493-
raise FatalError("HTTP error {} from endpoint {}".format(e.response.status_code, url))
494-
except:
495-
raise
458+
complete = False
459+
attempts = 3
460+
while not complete and attempts > 0:
461+
attempts -= 1
462+
try:
463+
# Construct Request URL and Authorization
464+
if service_org:
465+
url = 'https://%s.%s%s/%s' % (service_org, service_url, path, api)
466+
headers = __build_auth_header()
467+
else:
468+
url = 'http://%s%s/%s' % (service_url, path, api)
469+
# Perform Request
470+
if not stream:
471+
data = session.get(url, data=rqst, headers=headers, timeout=request_timeout)
472+
else:
473+
data = session.post(url, data=rqst, headers=headers, timeout=request_timeout, stream=True)
474+
data.raise_for_status()
475+
# Parse Response
476+
format = data.headers['Content-Type']
477+
if format == 'text/plain':
478+
rsps = __parse_json(data)
479+
elif format == 'application/json':
480+
rsps = __parse_json(data)
481+
elif format == 'application/octet-stream':
482+
rsps = __parse_native(data, callbacks)
483+
else:
484+
raise FatalError('unsupported content type: %s' % (format))
485+
# Success
486+
complete = True
487+
except requests.exceptions.SSLError as e:
488+
logger.error("Unable to verify SSL certificate: {} ...retrying request".format(e))
489+
except requests.ConnectionError as e:
490+
logger.error("Connection error to endpoint {} ...retrying request".format(url))
491+
except requests.Timeout as e:
492+
logger.error("Timed-out waiting for response from endpoint {} ...retrying request".format(url))
493+
except requests.exceptions.ChunkedEncodingError as e:
494+
logger.error("Unexpected termination of response from endpoint {} ...retrying request".format(url))
495+
except requests.HTTPError as e:
496+
if e.response.status_code == 503:
497+
raise TransientError("Server experiencing heavy load, stalling on request to {}".format(url))
498+
else:
499+
raise FatalError("HTTP error {} from endpoint {}".format(e.response.status_code, url))
500+
except:
501+
raise
502+
# Check Success
503+
if not complete:
504+
raise FatalError("Unable to complete request due to errors")
496505
# Return Response
497506
return rsps
498507

tests/test_arcticdem.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,10 @@ def test_sampler(self, domain, asset, organization):
3131
"maxi": 1,
3232
"samples": ["arcticdem-mosaic"] }
3333
gdf = icesat2.atl06p(parms, asset=asset, resources=[resource])
34+
assert len(gdf) == 964
35+
assert len(gdf.keys()) == 17
36+
assert gdf["rgt"][0] == 1160
37+
assert gdf["cycle"][0] == 2
38+
assert gdf['segment_id'].describe()["min"] == 405240
39+
assert gdf['segment_id'].describe()["max"] == 405915
40+
assert abs(gdf["arcticdem-mosaic-1980-01-06"].describe()["min"] - 655.14990234375) < 0.0001

tests/test_init.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Tests for sliderule-python connection errors when requests get sent back to back."""
2+
3+
import pytest
4+
import sliderule
5+
from sliderule import icesat2
6+
7+
@pytest.mark.network
8+
class TestInit:
9+
def test_loop_init(self, domain, organization):
10+
for _ in range(10):
11+
icesat2.init(domain, organization=organization)
12+
13+
def test_loop_versions(self, domain, organization):
14+
icesat2.init(domain, organization=organization)
15+
for _ in range(10):
16+
sliderule.source("version", {})

tests/test_parquet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Tests for sliderule-python arcticdem raster support."""
1+
"""Tests for sliderule-python parquet support."""
22

33
import pytest
44
from pathlib import Path

0 commit comments

Comments
 (0)