From 67f3a9c9ce2bfe85356554addcd9230463575581 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Tue, 27 Aug 2024 09:25:54 -0600 Subject: [PATCH 01/10] Make conditional exhaustive --- icepyx/core/APIformatting.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index 1c225d2e..44db61df 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -36,10 +36,6 @@ def _fmt_temporal(start, end, key): assert isinstance(start, dt.datetime) assert isinstance(end, dt.datetime) - assert key in [ - "time", - "temporal", - ], "An invalid time key was submitted for formatting." if key == "temporal": fmt_timerange = ( @@ -53,6 +49,8 @@ def _fmt_temporal(start, end, key): + "," + end.strftime("%Y-%m-%dT%H:%M:%S") ) + else: + raise RuntimeError("An invalid time key was submitted for formatting.") return {key: fmt_timerange} From f0c0d88d5097625097f18379f5f75508058deea8 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Thu, 29 Aug 2024 21:22:16 -0600 Subject: [PATCH 02/10] Annotate some of the Parameters class --- icepyx/core/APIformatting.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index 44db61df..6c329732 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -255,13 +255,15 @@ class Parameters(Generic[T]): on the type of query. Must be one of ['search','download'] """ + partype: T + _reqtype: Literal["search", "download"] | None fmted_keys = _FmtedKeysDescriptor() def __init__( self, partype: T, - values=None, - reqtype=None, + values: dict | None = None, + reqtype: Literal["search", "download"] | None = None, ): assert partype in [ "CMR", @@ -280,7 +282,7 @@ def __init__( self._fmted_keys = values if values is not None else {} @property - def poss_keys(self): + def poss_keys(self) -> dict[str, list[str]]: """ Returns a list of possible input keys for the given parameter object. Possible input keys depend on the parameter type (partype). @@ -340,7 +342,7 @@ def _get_possible_keys(self) -> dict[str, list[str]]: ], } - def _check_valid_keys(self): + def _check_valid_keys(self) -> None: """ Checks that any keys passed in with values are valid keys. """ @@ -356,7 +358,7 @@ def _check_valid_keys(self): ) # DevNote: can check_req_values and check_values be combined? - def check_req_values(self): + def check_req_values(self) -> bool: """ Check that all of the required keys have values, if the key was passed in with the values parameter. @@ -375,7 +377,7 @@ def check_req_values(self): else: return False - def check_values(self): + def check_values(self) -> bool: """ Check that the non-required keys have values, if the key was passed in with the values parameter. From 7c01510c89a867f257d251f0101e6c3fde6226d4 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Thu, 29 Aug 2024 21:24:39 -0600 Subject: [PATCH 03/10] Refactor poss_keys method --- icepyx/core/APIformatting.py | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index 6c329732..811b103e 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -288,25 +288,8 @@ def poss_keys(self) -> dict[str, list[str]]: Possible input keys depend on the parameter type (partype). """ - if not hasattr(self, "_poss_keys"): - self._get_possible_keys() - - return self._poss_keys - - # @property - # def wanted_keys(self): - # if not hasattr(_wanted): - # self._wanted = [] - - # return self._wanted - - def _get_possible_keys(self) -> dict[str, list[str]]: - """ - Use the parameter type to get a list of possible parameter keys. - """ - if self.partype == "CMR": - self._poss_keys = { + return { "spatial": ["bounding_box", "polygon"], "optional": [ "temporal", @@ -316,7 +299,7 @@ def _get_possible_keys(self) -> dict[str, list[str]]: ], } elif self.partype == "required": - self._poss_keys = { + return { "search": ["short_name", "version", "page_size"], "download": [ "short_name", @@ -331,7 +314,7 @@ def _get_possible_keys(self) -> dict[str, list[str]]: ], } elif self.partype == "subset": - self._poss_keys = { + return { "spatial": ["bbox", "Boundingshape"], "optional": [ "time", @@ -341,6 +324,15 @@ def _get_possible_keys(self) -> dict[str, list[str]]: "Coverage", ], } + else: + raise RuntimeError("Programmer error!") + + # @property + # def wanted_keys(self): + # if not hasattr(_wanted): + # self._wanted = [] + + # return self._wanted def _check_valid_keys(self) -> None: """ From 2948bea4fc7d8cc3ba29944f337f313b5a10e6f9 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Thu, 29 Aug 2024 21:25:53 -0600 Subject: [PATCH 04/10] Ignore some typechecker errors I don't know how to deal with right now --- icepyx/core/APIformatting.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index 811b103e..02a64cc9 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -229,7 +229,7 @@ def __get__( Returns the dictionary of formatted keys associated with the parameter object. """ - return instance._fmted_keys + return instance._fmted_keys # pyright: ignore[reportReturnType] # ---------------------------------------------------------------------- @@ -344,7 +344,7 @@ def _check_valid_keys(self) -> None: val_list = list({val for lis in self.poss_keys.values() for val in lis}) - for key in self.fmted_keys: + for key in self.fmted_keys: # pyright: ignore[reportAttributeAccessIssue] assert key in val_list, ( "An invalid key (" + key + ") was passed. Please remove it using `del`" ) @@ -359,11 +359,16 @@ def check_req_values(self) -> bool: assert ( self.partype == "required" ), "You cannot call this function for your parameter type" + + if not self._reqtype: + raise RuntimeError("Programmer error!") + reqkeys = self.poss_keys[self._reqtype] - if all(keys in self.fmted_keys for keys in reqkeys): + if all(keys in self.fmted_keys for keys in reqkeys): # pyright: ignore[reportAttributeAccessIssue] assert all( - self.fmted_keys.get(key, -9999) != -9999 for key in reqkeys + self.fmted_keys.get(key, -9999) != -9999 # pyright: ignore[reportAttributeAccessIssue] + for key in reqkeys ), "One of your formatted parameters is missing a value" return True else: @@ -383,7 +388,8 @@ def check_values(self) -> bool: # not the most robust check, but better than nothing... if any(keys in self._fmted_keys for keys in spatial_keys): assert any( - self.fmted_keys.get(key, -9999) != -9999 for key in spatial_keys + self.fmted_keys.get(key, -9999) != -9999 # pyright: ignore[reportAttributeAccessIssue] + for key in spatial_keys ), "One of your formatted parameters is missing a value" return True else: From 05d5abdc11fde2a05ea67fba20f12c2fc1a2b18f Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Thu, 29 Aug 2024 21:26:26 -0600 Subject: [PATCH 05/10] Add some typeguards --- icepyx/core/APIformatting.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index 02a64cc9..4d29b077 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -425,6 +425,9 @@ def build_params(self, **kwargs) -> None: self._check_valid_keys() if self.partype == "required": + if not self._reqtype: + raise RuntimeError("Programmer error!") + if self.check_req_values() and kwargs == {}: pass else: @@ -482,6 +485,7 @@ def build_params(self, **kwargs) -> None: if any(keys in self._fmted_keys for keys in spatial_keys): pass else: + k = None if self.partype == "CMR": k = kwargs["extent_type"] elif self.partype == "subset": @@ -490,6 +494,9 @@ def build_params(self, **kwargs) -> None: elif kwargs["extent_type"] == "polygon": k = "Boundingshape" + if not k: + raise RuntimeError("Programmer error!") + self._fmted_keys.update({k: kwargs["spatial_extent"]}) From ef5bdbe5b55f148a5e0716505a04b07d9f6b3887 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Thu, 29 Aug 2024 21:27:36 -0600 Subject: [PATCH 06/10] Start typechecking APIformatting module --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 564f5397..fb4907d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -140,7 +140,6 @@ exclude = [ # DevGoal: Remove all ignores ignore = [ "icepyx/quest/*", - "icepyx/core/APIformatting.py", "icepyx/core/auth.py", "icepyx/core/is2ref.py", "icepyx/core/read.py", From 3da94efb5a1528508ab983b78ce32812aaf3d4d2 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 4 Sep 2024 19:08:39 -0600 Subject: [PATCH 07/10] Use Python 3.9-compatible annotation style --- icepyx/core/APIformatting.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index 4d29b077..6d6c6edc 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -1,7 +1,7 @@ """Generate and format information for submitting to API (CMR and NSIDC).""" import datetime as dt -from typing import Any, Generic, Literal, TypeVar, Union, overload +from typing import Any, Generic, Literal, Optional, TypeVar, Union, overload from icepyx.core.types import ( CMRParams, @@ -256,14 +256,15 @@ class Parameters(Generic[T]): """ partype: T - _reqtype: Literal["search", "download"] | None + _reqtype: Optional[Literal["search", "download"]] fmted_keys = _FmtedKeysDescriptor() + # _fmted_keys: Union[CMRParams, EGISpecificRequiredParams, EGIParamsSubset] def __init__( self, partype: T, - values: dict | None = None, - reqtype: Literal["search", "download"] | None = None, + values: Optional[dict] = None, + reqtype: Optional[Literal["search", "download"]] = None, ): assert partype in [ "CMR", From 1b46d8586427bf7bd236e0556def458a849dcee7 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 24 Sep 2024 20:10:16 +0000 Subject: [PATCH 08/10] GitHub action UML generation auto-update --- .../documentation/classes_dev_uml.svg | 762 ++++++++++-------- .../documentation/classes_user_uml.svg | 560 ++++++++----- .../documentation/packages_user_uml.svg | 188 +++-- 3 files changed, 894 insertions(+), 616 deletions(-) diff --git a/doc/source/user_guide/documentation/classes_dev_uml.svg b/doc/source/user_guide/documentation/classes_dev_uml.svg index 9084ffd7..1c75cf0b 100644 --- a/doc/source/user_guide/documentation/classes_dev_uml.svg +++ b/doc/source/user_guide/documentation/classes_dev_uml.svg @@ -4,11 +4,11 @@ - - + + classes_dev_uml - + icepyx.quest.dataset_scripts.argo.Argo @@ -37,34 +37,34 @@ search_data(params, presRange, printURL): str - + icepyx.quest.dataset_scripts.dataset.DataSet - -DataSet - - -__init__ -(spatial_extent, date_range, start_time, end_time) -_fmt_coordinates -() -_fmt_timerange -() -_validate_inputs -() -download -() -save -(filepath) -search_data -() -visualize -() + +DataSet + + +__init__ +(spatial_extent, date_range, start_time, end_time) +_fmt_coordinates +() +_fmt_timerange +() +_validate_inputs +() +download +() +save +(filepath) +search_data +() +visualize +() - + icepyx.quest.dataset_scripts.argo.Argo->icepyx.quest.dataset_scripts.dataset.DataSet - - + + @@ -75,402 +75,528 @@ - + + +icepyx.core.types.CMRParamsWithBbox + +CMRParamsWithBbox + +bounding_box : str + + + + +icepyx.core.types.CMRParamsWithPolygon + +CMRParamsWithPolygon + +polygon : str + + + + + icepyx.core.exceptions.DeprecationError - -DeprecationError - - - + +DeprecationError + + + + + + +icepyx.core.types.EGIParamsSubsetBase + +EGIParamsSubsetBase + +Coverage : NotRequired[str] +format : NotRequired[str] +projection : NotRequired[str] +projection_parameters : NotRequired[str] +time : NotRequired[str] + + + + + +icepyx.core.types.EGIParamsSubsetBbox + +EGIParamsSubsetBbox + +bbox : NotRequired[str] + + + + + +icepyx.core.types.EGIParamsSubsetBbox->icepyx.core.types.EGIParamsSubsetBase + + + + + +icepyx.core.types.EGIParamsSubsetBoundingShape + +EGIParamsSubsetBoundingShape + +Boundingshape : NotRequired[str] + + + + + +icepyx.core.types.EGIParamsSubsetBoundingShape->icepyx.core.types.EGIParamsSubsetBase + + + + + +icepyx.core.types.EGIRequiredParamsBase + +EGIRequiredParamsBase + +page_num : int +page_size : int +short_name : Literal +version : str + + + + + +icepyx.core.types.EGIRequiredParamsDownload + +EGIRequiredParamsDownload + +client_string : Literal['icepyx'] +include_meta : Literal['Y', 'N'] +request_mode : Literal['sync', 'async', 'stream'] + + + + + +icepyx.core.types.EGIRequiredParamsDownload->icepyx.core.types.EGIRequiredParamsBase + + + + + +icepyx.core.types.EGIRequiredParamsSearch + +EGIRequiredParamsSearch + + + + + + +icepyx.core.types.EGIRequiredParamsSearch->icepyx.core.types.EGIRequiredParamsBase + + - + icepyx.core.auth.EarthdataAuthMixin - -EarthdataAuthMixin - -_auth : NoneType -_s3_initial_ts : NoneType, datetime -_s3login_credentials : NoneType -_session : NoneType -auth -s3login_credentials -session - -__init__(auth) -__str__() -earthdata_login(uid, email, s3token): None + +EarthdataAuthMixin + +_auth : NoneType +_s3_initial_ts : NoneType, datetime +_s3login_credentials : NoneType +_session : NoneType +auth +s3login_credentials +session + +__init__(auth) +__str__(): str +earthdata_login(uid, email, s3token): None - + icepyx.core.query.GenQuery - -GenQuery - -_spatial -_temporal -dates -end_time -spatial -spatial_extent -start_time -temporal - -__init__(spatial_extent, date_range, start_time, end_time) -__str__() + +GenQuery + +_spatial +_temporal +dates +end_time +spatial +spatial_extent +start_time +temporal + +__init__(spatial_extent, date_range, start_time, end_time) +__str__() - + icepyx.core.granules.Granules - -Granules - -avail : list -orderIDs : list - -__init__() -download(verbose, path, restart) -get_avail(CMRparams, reqparams, cloud) -place_order(CMRparams, reqparams, subsetparams, verbose, subset, geom_filepath) + +Granules + +avail : list +orderIDs : list + +__init__() +download(verbose, path, restart) +get_avail(CMRparams: CMRParams, reqparams: EGIRequiredParamsSearch, cloud: bool) +place_order(CMRparams: CMRParams, reqparams: EGIRequiredParamsDownload, subsetparams, verbose, subset, geom_filepath) icepyx.core.granules.Granules->icepyx.core.auth.EarthdataAuthMixin - - + + - + icepyx.core.query.Query - -Query - -CMRparams -_CMRparams -_about_product -_cust_options : dict -_cycles : list -_granules -_order_vars -_prod : NoneType, str -_readable_granule_name : list -_reqparams -_subsetparams : NoneType -_tracks : list -_version -cycles -dataset -granules -order_vars -product -product_version -reqparams -tracks - -__init__(product, spatial_extent, date_range, start_time, end_time, version, cycles, tracks, auth) -__str__() -avail_granules(ids, cycles, tracks, cloud) -download_granules(path, verbose, subset, restart) -latest_version() -order_granules(verbose, subset, email) -product_all_info() -product_summary_info() -show_custom_options(dictview) -subsetparams() -visualize_elevation() -visualize_spatial_extent() + +Query + +CMRparams +_CMRparams +_about_product +_cust_options : dict +_cycles : list +_granules +_order_vars +_prod : NoneType, str +_readable_granule_name : list +_reqparams +_subsetparams : Optional[apifmt.SubsetParameters] +_tracks : list +_version +cycles +dataset +granules +order_vars +product +product_version +reqparams +tracks + +__init__(product, spatial_extent, date_range, start_time, end_time, version, cycles, tracks, auth) +__str__() +avail_granules(ids, cycles, tracks, cloud) +download_granules(path, verbose, subset, restart) +latest_version() +order_granules(verbose, subset, email) +product_all_info() +product_summary_info() +show_custom_options(dictview) +subsetparams(): Union[EGIParamsSubset, dict[Never, Never]] +visualize_elevation() +visualize_spatial_extent() - + icepyx.core.granules.Granules->icepyx.core.query.Query - - -_granules + + +_granules - + icepyx.core.icesat2data.Icesat2Data - -Icesat2Data - - -__init__() + +Icesat2Data + + +__init__() - + icepyx.core.exceptions.NsidcQueryError - -NsidcQueryError - -errmsg -msgtxt : str - -__init__(errmsg, msgtxt) -__str__() + +NsidcQueryError + +errmsg +msgtxt : str + +__init__(errmsg, msgtxt) +__str__() - + icepyx.core.exceptions.QueryError - -QueryError - - - + +QueryError + + + icepyx.core.exceptions.NsidcQueryError->icepyx.core.exceptions.QueryError - - + + - + icepyx.core.APIformatting.Parameters - -Parameters - -_fmted_keys : NoneType, dict -_poss_keys : dict -_reqtype : NoneType, str -fmted_keys -partype -poss_keys - -__init__(partype, values, reqtype) -_check_valid_keys() -_get_possible_keys() -build_params() -check_req_values() -check_values() + +Parameters + +_fmted_keys : NoneType, dict +_reqtype : Optional[Literal['search', 'download']] +fmted_keys +partype : T +poss_keys + +__init__(partype: T, values: Optional[dict], reqtype: Optional[Literal['search', 'download']]) +_check_valid_keys(): None +build_params(): None +check_req_values(): bool +check_values(): bool - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_CMRparams + + +_CMRparams - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_reqparams + + +_reqparams - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_subsetparams + + +_subsetparams - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_subsetparams + + +_subsetparams icepyx.core.query.Query->icepyx.core.auth.EarthdataAuthMixin - - + + icepyx.core.query.Query->icepyx.core.query.GenQuery - - + + - + icepyx.quest.quest.Quest - -Quest - -datasets : dict - -__init__(spatial_extent, date_range, start_time, end_time, proj) -__str__() -add_argo(params, presRange): None -add_icesat2(product, start_time, end_time, version, cycles, tracks, files): None -download_all(path) -save_all(path) -search_all() + +Quest + +datasets : dict + +__init__(spatial_extent, date_range, start_time, end_time, proj) +__str__() +add_argo(params, presRange): None +add_icesat2(product, start_time, end_time, version, cycles, tracks, files): None +download_all(path) +save_all(path) +search_all() - + icepyx.quest.quest.Quest->icepyx.core.query.GenQuery - - + + - + icepyx.core.read.Read - -Read - -_filelist -_out_obj : Dataset -_product -_read_vars -filelist -is_s3 -product -vars - -__init__(data_source, glob_kwargs, out_obj_type, product, filename_pattern, catalog) -_add_vars_to_ds(is2ds, ds, grp_path, wanted_groups_tiered, wanted_dict) -_build_dataset_template(file) -_build_single_file_dataset(file, groups_list) -_combine_nested_vars(is2ds, ds, grp_path, wanted_dict) -_read_single_grp(file, grp_path) -load() + +Read + +_filelist +_out_obj : Dataset +_product +_read_vars +filelist +is_s3 +product +vars + +__init__(data_source, glob_kwargs, out_obj_type, product, filename_pattern, catalog) +_add_vars_to_ds(is2ds, ds, grp_path, wanted_groups_tiered, wanted_dict) +_build_dataset_template(file) +_build_single_file_dataset(file, groups_list) +_combine_nested_vars(is2ds, ds, grp_path, wanted_dict) +_read_single_grp(file, grp_path) +load() icepyx.core.read.Read->icepyx.core.auth.EarthdataAuthMixin - - + + - + icepyx.core.spatial.Spatial - -Spatial - -_ext_type : str -_gdf_spat : GeoDataFrame -_geom_file : NoneType -_spatial_ext -_xdateln -extent -extent_as_gdf -extent_file -extent_type - -__init__(spatial_extent) -__str__() -fmt_for_CMR() -fmt_for_EGI() + +Spatial + +_ext_type : str +_gdf_spat : GeoDataFrame +_geom_file : NoneType +_spatial_ext +_xdateln +extent +extent_as_gdf +extent_file +extent_type + +__init__(spatial_extent) +__str__() +fmt_for_CMR() +fmt_for_EGI() - + icepyx.core.spatial.Spatial->icepyx.core.query.GenQuery - - -_spatial + + +_spatial - + icepyx.core.spatial.Spatial->icepyx.core.query.GenQuery - - -_spatial + + +_spatial - + icepyx.core.temporal.Temporal - -Temporal - -_end : datetime -_start : datetime -end -start - -__init__(date_range, start_time, end_time) -__str__() + +Temporal + +_end : datetime +_start : datetime +end +start + +__init__(date_range, start_time, end_time) +__str__() - + icepyx.core.temporal.Temporal->icepyx.core.query.GenQuery - - -_temporal + + +_temporal - + icepyx.core.variables.Variables - -Variables - -_avail : NoneType, list -_path : NoneType -_product : NoneType, str -_version -path -product -version -wanted : NoneType, dict - -__init__(vartype, path, product, version, avail, wanted, auth) -_check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) -_get_combined_list(beam_list, keyword_list) -_get_sum_varlist(var_list, all_vars, defaults) -_iter_paths(sum_varlist, req_vars, vgrp, beam_list, keyword_list) -_iter_vars(sum_varlist, req_vars, vgrp) -append(defaults, var_list, beam_list, keyword_list) -avail(options, internal) -parse_var_list(varlist, tiered, tiered_vars) -remove(all, var_list, beam_list, keyword_list) + +Variables + +_avail : NoneType, list +_path : NoneType +_product : NoneType, str +_version +path +product +version +wanted : NoneType, dict + +__init__(vartype, path, product, version, avail, wanted, auth) +_check_valid_lists(vgrp, allpaths, var_list, beam_list, keyword_list) +_get_combined_list(beam_list, keyword_list) +_get_sum_varlist(var_list, all_vars, defaults) +_iter_paths(sum_varlist, req_vars, vgrp, beam_list, keyword_list) +_iter_vars(sum_varlist, req_vars, vgrp) +append(defaults, var_list, beam_list, keyword_list) +avail(options, internal) +parse_var_list(varlist, tiered, tiered_vars) +remove(all, var_list, beam_list, keyword_list) - + icepyx.core.variables.Variables->icepyx.core.auth.EarthdataAuthMixin - - + + - + icepyx.core.variables.Variables->icepyx.core.query.Query - - -_order_vars + + +_order_vars - + icepyx.core.variables.Variables->icepyx.core.query.Query - - -_order_vars + + +_order_vars - + icepyx.core.variables.Variables->icepyx.core.read.Read - - -_read_vars + + +_read_vars - + icepyx.core.variables.Variables->icepyx.core.read.Read - - -_read_vars + + +_read_vars - + icepyx.core.visualization.Visualize - -Visualize - -bbox : list -cycles : NoneType -date_range : NoneType -product : NoneType, str -tracks : NoneType - -__init__(query_obj, product, spatial_extent, date_range, cycles, tracks) -generate_OA_parameters(): list -grid_bbox(binsize): list -make_request(base_url, payload) -parallel_request_OA(): da.array -query_icesat2_filelist(): tuple -request_OA_data(paras): da.array -viz_elevation(): (hv.DynamicMap, hv.Layout) + +Visualize + +bbox : list +cycles : NoneType +date_range : NoneType +product : NoneType, str +tracks : NoneType + +__init__(query_obj, product, spatial_extent, date_range, cycles, tracks) +generate_OA_parameters(): list +grid_bbox(binsize): list +make_request(base_url, payload) +parallel_request_OA(): da.array +query_icesat2_filelist(): tuple +request_OA_data(paras): da.array +viz_elevation(): tuple[hv.DynamicMap, hv.Layout] + + + +icepyx.core.APIformatting._FmtedKeysDescriptor + +_FmtedKeysDescriptor + + +__get__(instance: 'Parameters[Literal["CMR"]]', owner: Any): CMRParams + + + +icepyx.core.APIformatting._FmtedKeysDescriptor->icepyx.core.APIformatting.Parameters + + +fmted_keys diff --git a/doc/source/user_guide/documentation/classes_user_uml.svg b/doc/source/user_guide/documentation/classes_user_uml.svg index 8b127359..b4d2a024 100644 --- a/doc/source/user_guide/documentation/classes_user_uml.svg +++ b/doc/source/user_guide/documentation/classes_user_uml.svg @@ -4,11 +4,11 @@ - - + + classes_user_uml - + icepyx.core.auth.AuthenticationError @@ -18,318 +18,446 @@ - + +icepyx.core.types.CMRParamsWithBbox + +CMRParamsWithBbox + +bounding_box : str + + + + + +icepyx.core.types.CMRParamsWithPolygon + +CMRParamsWithPolygon + +polygon : str + + + + + icepyx.core.exceptions.DeprecationError - -DeprecationError - - - + +DeprecationError + + + + + + +icepyx.core.types.EGIParamsSubsetBase + +EGIParamsSubsetBase + +Coverage : NotRequired[str] +format : NotRequired[str] +projection : NotRequired[str] +projection_parameters : NotRequired[str] +time : NotRequired[str] + + + + + +icepyx.core.types.EGIParamsSubsetBbox + +EGIParamsSubsetBbox + +bbox : NotRequired[str] + + + + + +icepyx.core.types.EGIParamsSubsetBbox->icepyx.core.types.EGIParamsSubsetBase + + + + + +icepyx.core.types.EGIParamsSubsetBoundingShape + +EGIParamsSubsetBoundingShape + +Boundingshape : NotRequired[str] + + + + + +icepyx.core.types.EGIParamsSubsetBoundingShape->icepyx.core.types.EGIParamsSubsetBase + + + + + +icepyx.core.types.EGIRequiredParamsBase + +EGIRequiredParamsBase + +page_num : int +page_size : int +short_name : Literal +version : str + + + + + +icepyx.core.types.EGIRequiredParamsDownload + +EGIRequiredParamsDownload + +client_string : Literal['icepyx'] +include_meta : Literal['Y', 'N'] +request_mode : Literal['sync', 'async', 'stream'] + + + + + +icepyx.core.types.EGIRequiredParamsDownload->icepyx.core.types.EGIRequiredParamsBase + + + + + +icepyx.core.types.EGIRequiredParamsSearch + +EGIRequiredParamsSearch + + + + + + +icepyx.core.types.EGIRequiredParamsSearch->icepyx.core.types.EGIRequiredParamsBase + + - + icepyx.core.auth.EarthdataAuthMixin - -EarthdataAuthMixin - -auth -s3login_credentials -session - -earthdata_login(uid, email, s3token): None + +EarthdataAuthMixin + +auth +s3login_credentials +session + +earthdata_login(uid, email, s3token): None - + icepyx.core.query.GenQuery - -GenQuery - -dates -end_time -spatial -spatial_extent -start_time -temporal - - + +GenQuery + +dates +end_time +spatial +spatial_extent +start_time +temporal + + - + icepyx.core.granules.Granules - -Granules - -avail : list -orderIDs : list - -download(verbose, path, restart) -get_avail(CMRparams, reqparams, cloud) -place_order(CMRparams, reqparams, subsetparams, verbose, subset, geom_filepath) + +Granules + +avail : list +orderIDs : list + +download(verbose, path, restart) +get_avail(CMRparams: CMRParams, reqparams: EGIRequiredParamsSearch, cloud: bool) +place_order(CMRparams: CMRParams, reqparams: EGIRequiredParamsDownload, subsetparams, verbose, subset, geom_filepath) icepyx.core.granules.Granules->icepyx.core.auth.EarthdataAuthMixin - - + + - + icepyx.core.query.Query - -Query - -CMRparams -cycles -dataset -granules -order_vars -product -product_version -reqparams -tracks - -avail_granules(ids, cycles, tracks, cloud) -download_granules(path, verbose, subset, restart) -latest_version() -order_granules(verbose, subset, email) -product_all_info() -product_summary_info() -show_custom_options(dictview) -subsetparams() -visualize_elevation() -visualize_spatial_extent() + +Query + +CMRparams +cycles +dataset +granules +order_vars +product +product_version +reqparams +tracks + +avail_granules(ids, cycles, tracks, cloud) +download_granules(path, verbose, subset, restart) +latest_version() +order_granules(verbose, subset, email) +product_all_info() +product_summary_info() +show_custom_options(dictview) +subsetparams(): Union[EGIParamsSubset, dict[Never, Never]] +visualize_elevation() +visualize_spatial_extent() - + icepyx.core.granules.Granules->icepyx.core.query.Query - - -_granules + + +_granules - + icepyx.core.icesat2data.Icesat2Data - -Icesat2Data - - - + +Icesat2Data + + + - + icepyx.core.exceptions.NsidcQueryError - -NsidcQueryError - -errmsg -msgtxt : str - - + +NsidcQueryError + +errmsg +msgtxt : str + + - + icepyx.core.exceptions.QueryError - -QueryError - - - + +QueryError + + + icepyx.core.exceptions.NsidcQueryError->icepyx.core.exceptions.QueryError - - + + - + icepyx.core.APIformatting.Parameters - -Parameters - -fmted_keys -partype -poss_keys - -build_params() -check_req_values() -check_values() + +Parameters + +fmted_keys +partype : T +poss_keys + +build_params(): None +check_req_values(): bool +check_values(): bool - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_CMRparams + + +_CMRparams - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_reqparams + + +_reqparams - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_subsetparams + + +_subsetparams - + icepyx.core.APIformatting.Parameters->icepyx.core.query.Query - - -_subsetparams + + +_subsetparams icepyx.core.query.Query->icepyx.core.auth.EarthdataAuthMixin - - + + icepyx.core.query.Query->icepyx.core.query.GenQuery - - + + - + icepyx.core.read.Read - -Read - -filelist -is_s3 -product -vars - -load() + +Read + +filelist +is_s3 +product +vars + +load() icepyx.core.read.Read->icepyx.core.auth.EarthdataAuthMixin - - + + - + icepyx.core.spatial.Spatial - -Spatial - -extent -extent_as_gdf -extent_file -extent_type - -fmt_for_CMR() -fmt_for_EGI() + +Spatial + +extent +extent_as_gdf +extent_file +extent_type + +fmt_for_CMR() +fmt_for_EGI() - + icepyx.core.spatial.Spatial->icepyx.core.query.GenQuery - - -_spatial + + +_spatial - + icepyx.core.spatial.Spatial->icepyx.core.query.GenQuery - - -_spatial + + +_spatial - + icepyx.core.temporal.Temporal - -Temporal - -end -start - - + +Temporal + +end +start + + - + icepyx.core.temporal.Temporal->icepyx.core.query.GenQuery - - -_temporal + + +_temporal - + icepyx.core.variables.Variables - -Variables - -path -product -version -wanted : NoneType, dict - -append(defaults, var_list, beam_list, keyword_list) -avail(options, internal) -parse_var_list(varlist, tiered, tiered_vars) -remove(all, var_list, beam_list, keyword_list) + +Variables + +path +product +version +wanted : NoneType, dict + +append(defaults, var_list, beam_list, keyword_list) +avail(options, internal) +parse_var_list(varlist, tiered, tiered_vars) +remove(all, var_list, beam_list, keyword_list) - + icepyx.core.variables.Variables->icepyx.core.auth.EarthdataAuthMixin - - + + - + icepyx.core.variables.Variables->icepyx.core.query.Query - - -_order_vars + + +_order_vars - + icepyx.core.variables.Variables->icepyx.core.query.Query - - -_order_vars + + +_order_vars - + icepyx.core.variables.Variables->icepyx.core.read.Read - - -_read_vars + + +_read_vars - + icepyx.core.variables.Variables->icepyx.core.read.Read - - -_read_vars + + +_read_vars - + icepyx.core.visualization.Visualize - -Visualize - -bbox : list -cycles : NoneType -date_range : NoneType -product : NoneType, str -tracks : NoneType - -generate_OA_parameters(): list -grid_bbox(binsize): list -make_request(base_url, payload) -parallel_request_OA(): da.array -query_icesat2_filelist(): tuple -request_OA_data(paras): da.array -viz_elevation(): (hv.DynamicMap, hv.Layout) + +Visualize + +bbox : list +cycles : NoneType +date_range : NoneType +product : NoneType, str +tracks : NoneType + +generate_OA_parameters(): list +grid_bbox(binsize): list +make_request(base_url, payload) +parallel_request_OA(): da.array +query_icesat2_filelist(): tuple +request_OA_data(paras): da.array +viz_elevation(): tuple[hv.DynamicMap, hv.Layout] + + + +icepyx.core.APIformatting._FmtedKeysDescriptor + +_FmtedKeysDescriptor + + + + + + +icepyx.core.APIformatting._FmtedKeysDescriptor->icepyx.core.APIformatting.Parameters + + +fmted_keys diff --git a/doc/source/user_guide/documentation/packages_user_uml.svg b/doc/source/user_guide/documentation/packages_user_uml.svg index 2cfe26a6..9d29c40d 100644 --- a/doc/source/user_guide/documentation/packages_user_uml.svg +++ b/doc/source/user_guide/documentation/packages_user_uml.svg @@ -4,190 +4,214 @@ - + packages_user_uml - + icepyx.core - -icepyx.core + +icepyx.core icepyx.core.APIformatting - -icepyx.core.APIformatting + +icepyx.core.APIformatting + + + +icepyx.core.types + +icepyx.core.types + + + +icepyx.core.APIformatting->icepyx.core.types + + icepyx.core.auth - -icepyx.core.auth + +icepyx.core.auth icepyx.core.exceptions - -icepyx.core.exceptions + +icepyx.core.exceptions - + icepyx.core.auth->icepyx.core.exceptions - - + + icepyx.core.granules - -icepyx.core.granules + +icepyx.core.granules - + icepyx.core.granules->icepyx.core.auth - - + + + + + +icepyx.core.granules->icepyx.core.types + + - + icepyx.core.urls - -icepyx.core.urls + +icepyx.core.urls - + icepyx.core.granules->icepyx.core.urls - - + + icepyx.core.icesat2data - -icepyx.core.icesat2data + +icepyx.core.icesat2data - + icepyx.core.icesat2data->icepyx.core.exceptions - - + + icepyx.core.is2ref - -icepyx.core.is2ref + +icepyx.core.is2ref - + icepyx.core.is2ref->icepyx.core.urls - - + + icepyx.core.query - -icepyx.core.query + +icepyx.core.query - + icepyx.core.query->icepyx.core.auth - - + + - + icepyx.core.query->icepyx.core.exceptions - - + + - + icepyx.core.query->icepyx.core.granules - - + + + + + +icepyx.core.query->icepyx.core.types + + - + icepyx.core.variables - -icepyx.core.variables + +icepyx.core.variables - + icepyx.core.query->icepyx.core.variables - - + + - + icepyx.core.visualization - -icepyx.core.visualization + +icepyx.core.visualization - + icepyx.core.query->icepyx.core.visualization - - + + icepyx.core.read - -icepyx.core.read + +icepyx.core.read - + icepyx.core.read->icepyx.core.auth - - + + - + icepyx.core.read->icepyx.core.exceptions - - + + - + icepyx.core.read->icepyx.core.variables - - + + icepyx.core.spatial - -icepyx.core.spatial + +icepyx.core.spatial icepyx.core.temporal - -icepyx.core.temporal + +icepyx.core.temporal - + icepyx.core.validate_inputs - -icepyx.core.validate_inputs + +icepyx.core.validate_inputs - + icepyx.core.variables->icepyx.core.auth - - + + - + icepyx.core.variables->icepyx.core.exceptions - - + + From ea4a0c52db0423258a6c6f4dfb22a53c81f9bddd Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 25 Sep 2024 16:16:17 -0600 Subject: [PATCH 09/10] Replace generic exceptions with new custom ones --- icepyx/core/APIformatting.py | 9 +++++---- icepyx/core/exceptions.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index 6d6c6edc..a70b66e0 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -3,6 +3,7 @@ import datetime as dt from typing import Any, Generic, Literal, Optional, TypeVar, Union, overload +from icepyx.core.exceptions import ExhaustiveTypeGuardException, TypeGuardException from icepyx.core.types import ( CMRParams, EGIParamsSubset, @@ -326,7 +327,7 @@ def poss_keys(self) -> dict[str, list[str]]: ], } else: - raise RuntimeError("Programmer error!") + raise ExhaustiveTypeGuardException # @property # def wanted_keys(self): @@ -362,7 +363,7 @@ def check_req_values(self) -> bool: ), "You cannot call this function for your parameter type" if not self._reqtype: - raise RuntimeError("Programmer error!") + raise TypeGuardException reqkeys = self.poss_keys[self._reqtype] @@ -427,7 +428,7 @@ def build_params(self, **kwargs) -> None: if self.partype == "required": if not self._reqtype: - raise RuntimeError("Programmer error!") + raise TypeGuardException if self.check_req_values() and kwargs == {}: pass @@ -496,7 +497,7 @@ def build_params(self, **kwargs) -> None: k = "Boundingshape" if not k: - raise RuntimeError("Programmer error!") + raise TypeGuardException self._fmted_keys.update({k: kwargs["spatial_extent"]}) diff --git a/icepyx/core/exceptions.py b/icepyx/core/exceptions.py index 94bbea76..085fed8c 100644 --- a/icepyx/core/exceptions.py +++ b/icepyx/core/exceptions.py @@ -1,3 +1,10 @@ +ISSUE_REPORTING_INSTRUCTIONS = ( + "If you are a user seeing this message, the developers of this software have made a" + " mistake! Please report the full error traceback in the icepyx GitHub repository:" + " " +) + + class DeprecationError(Exception): """ Class raised for use of functionality that is no longer supported by icepyx. @@ -24,3 +31,25 @@ def __init__( def __str__(self): return f"{self.msgtxt}: {self.errmsg}" + + +class TypeGuardException(Exception): + """ + Should never be raised at runtime. + + Used in cases where a runtime check is not desired, but we want to add a "type guard" + (https://github.com/microsoft/pyright/blob/main/docs/type-concepts-advanced.md#type-guards) + to give the type checker more information. + """ + + def __str__(self): + return ISSUE_REPORTING_INSTRUCTIONS + + +class ExhaustiveTypeGuardException(TypeGuardException): + """ + Should never be raised at runtime. + + Used exclusively in cases where the typechecker needs a typeguard to tell it that a + check is exhaustive. + """ From b90a10e95ecb7694564fab50a56c854433bc8e5a Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Wed, 25 Sep 2024 16:18:28 -0600 Subject: [PATCH 10/10] Use a more specific error type than RuntimeError --- icepyx/core/APIformatting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icepyx/core/APIformatting.py b/icepyx/core/APIformatting.py index a70b66e0..7ef1e7e7 100644 --- a/icepyx/core/APIformatting.py +++ b/icepyx/core/APIformatting.py @@ -51,7 +51,7 @@ def _fmt_temporal(start, end, key): + end.strftime("%Y-%m-%dT%H:%M:%S") ) else: - raise RuntimeError("An invalid time key was submitted for formatting.") + raise ValueError("An invalid time key was submitted for formatting.") return {key: fmt_timerange}