Skip to content

Commit 8ffa68e

Browse files
authored
rm IPython (#720)
* rm ipython * rm ipython from tests * refactor text repr
1 parent 6625d93 commit 8ffa68e

File tree

8 files changed

+78
-114
lines changed

8 files changed

+78
-114
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Internals/Minor Fixes
2222
---------------------
2323
- Refactor ``asv`` benchmarking. Add ``run-benchmarks`` label to ``PR`` to run ``asv``
2424
via Github Actions. (:issue:`664`, :pr:`718`) `Aaron Spring`_.
25+
- Remove ``ipython`` from ``requirements.txt``. (:pr:`720`) `Aaron Spring`_.
2526

2627

2728
climpred v2.2.0 (2021-12-20)

ci/requirements/climpred-dev.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ dependencies:
1111
- sphinxcontrib-napoleon
1212
- sphinx-copybutton
1313
# IDE
14-
- ipywidgets
1514
- jupyterlab
1615
- nb_conda_kernels # switch conda envs in jupyter
1716
# Input/Output

ci/requirements/docs.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ dependencies:
66
- python=3.9
77
- matplotlib-base
88
- netcdf4
9-
- toolz
109
- xarray>=0.19.0
1110
- xskillscore>=0.0.18
12-
- cftime
11+
- cftime>=0.1.5
1312
# docs
1413
- jupyterlab
1514
- sphinx

ci/requirements/maximum-tests.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@ channels:
44
- nodefaults
55
dependencies:
66
- python>=3.7
7-
- cftime>=1.1.2
7+
- cftime>=1.5.0
88
- coveralls
99
- dask-core
1010
- eofs
1111
- esmpy=*=mpi* # Ensures MPI works with version of esmpy.
12-
- ipython
1312
- matplotlib-base
1413
- nc-time-axis>=1.4.0
1514
- netcdf4

ci/requirements/minimum-tests.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ channels:
44
- nodefaults
55
dependencies:
66
- python>=3.7
7-
- cftime>=1.1.2
7+
- cftime>=1.5.0
88
- coveralls
99
- dask-core
10-
- ipython
1110
- netcdf4
1211
- pip
1312
- pytest

climpred/classes.py

Lines changed: 51 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import cf_xarray # noqa
2020
import numpy as np
2121
import xarray as xr
22-
from IPython.display import display_html
2322
from xarray.core.coordinates import DatasetCoordinates
2423
from xarray.core.dataset import DataVariables
2524
from xarray.core.formatting_html import dataset_repr
@@ -109,86 +108,39 @@ def _display_metadata(self) -> str:
109108
>>> hindcast = climpred.HindcastEnsemble(init)
110109
>>> print(hindcast)
111110
<climpred.HindcastEnsemble>
112-
Initialized Ensemble:
111+
Initialized:
113112
SST (init, lead, member) float64 -0.2404 -0.2085 ... 0.7442 0.7384
114-
Observations:
115-
None
116113
Uninitialized:
117114
None
115+
Observations:
116+
None
118117
119118
"""
120119
SPACE = " "
121-
header = f"<climpred.{type(self).__name__}>"
122-
summary = header + "\nInitialized Ensemble:\n"
123-
summary += SPACE + str(self._datasets["initialized"].data_vars)[18:].strip() + "\n"
124-
if isinstance(self, HindcastEnsemble):
125-
# Prints out observations and associated variables if they exist.
126-
# If not, just write "None".
127-
summary += "Observations:\n"
128-
if any(self._datasets["observations"]):
129-
num_obs = len(self._datasets["observations"].data_vars)
130-
for i in range(1, num_obs + 1):
131-
summary += (
132-
SPACE
133-
+ str(self._datasets["observations"].data_vars)
134-
.split("\n")[i]
135-
.strip()
136-
+ "\n"
137-
)
138-
else:
139-
summary += SPACE + "None\n"
140-
elif isinstance(self, PerfectModelEnsemble):
141-
summary += "Control:\n"
142-
# Prints out control variables if a control is appended. If not,
143-
# just write "None".
144-
if any(self._datasets["control"]):
145-
num_ctrl = len(self._datasets["control"].data_vars)
146-
for i in range(1, num_ctrl + 1):
147-
summary += (
148-
SPACE
149-
+ str(self._datasets["control"].data_vars).split("\n")[i].strip()
150-
+ "\n"
120+
summary = f"<climpred.{type(self).__name__}>\n"
121+
122+
for k in self._datasets.keys():
123+
if self._datasets[k]:
124+
summary += (
125+
str(self._datasets[k].data_vars).replace(
126+
"Data variables", k.capitalize()
151127
)
152-
else:
153-
summary += SPACE + "None\n"
154-
if any(self._datasets["uninitialized"]):
155-
summary += "Uninitialized:\n"
156-
summary += SPACE + str(self._datasets["uninitialized"].data_vars)[18:].strip()
157-
else:
158-
summary += "Uninitialized:\n"
159-
summary += SPACE + "None"
160-
return summary
161-
162-
163-
def _display_metadata_html(self) -> str:
164-
"""Print contents of :py:class:`.PredictionEnsemble` as html."""
165-
header = f"<h4>climpred.{type(self).__name__}</h4>"
166-
display_html(header, raw=True)
167-
init_repr_str = dataset_repr(self._datasets["initialized"])
168-
init_repr_str = init_repr_str.replace("xarray.Dataset", "Initialized Ensemble")
169-
display_html(init_repr_str, raw=True)
170-
171-
if isinstance(self, HindcastEnsemble):
172-
if any(self._datasets["observations"]):
173-
obs_repr_str = dataset_repr(self._datasets["observations"])
174-
obs_repr_str = obs_repr_str.replace("xarray.Dataset", "Observations")
175-
display_html(obs_repr_str, raw=True)
176-
elif isinstance(self, PerfectModelEnsemble):
177-
if any(self._datasets["control"]):
178-
control_repr_str = dataset_repr(self._datasets["control"])
179-
control_repr_str = control_repr_str.replace(
180-
"xarray.Dataset", "Control Simulation"
128+
+ "\n"
181129
)
182-
display_html(control_repr_str, raw=True)
130+
else:
131+
summary += f"{k.capitalize()}:\n{SPACE}None\n"
132+
return summary.strip("\n")
183133

184-
if any(self._datasets["uninitialized"]):
185-
uninit_repr_str = dataset_repr(self._datasets["uninitialized"])
186-
uninit_repr_str = uninit_repr_str.replace("xarray.Dataset", "Uninitialized")
187-
display_html(uninit_repr_str, raw=True)
188-
# better would be to aggregate repr_strs and then all return but this fails
189-
# TypeError: __repr__ returned non-string (type NoneType)
190-
# workaround return empty string
191-
return ""
134+
135+
def _display_metadata_html(self):
136+
"""Show contents of :py:class:`.PredictionEnsemble` as html."""
137+
html_str = f"<h4>climpred.{type(self).__name__}</h4>"
138+
for k in self._datasets.keys():
139+
if self._datasets[k]:
140+
html_str += dataset_repr(self._datasets[k]).replace(
141+
"xarray.Dataset", k.capitalize()
142+
)
143+
return html_str
192144

193145

194146
class PredictionEnsemble:
@@ -377,14 +329,17 @@ def data_vars(self) -> DataVariables:
377329
varlist = list(varset)
378330
return self.get_initialized()[varlist].data_vars
379331

380-
# when you just print it interactively
381-
# https://stackoverflow.com/questions/1535327/how-to-print-objects-of-class-using-print
332+
def _repr_html_(self):
333+
"""Return for :py:class:`.PredictionEnsemble` in html."""
334+
from html import escape
335+
336+
if XR_OPTIONS["display_style"] == "text":
337+
return f"<pre>{escape(repr(self))}</pre>"
338+
return _display_metadata_html(self)
339+
382340
def __repr__(self) -> str:
383-
"""Return for print(PredictionEnsemble)."""
384-
if XR_OPTIONS["display_style"] == "html":
385-
return _display_metadata_html(self)
386-
else:
387-
return _display_metadata(self)
341+
"""Return for print(:py:class:`.PredictionEnsemble`)."""
342+
return _display_metadata(self)
388343

389344
def __len__(self) -> int:
390345
"""Return number of all variables :py:class:`.PredictionEnsemble`."""
@@ -658,6 +613,8 @@ def __getattr__(
658613
Args:
659614
* name: str of xarray function, e.g., ``.isel()`` or ``.sum()``.
660615
"""
616+
if name == "_ipython_canary_method_should_not_exist_":
617+
raise AttributeError(name)
661618

662619
def wrapper(*args, **kwargs):
663620
"""Apply arbitrary function to all datasets in ``PerfectModelEnsemble``.
@@ -809,12 +766,12 @@ def smooth(
809766
810767
>>> HindcastEnsemble_3D.smooth({"lon": 1, "lat": 1})
811768
<climpred.HindcastEnsemble>
812-
Initialized Ensemble:
769+
Initialized:
813770
SST (init, lead, lat, lon) float32 -0.3236 -0.3161 -0.3083 ... 0.0 0.0
814-
Observations:
815-
SST (time, lat, lon) float32 0.002937 0.001561 0.002587 ... 0.0 0.0 0.0
816771
Uninitialized:
817772
None
773+
Observations:
774+
SST (time, lat, lon) float32 0.002937 0.001561 0.002587 ... 0.0 0.0 0.0
818775
819776
``smooth`` simultaneously aggregates spatially listening to ``lon`` and
820777
``lat`` and temporally listening to ``lead`` or ``time``.
@@ -924,21 +881,21 @@ def remove_seasonality(
924881
Examples:
925882
>>> HindcastEnsemble
926883
<climpred.HindcastEnsemble>
927-
Initialized Ensemble:
884+
Initialized:
928885
SST (init, lead, member) float64 -0.2392 -0.2203 ... 0.618 0.6136
929-
Observations:
930-
SST (time) float32 -0.4015 -0.3524 -0.1851 ... 0.2481 0.346 0.4502
931886
Uninitialized:
932887
SST (time, member) float64 -0.1969 -0.01221 -0.275 ... 0.4179 0.3974
888+
Observations:
889+
SST (time) float32 -0.4015 -0.3524 -0.1851 ... 0.2481 0.346 0.4502
933890
>>> # example already effectively without seasonal cycle
934891
>>> HindcastEnsemble.remove_seasonality(seasonality="month")
935892
<climpred.HindcastEnsemble>
936-
Initialized Ensemble:
893+
Initialized:
937894
SST (init, lead, member) float64 -0.2349 -0.216 ... 0.6476 0.6433
938-
Observations:
939-
SST (time) float32 -0.3739 -0.3248 -0.1575 ... 0.2757 0.3736 0.4778
940895
Uninitialized:
941896
SST (time, member) float64 -0.1789 0.005732 -0.257 ... 0.4359 0.4154
897+
Observations:
898+
SST (time) float32 -0.3739 -0.3248 -0.1575 ... 0.2757 0.3736 0.4778
942899
"""
943900

944901
def _remove_seasonality(ds, initialized_dim="init", seasonality=None):
@@ -1797,21 +1754,21 @@ def generate_uninitialized(
17971754
Example:
17981755
>>> HindcastEnsemble # uninitialized from historical simulations
17991756
<climpred.HindcastEnsemble>
1800-
Initialized Ensemble:
1757+
Initialized:
18011758
SST (init, lead, member) float64 -0.2392 -0.2203 ... 0.618 0.6136
1802-
Observations:
1803-
SST (time) float32 -0.4015 -0.3524 -0.1851 ... 0.2481 0.346 0.4502
18041759
Uninitialized:
18051760
SST (time, member) float64 -0.1969 -0.01221 -0.275 ... 0.4179 0.3974
1761+
Observations:
1762+
SST (time) float32 -0.4015 -0.3524 -0.1851 ... 0.2481 0.346 0.4502
18061763
18071764
>>> HindcastEnsemble.generate_uninitialized() # newly generated from initialized
18081765
<climpred.HindcastEnsemble>
1809-
Initialized Ensemble:
1766+
Initialized:
18101767
SST (init, lead, member) float64 -0.2392 -0.2203 ... 0.618 0.6136
1811-
Observations:
1812-
SST (time) float32 -0.4015 -0.3524 -0.1851 ... 0.2481 0.346 0.4502
18131768
Uninitialized:
18141769
SST (time, member) float64 0.04868 0.07173 0.09435 ... 0.4158 0.418
1770+
Observations:
1771+
SST (time) float32 -0.4015 -0.3524 -0.1851 ... 0.2481 0.346 0.4502
18151772
"""
18161773
uninit = resample_uninitialized_from_initialized(
18171774
self._datasets["initialized"], resample_dim=resample_dim

climpred/tests/test_repr.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,51 @@
11
import pytest
22
import xarray as xr
3-
from IPython.display import display_html
43

54
from climpred.classes import HindcastEnsemble, PerfectModelEnsemble
65

76

7+
def assert_repr(pe, display_style):
8+
if display_style == "text":
9+
repr_str = pe.__repr__()
10+
elif display_style == "html":
11+
repr_str = pe._repr_html_()
12+
assert "Ensemble" in repr_str
13+
if display_style == "text":
14+
assert "</pre>" not in repr_str
15+
elif display_style == "html":
16+
assert "icon" in repr_str
17+
18+
819
@pytest.mark.parametrize("display_style", ("html", "text"))
9-
def test_repr_PM(PM_da_initialized_1d, PM_da_control_1d, display_style):
20+
def test_repr_PerfectModelEnsemble(
21+
PM_da_initialized_1d, PM_da_control_1d, display_style
22+
):
1023
"""Test html and text repr."""
11-
display = print if display_style == "text" else display_html
1224
with xr.set_options(display_style=display_style):
1325
pm = PerfectModelEnsemble(PM_da_initialized_1d)
14-
display(pm)
26+
assert_repr(pm, display_style)
1527
pm = pm.add_control(PM_da_control_1d)
16-
display(pm)
28+
assert_repr(pm, display_style)
1729
pm = pm.generate_uninitialized()
18-
display(pm)
30+
assert_repr(pm, display_style)
1931

2032

2133
@pytest.mark.parametrize("display_style", ("html", "text"))
22-
def test_repr_HC(
34+
def test_repr_HindcastEnsemble(
2335
hind_ds_initialized_1d,
2436
hist_ds_uninitialized_1d,
2537
observations_ds_1d,
2638
display_style,
2739
):
2840
"""Test html repr."""
29-
display = print if display_style == "text" else display_html
3041
with xr.set_options(display_style=display_style):
3142
he = HindcastEnsemble(hind_ds_initialized_1d)
32-
display(he)
43+
assert_repr(he, display_style)
3344
he = he.add_uninitialized(hist_ds_uninitialized_1d)
34-
display(he)
45+
assert_repr(he, display_style)
3546
he = he.add_observations(observations_ds_1d)
36-
display(he)
47+
assert_repr(he, display_style)
3748
# no uninit
3849
he = HindcastEnsemble(hind_ds_initialized_1d)
3950
he = he.add_observations(observations_ds_1d)
40-
display(he)
51+
assert_repr(he, display_style)

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
xarray>=0.19.0
22
dask>=2021.10.0
3-
ipython<8.0.0
43
toolz
54
cftime>=1.5.0
65
xskillscore>=0.0.20

0 commit comments

Comments
 (0)