From 5a0f1eea9967e6a39f86b1e888529fe4b8bccf1f Mon Sep 17 00:00:00 2001 From: martin-springer Date: Wed, 4 Feb 2026 13:08:09 -0500 Subject: [PATCH 1/4] Add stacklevel to warnings.warn() calls Specify stacklevel parameter in all warnings.warn() calls to ensure warning messages point to user code rather than rdtools internals: - stacklevel=2: For public functions/methods called directly by users - stacklevel=3: For private helper methods called by public methods Files updated: - analysis_chains.py: 6 warnings - filtering.py: 2 warnings - soiling.py: 4 warnings - plotting.py: 3 warnings - normalization.py: 1 warning - availability.py: 1 warning - clearsky_temperature.py: 2 warnings --- docs/sphinx/source/changelog/pending.rst | 4 ++++ rdtools/analysis_chains.py | 18 ++++++++++++------ rdtools/availability.py | 2 +- rdtools/clearsky_temperature.py | 6 ++++-- rdtools/filtering.py | 6 ++++-- rdtools/normalization.py | 2 +- rdtools/plotting.py | 6 +++--- rdtools/soiling.py | 11 +++++++---- 8 files changed, 36 insertions(+), 19 deletions(-) diff --git a/docs/sphinx/source/changelog/pending.rst b/docs/sphinx/source/changelog/pending.rst index 41050e82..b9fb2b9f 100644 --- a/docs/sphinx/source/changelog/pending.rst +++ b/docs/sphinx/source/changelog/pending.rst @@ -64,6 +64,10 @@ Enhancements * Added frequency validation for ``clip_filter`` in ``TrendAnalysis._filter()`` that raises a ``ValueError`` if the time series has a median time step greater than 60 minutes, as clipping detection requires higher resolution data. +* Added ``stacklevel`` parameter to all ``warnings.warn()`` calls so that warning + messages point to user code rather than rdtools internals. Affected modules: + ``analysis_chains``, ``filtering``, ``soiling``, ``plotting``, ``normalization``, + ``availability``, and ``clearsky_temperature``. Warnings diff --git a/rdtools/analysis_chains.py b/rdtools/analysis_chains.py index 77c5c323..d2c58022 100644 --- a/rdtools/analysis_chains.py +++ b/rdtools/analysis_chains.py @@ -475,7 +475,8 @@ def _pvwatts_norm(self, poa_global, temperature_cell): if self.gamma_pdc is None: warnings.warn( "Temperature coefficient not passed in to TrendAnalysis. " - "No temperature correction will be conducted." + "No temperature correction will be conducted.", + stacklevel=3, ) pvwatts_kws = { "poa_global": poa_global, @@ -619,7 +620,8 @@ def _call_clearsky_filter(filter_string): if ad_hoc_filter.isnull().any(): warnings.warn( - "ad_hoc_filter contains NaN values; setting to False (excluding)" + "ad_hoc_filter contains NaN values; setting to False (excluding)", + stacklevel=3, ) ad_hoc_filter.loc[ad_hoc_filter.isnull()] = False @@ -627,7 +629,8 @@ def _call_clearsky_filter(filter_string): warnings.warn( "ad_hoc_filter index does not match index of other filters; missing " "values will be set to True (kept). Align the index with the index " - "of the filter_components attribute to prevent this warning" + "of the filter_components attribute to prevent this warning", + stacklevel=3, ) ad_hoc_filter = ad_hoc_filter.reindex(filter_components.index) ad_hoc_filter.loc[ad_hoc_filter.isnull()] = True @@ -709,7 +712,8 @@ def _aggregated_filter(self, aggregated, case): if ad_hoc_filter_aggregated.isnull().any(): warnings.warn( - "aggregated ad_hoc_filter contains NaN values; setting to False (excluding)" + "aggregated ad_hoc_filter contains NaN values; setting to False (excluding)", + stacklevel=3, ) ad_hoc_filter_aggregated.loc[ad_hoc_filter_aggregated.isnull()] = False @@ -720,7 +724,8 @@ def _aggregated_filter(self, aggregated, case): "Aggregated ad_hoc_filter index does not match index of other " "filters; missing values will be set to True (kept). " "Align the index with the index of the " - "filter_components_aggregated attribute to prevent this warning" + "filter_components_aggregated attribute to prevent this warning", + stacklevel=3, ) ad_hoc_filter_aggregated = ad_hoc_filter_aggregated.reindex( filter_components_aggregated.index @@ -963,7 +968,8 @@ def _clearsky_preprocess(self): """Clear-sky analysis is performed but `power_expected` was passed in by user. In this case, the power normalization is not tied to the modeled clear-sky irradiance and the clear-sky workflow may provide similar results to - the sensor workflow.""" + the sensor workflow.""", + stacklevel=2, ) self._filter(cs_normalized, "clearsky") cs_aggregated, cs_aggregated_insolation = self._aggregate( diff --git a/rdtools/availability.py b/rdtools/availability.py index 06d57780..e666eedf 100644 --- a/rdtools/availability.py +++ b/rdtools/availability.py @@ -543,7 +543,7 @@ def _combine_losses(self, rollup_period="ME"): 'levels. This is unexpected and could indicate a problem with ' 'the input time series data.' ) - warnings.warn(msg, UserWarning) + warnings.warn(msg, UserWarning, stacklevel=3) self.loss_total = self.loss_system + self.loss_subsystem diff --git a/rdtools/clearsky_temperature.py b/rdtools/clearsky_temperature.py index ebe617f2..d87cae03 100644 --- a/rdtools/clearsky_temperature.py +++ b/rdtools/clearsky_temperature.py @@ -56,7 +56,8 @@ def get_clearsky_tamb(times, latitude, longitude, window_size=40, # workaround from https://github.com/pandas-dev/pandas/issues/55794 freq_actual = pd.infer_freq(times[:10]) warnings.warn("Input 'times' has no frequency attribute. " - "Inferring frequency from first 10 timestamps.") + "Inferring frequency from first 10 timestamps.", + stacklevel=2) else: freq_actual = times.freq @@ -121,7 +122,8 @@ def solar_noon_offset(utc_offset): df['solar_noon_offset'].values) if df['Clear Sky Temperature (C)'].isna().any(): warnings.warn("Clear Sky Temperature includes NaNs, " - "possibly invalid Lat/Lon coordinates.", UserWarning) + "possibly invalid Lat/Lon coordinates.", UserWarning, + stacklevel=2) return df['Clear Sky Temperature (C)'] diff --git a/rdtools/filtering.py b/rdtools/filtering.py index ecc5162d..3b2569b2 100644 --- a/rdtools/filtering.py +++ b/rdtools/filtering.py @@ -391,7 +391,8 @@ def _format_clipping_time_series(power_ac, mounting_type): warnings.warn( "Function expects timestamps in local time. " "For best results pass a time-zone-localized " - "time series localized to the correct local time zone." + "time series localized to the correct local time zone.", + stacklevel=3, ) # Check the other input variables to ensure that they are the # correct format @@ -448,7 +449,8 @@ def _check_data_sampling_frequency(power_ac): "Variable sampling frequency across time series. " "Less than 95% of the time series is sampled at the " "same interval. This function was not tested " - "on variable frequency data--use at your own risk!" + "on variable frequency data--use at your own risk!", + stacklevel=3, ) return diff --git a/rdtools/normalization.py b/rdtools/normalization.py index f206dd90..99fa3cef 100644 --- a/rdtools/normalization.py +++ b/rdtools/normalization.py @@ -594,7 +594,7 @@ def _interpolate_series(time_series, target_index, max_timedelta=None, warnings.warn("Fraction of excluded data " f"({100*fraction_excluded:0.02f}%) " "exceeded threshold", - UserWarning) + UserWarning, stacklevel=3) # put data on index that includes both original and target indices # Convert target to UTC for consistent epoch calculation diff --git a/rdtools/plotting.py b/rdtools/plotting.py index 93a07bac..d2081ac3 100644 --- a/rdtools/plotting.py +++ b/rdtools/plotting.py @@ -173,7 +173,7 @@ def soiling_monte_carlo_plot(soiling_info, normalized_yield, point_alpha=0.5, warnings.warn( 'The soiling module is currently experimental. The API, results, ' 'and default behaviors may change in future releases (including MINOR ' - 'and PATCH releases) as the code matures.' + 'and PATCH releases) as the code matures.', stacklevel=2 ) fig, ax = plt.subplots() @@ -233,7 +233,7 @@ def soiling_interval_plot(soiling_info, normalized_yield, point_alpha=0.5, warnings.warn( 'The soiling module is currently experimental. The API, results, ' 'and default behaviors may change in future releases (including MINOR ' - 'and PATCH releases) as the code matures.' + 'and PATCH releases) as the code matures.', stacklevel=2 ) sratio = soiling_info['soiling_ratio_perfect_clean'] @@ -273,7 +273,7 @@ def soiling_rate_histogram(soiling_info, bins=None): warnings.warn( 'The soiling module is currently experimental. The API, results, ' 'and default behaviors may change in future releases (including MINOR ' - 'and PATCH releases) as the code matures.' + 'and PATCH releases) as the code matures.', stacklevel=2 ) soiling_summary = soiling_info['soiling_interval_summary'] diff --git a/rdtools/soiling.py b/rdtools/soiling.py index e8524ff4..ef7b2b05 100644 --- a/rdtools/soiling.py +++ b/rdtools/soiling.py @@ -27,7 +27,7 @@ warnings.warn( 'The soiling module is currently experimental. The API, results, ' 'and default behaviors may change in future releases (including MINOR ' - 'and PATCH releases) as the code matures.' + 'and PATCH releases) as the code matures.', stacklevel=2 ) @@ -123,7 +123,8 @@ def _calc_daily_df(self, day_scale=13, clean_threshold='infer', warnings.warn('An even value of day_scale was passed. An odd value is ' 'recommended, otherwise, consecutive days may be erroneously ' 'flagged as cleaning events. ' - 'See https://github.com/NREL/rdtools/issues/189') + 'See https://github.com/NREL/rdtools/issues/189', + stacklevel=2) df = self.pm.to_frame() df.columns = ['pi'] @@ -382,7 +383,8 @@ def _calc_monte(self, monte, method='half_norm_clean'): 'validity criteria such as increasing "max_relative_slope_error" ' 'and/or "max_negative_step" and/or decreasing "min_interval_length".' ' Alternatively, consider using method="perfect_clean". For more' - ' info see https://github.com/NREL/rdtools/issues/272' + ' info see https://github.com/NREL/rdtools/issues/272', + stacklevel=2 ) monte_losses = [] random_profiles = [] @@ -906,7 +908,8 @@ def annual_soiling_ratios(stochastic_soiling_profiles, 'The indexes of stochastic_soiling_profiles are not entirely ' 'contained within the index of insolation_daily. Every day in ' 'stochastic_soiling_profiles should be represented in ' - 'insolation_daily. This may cause erroneous results.') + 'insolation_daily. This may cause erroneous results.', + stacklevel=2) insolation_daily = insolation_daily.reindex(all_profiles.index) From 2b1ac529413299aef57fd9d7e38ae62615cf686d Mon Sep 17 00:00:00 2001 From: martin-springer Date: Wed, 4 Feb 2026 14:02:59 -0500 Subject: [PATCH 2/4] sanitize line numbers in warning messages --- docs/nbval_sanitization_rules.cfg | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/nbval_sanitization_rules.cfg b/docs/nbval_sanitization_rules.cfg index 7a8b786a..62c6ac31 100644 --- a/docs/nbval_sanitization_rules.cfg +++ b/docs/nbval_sanitization_rules.cfg @@ -12,10 +12,16 @@ regex: .*: UserWarning: replace: NBVAL-FILEPATH: UserWarning: +# sanitize the specific line of code shown in warning tracebacks +# since stacklevel changes will alter which line is reported [regex2] +regex: " .*\n" +replace: " CODE-LINE\n" + +[regex3] regex: \d{1,2}/\d{1,2}/\d{2,4} replace: DATE-STAMP -[regex3] +[regex4] regex: \d{2}:\d{2}:\d{2} replace: TIME-STAMP From 5914a2ee67555ad9110b60b7291e0a0c572070ae Mon Sep 17 00:00:00 2001 From: martin-springer Date: Wed, 4 Feb 2026 14:18:15 -0500 Subject: [PATCH 3/4] re-run notebook --- docs/TrendAnalysis_example.ipynb | 429 ++++++++++++++++++++++++++----- 1 file changed, 365 insertions(+), 64 deletions(-) diff --git a/docs/TrendAnalysis_example.ipynb b/docs/TrendAnalysis_example.ipynb index 45744e99..cd036911 100644 --- a/docs/TrendAnalysis_example.ipynb +++ b/docs/TrendAnalysis_example.ipynb @@ -22,7 +22,14 @@ { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:12:11.059602Z", + "iopub.status.busy": "2026-02-04T19:12:11.058607Z", + "iopub.status.idle": "2026-02-04T19:12:14.608421Z", + "shell.execute_reply": "2026-02-04T19:12:14.608421Z" + } + }, "outputs": [], "source": [ "import pandas as pd\n", @@ -36,7 +43,14 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:12:14.611430Z", + "iopub.status.busy": "2026-02-04T19:12:14.611430Z", + "iopub.status.idle": "2026-02-04T19:12:14.622950Z", + "shell.execute_reply": "2026-02-04T19:12:14.622431Z" + } + }, "outputs": [], "source": [ "#Update the style of plots\n", @@ -54,7 +68,14 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:12:14.625956Z", + "iopub.status.busy": "2026-02-04T19:12:14.625956Z", + "iopub.status.idle": "2026-02-04T19:12:14.628409Z", + "shell.execute_reply": "2026-02-04T19:12:14.628409Z" + } + }, "outputs": [], "source": [ "# Set the random seed for numpy to ensure consistent results\n", @@ -78,7 +99,14 @@ { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:12:14.630418Z", + "iopub.status.busy": "2026-02-04T19:12:14.630418Z", + "iopub.status.idle": "2026-02-04T19:12:14.682304Z", + "shell.execute_reply": "2026-02-04T19:12:14.681301Z" + } + }, "outputs": [], "source": [ "# Import the example data\n", @@ -99,7 +127,14 @@ { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:12:14.686163Z", + "iopub.status.busy": "2026-02-04T19:12:14.685306Z", + "iopub.status.idle": "2026-02-04T19:12:14.890023Z", + "shell.execute_reply": "2026-02-04T19:12:14.890023Z" + } + }, "outputs": [], "source": [ "df = df.rename(columns = {\n", @@ -139,7 +174,14 @@ { "cell_type": "code", "execution_count": 6, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:12:14.893032Z", + "iopub.status.busy": "2026-02-04T19:12:14.893032Z", + "iopub.status.idle": "2026-02-04T19:12:41.974653Z", + "shell.execute_reply": "2026-02-04T19:12:41.974146Z" + } + }, "outputs": [ { "data": { @@ -181,7 +223,14 @@ { "cell_type": "code", "execution_count": 7, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:12:42.016670Z", + "iopub.status.busy": "2026-02-04T19:12:42.016670Z", + "iopub.status.idle": "2026-02-04T19:13:04.387333Z", + "shell.execute_reply": "2026-02-04T19:13:04.386324Z" + } + }, "outputs": [], "source": [ "ta = rdtools.TrendAnalysis(df['power'], df['poa'],\n", @@ -209,14 +258,21 @@ { "cell_type": "code", "execution_count": 8, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:13:04.392334Z", + "iopub.status.busy": "2026-02-04T19:13:04.392334Z", + "iopub.status.idle": "2026-02-04T19:14:02.163631Z", + "shell.execute_reply": "2026-02-04T19:14:02.163127Z" + } + }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\rdtools\\rdtools\\soiling.py:27: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", - " warnings.warn(\n" + "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\public\\rdtools\\rdtools\\analysis_chains.py:851: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", + " from rdtools import soiling\n" ] } ], @@ -234,7 +290,14 @@ { "cell_type": "code", "execution_count": 9, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:02.168637Z", + "iopub.status.busy": "2026-02-04T19:14:02.168637Z", + "iopub.status.idle": "2026-02-04T19:14:02.173143Z", + "shell.execute_reply": "2026-02-04T19:14:02.172638Z" + } + }, "outputs": [], "source": [ "yoy_results = ta.results['sensor']['yoy_degradation']\n", @@ -244,7 +307,14 @@ { "cell_type": "code", "execution_count": 10, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:02.177148Z", + "iopub.status.busy": "2026-02-04T19:14:02.176152Z", + "iopub.status.idle": "2026-02-04T19:14:02.181516Z", + "shell.execute_reply": "2026-02-04T19:14:02.181516Z" + } + }, "outputs": [ { "name": "stdout", @@ -264,7 +334,14 @@ { "cell_type": "code", "execution_count": 11, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:02.185524Z", + "iopub.status.busy": "2026-02-04T19:14:02.184522Z", + "iopub.status.idle": "2026-02-04T19:14:02.189439Z", + "shell.execute_reply": "2026-02-04T19:14:02.189439Z" + } + }, "outputs": [ { "name": "stdout", @@ -292,7 +369,14 @@ { "cell_type": "code", "execution_count": 12, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:02.193452Z", + "iopub.status.busy": "2026-02-04T19:14:02.193452Z", + "iopub.status.idle": "2026-02-04T19:14:03.089080Z", + "shell.execute_reply": "2026-02-04T19:14:03.088071Z" + } + }, "outputs": [ { "data": { @@ -316,7 +400,14 @@ { "cell_type": "code", "execution_count": 13, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:03.093086Z", + "iopub.status.busy": "2026-02-04T19:14:03.093086Z", + "iopub.status.idle": "2026-02-04T19:14:03.409299Z", + "shell.execute_reply": "2026-02-04T19:14:03.408292Z" + } + }, "outputs": [ { "data": { @@ -340,7 +431,14 @@ { "cell_type": "code", "execution_count": 14, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:03.414300Z", + "iopub.status.busy": "2026-02-04T19:14:03.413304Z", + "iopub.status.idle": "2026-02-04T19:14:06.642546Z", + "shell.execute_reply": "2026-02-04T19:14:06.642546Z" + } + }, "outputs": [ { "data": { @@ -369,14 +467,21 @@ { "cell_type": "code", "execution_count": 15, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:06.646552Z", + "iopub.status.busy": "2026-02-04T19:14:06.646552Z", + "iopub.status.idle": "2026-02-04T19:14:30.828095Z", + "shell.execute_reply": "2026-02-04T19:14:30.827572Z" + } + }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\rdtools\\rdtools\\plotting.py:173: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", - " warnings.warn(\n" + "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\public\\rdtools\\rdtools\\analysis_chains.py:1144: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", + " fig = plotting.soiling_monte_carlo_plot(\n" ] }, { @@ -398,14 +503,21 @@ { "cell_type": "code", "execution_count": 16, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:30.831111Z", + "iopub.status.busy": "2026-02-04T19:14:30.831111Z", + "iopub.status.idle": "2026-02-04T19:14:31.013295Z", + "shell.execute_reply": "2026-02-04T19:14:31.013295Z" + } + }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\rdtools\\rdtools\\plotting.py:233: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", - " warnings.warn(\n" + "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\public\\rdtools\\rdtools\\analysis_chains.py:1176: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", + " fig = plotting.soiling_interval_plot(\n" ] }, { @@ -427,14 +539,21 @@ { "cell_type": "code", "execution_count": 17, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:31.016303Z", + "iopub.status.busy": "2026-02-04T19:14:31.016303Z", + "iopub.status.idle": "2026-02-04T19:14:31.143186Z", + "shell.execute_reply": "2026-02-04T19:14:31.143186Z" + } + }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\rdtools\\rdtools\\plotting.py:273: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", - " warnings.warn(\n" + "c:\\users\\mspringe\\onedrive - nrel\\msp\\pvfleets\\repos\\public\\rdtools\\rdtools\\analysis_chains.py:1206: UserWarning: The soiling module is currently experimental. The API, results, and default behaviors may change in future releases (including MINOR and PATCH releases) as the code matures.\n", + " fig = plotting.soiling_rate_histogram(results_dict[\"calc_info\"], **kwargs)\n" ] }, { @@ -463,7 +582,14 @@ { "cell_type": "code", "execution_count": 18, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:31.147269Z", + "iopub.status.busy": "2026-02-04T19:14:31.147269Z", + "iopub.status.idle": "2026-02-04T19:14:31.159709Z", + "shell.execute_reply": "2026-02-04T19:14:31.159195Z" + } + }, "outputs": [ { "data": { @@ -606,7 +732,14 @@ { "cell_type": "code", "execution_count": 19, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:14:31.162723Z", + "iopub.status.busy": "2026-02-04T19:14:31.161723Z", + "iopub.status.idle": "2026-02-04T19:15:26.676904Z", + "shell.execute_reply": "2026-02-04T19:15:26.676904Z" + } + }, "outputs": [], "source": [ "# Instantiate a new instance of TrendAnalysis\n", @@ -628,7 +761,14 @@ { "cell_type": "code", "execution_count": 20, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:26.682912Z", + "iopub.status.busy": "2026-02-04T19:15:26.681914Z", + "iopub.status.idle": "2026-02-04T19:15:26.691519Z", + "shell.execute_reply": "2026-02-04T19:15:26.691519Z" + } + }, "outputs": [ { "data": { @@ -726,7 +866,14 @@ { "cell_type": "code", "execution_count": 21, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:26.694526Z", + "iopub.status.busy": "2026-02-04T19:15:26.694526Z", + "iopub.status.idle": "2026-02-04T19:15:26.700822Z", + "shell.execute_reply": "2026-02-04T19:15:26.700822Z" + } + }, "outputs": [ { "data": { @@ -752,8 +899,44 @@ { "cell_type": "code", "execution_count": 22, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:26.703836Z", + "iopub.status.busy": "2026-02-04T19:15:26.703836Z", + "iopub.status.idle": "2026-02-04T19:15:28.228660Z", + "shell.execute_reply": "2026-02-04T19:15:28.228151Z" + } + }, "outputs": [ + { + "data": { + "text/html": [ + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "application/vnd.plotly.v1+json": { @@ -61364,7 +61547,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61400,7 +61583,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -61424,7 +61607,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61460,7 +61643,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -61475,7 +61658,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61511,7 +61694,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -61538,7 +61721,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61574,7 +61757,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -61589,7 +61772,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61625,7 +61808,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -61770,7 +61953,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61806,7 +61989,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -61897,7 +62080,7 @@ ], "sequential": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61933,13 +62116,13 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], "sequentialminus": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -61975,7 +62158,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ] @@ -62107,8 +62290,8 @@ "xaxis": { "anchor": "y", "domain": [ - 0, - 1 + 0.0, + 1.0 ], "title": { "text": "datetime" @@ -62117,15 +62300,42 @@ "yaxis": { "anchor": "x", "domain": [ - 0, - 1 + 0.0, + 1.0 ], "title": { "text": "energy_Wh" } } } - } + }, + "text/html": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -62143,7 +62353,14 @@ { "cell_type": "code", "execution_count": 23, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:28.270175Z", + "iopub.status.busy": "2026-02-04T19:15:28.270175Z", + "iopub.status.idle": "2026-02-04T19:15:28.532947Z", + "shell.execute_reply": "2026-02-04T19:15:28.532947Z" + } + }, "outputs": [ { "data": { @@ -62175,7 +62392,14 @@ { "cell_type": "code", "execution_count": 24, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:28.536968Z", + "iopub.status.busy": "2026-02-04T19:15:28.535960Z", + "iopub.status.idle": "2026-02-04T19:15:28.541456Z", + "shell.execute_reply": "2026-02-04T19:15:28.540952Z" + } + }, "outputs": [], "source": [ "def filter_stuck_values(pandas_series):\n", @@ -62194,7 +62418,14 @@ { "cell_type": "code", "execution_count": 25, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:28.544460Z", + "iopub.status.busy": "2026-02-04T19:15:28.543463Z", + "iopub.status.idle": "2026-02-04T19:15:47.311806Z", + "shell.execute_reply": "2026-02-04T19:15:47.311806Z" + } + }, "outputs": [], "source": [ "# Instantiate a new instance of TrendAnalysis\n", @@ -62209,7 +62440,14 @@ { "cell_type": "code", "execution_count": 26, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:47.316873Z", + "iopub.status.busy": "2026-02-04T19:15:47.315875Z", + "iopub.status.idle": "2026-02-04T19:15:47.783337Z", + "shell.execute_reply": "2026-02-04T19:15:47.783337Z" + } + }, "outputs": [], "source": [ "stuck_filter = (\n", @@ -62228,7 +62466,14 @@ { "cell_type": "code", "execution_count": 27, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:15:47.788385Z", + "iopub.status.busy": "2026-02-04T19:15:47.787342Z", + "iopub.status.idle": "2026-02-04T19:16:12.566655Z", + "shell.execute_reply": "2026-02-04T19:16:12.566655Z" + } + }, "outputs": [ { "data": { @@ -62263,7 +62508,14 @@ { "cell_type": "code", "execution_count": 28, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:16:12.571003Z", + "iopub.status.busy": "2026-02-04T19:16:12.571003Z", + "iopub.status.idle": "2026-02-04T19:16:12.576522Z", + "shell.execute_reply": "2026-02-04T19:16:12.576017Z" + } + }, "outputs": [ { "data": { @@ -62290,7 +62542,14 @@ { "cell_type": "code", "execution_count": 29, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:16:12.578526Z", + "iopub.status.busy": "2026-02-04T19:16:12.578526Z", + "iopub.status.idle": "2026-02-04T19:16:36.383047Z", + "shell.execute_reply": "2026-02-04T19:16:36.383047Z" + } + }, "outputs": [ { "data": { @@ -62326,7 +62585,14 @@ { "cell_type": "code", "execution_count": 30, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:16:36.387060Z", + "iopub.status.busy": "2026-02-04T19:16:36.387060Z", + "iopub.status.idle": "2026-02-04T19:16:36.645957Z", + "shell.execute_reply": "2026-02-04T19:16:36.645957Z" + } + }, "outputs": [ { "data": { @@ -62364,7 +62630,14 @@ { "cell_type": "code", "execution_count": 31, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:16:36.649968Z", + "iopub.status.busy": "2026-02-04T19:16:36.648966Z", + "iopub.status.idle": "2026-02-04T19:16:36.654476Z", + "shell.execute_reply": "2026-02-04T19:16:36.653969Z" + } + }, "outputs": [], "source": [ "from rdtools.soiling import CODSAnalysis\n", @@ -62382,7 +62655,14 @@ { "cell_type": "code", "execution_count": 32, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:16:36.657488Z", + "iopub.status.busy": "2026-02-04T19:16:36.657488Z", + "iopub.status.idle": "2026-02-04T19:17:38.775433Z", + "shell.execute_reply": "2026-02-04T19:17:38.775433Z" + } + }, "outputs": [], "source": [ "results_df, degradation, soiling_loss = CODS.run_bootstrap(reps=16, bootstrap_seed=42)" @@ -62391,7 +62671,14 @@ { "cell_type": "code", "execution_count": 33, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:17:38.780440Z", + "iopub.status.busy": "2026-02-04T19:17:38.779440Z", + "iopub.status.idle": "2026-02-04T19:17:41.020579Z", + "shell.execute_reply": "2026-02-04T19:17:41.020579Z" + } + }, "outputs": [ { "data": { @@ -62522,7 +62809,14 @@ { "cell_type": "code", "execution_count": 34, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:17:41.024589Z", + "iopub.status.busy": "2026-02-04T19:17:41.024589Z", + "iopub.status.idle": "2026-02-04T19:17:41.029924Z", + "shell.execute_reply": "2026-02-04T19:17:41.029924Z" + } + }, "outputs": [ { "name": "stdout", @@ -62548,7 +62842,14 @@ { "cell_type": "code", "execution_count": 35, - "metadata": {}, + "metadata": { + "execution": { + "iopub.execute_input": "2026-02-04T19:17:41.034026Z", + "iopub.status.busy": "2026-02-04T19:17:41.032930Z", + "iopub.status.idle": "2026-02-04T19:17:41.831294Z", + "shell.execute_reply": "2026-02-04T19:17:41.830790Z" + } + }, "outputs": [ { "data": { From a456ac481c75cb590e732e0112feba737754ce58 Mon Sep 17 00:00:00 2001 From: martin-springer Date: Wed, 4 Feb 2026 14:24:10 -0500 Subject: [PATCH 4/4] Update nbval sanitization rules to ignore warning traceback code lines The stacklevel parameter changes which line of code is shown in warning tracebacks. Add regex rule to sanitize these lines so tests pass regardless of stacklevel values. --- docs/nbval_sanitization_rules.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/nbval_sanitization_rules.cfg b/docs/nbval_sanitization_rules.cfg index 62c6ac31..58fbe995 100644 --- a/docs/nbval_sanitization_rules.cfg +++ b/docs/nbval_sanitization_rules.cfg @@ -15,8 +15,8 @@ replace: NBVAL-FILEPATH: UserWarning: # sanitize the specific line of code shown in warning tracebacks # since stacklevel changes will alter which line is reported [regex2] -regex: " .*\n" -replace: " CODE-LINE\n" +regex: ^ .*$ +replace: CODE-LINE [regex3] regex: \d{1,2}/\d{1,2}/\d{2,4}