Skip to content

Commit 7db3d62

Browse files
committed
Update reprs and add FieldSet repr
Multiline particle printing Update function naming FieldSet repr
1 parent b0fe885 commit 7db3d62

File tree

6 files changed

+80
-24
lines changed

6 files changed

+80
-24
lines changed

parcels/field.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
assert_valid_gridindexingtype,
2222
assert_valid_interp_method,
2323
)
24-
from parcels.tools._helpers import deprecated_made_private, pretty_field
24+
from parcels.tools._helpers import deprecated_made_private, field_repr
2525
from parcels.tools.converters import (
2626
Geographic,
2727
GeographicPolar,
@@ -313,7 +313,7 @@ def __init__(
313313
raise SyntaxError(f'Field received an unexpected keyword argument "{list(kwargs.keys())[0]}"')
314314

315315
def __repr__(self) -> str:
316-
return pretty_field(self)
316+
return field_repr(self)
317317

318318
@property
319319
@deprecated_made_private # TODO: Remove 6 months after v3.1.0

parcels/fieldset.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from parcels.grid import Grid
1515
from parcels.gridset import GridSet
1616
from parcels.particlefile import ParticleFile
17-
from parcels.tools._helpers import deprecated_made_private
17+
from parcels.tools._helpers import deprecated_made_private, fieldset_repr
1818
from parcels.tools.converters import TimeConverter, convert_xarray_time_units
1919
from parcels.tools.loggers import logger
2020
from parcels.tools.statuscodes import TimeExtrapolationError
@@ -56,6 +56,9 @@ def __init__(self, U: Field | NestedField | None, V: Field | NestedField | None,
5656
self.compute_on_defer = None
5757
self._add_UVfield()
5858

59+
def __repr__(self):
60+
return fieldset_repr(self)
61+
5962
@property
6063
def particlefile(self):
6164
return self._particlefile

parcels/particleset.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from parcels.particle import JITParticle, Variable
2828
from parcels.particledata import ParticleData, ParticleDataIterator
2929
from parcels.particlefile import ParticleFile
30-
from parcels.tools._helpers import deprecated, deprecated_made_private, pretty_particleset
30+
from parcels.tools._helpers import deprecated, deprecated_made_private, particleset_repr
3131
from parcels.tools.converters import _get_cftime_calendars, convert_to_flat_array
3232
from parcels.tools.global_statics import get_package_dir
3333
from parcels.tools.loggers import logger
@@ -386,7 +386,7 @@ def size(self):
386386
return len(self.particledata)
387387

388388
def __repr__(self):
389-
return pretty_particleset(self)
389+
return particleset_repr(self)
390390

391391
def __len__(self):
392392
return len(self.particledata)

parcels/tools/_helpers.py

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
from __future__ import annotations
44

55
import functools
6+
import textwrap
67
import warnings
78
from collections.abc import Callable
8-
from textwrap import dedent
99
from typing import TYPE_CHECKING, Any
1010

1111
if TYPE_CHECKING:
12-
from parcels import Field, ParticleSet
12+
from parcels import Field, FieldSet, ParticleSet
13+
1314
PACKAGE = "Parcels"
1415

1516

@@ -64,34 +65,70 @@ def patch_docstring(obj: Callable, extra: str) -> None:
6465
obj.__doc__ = f"{obj.__doc__ or ''}{extra}".strip()
6566

6667

67-
def pretty_field(field: Field) -> str:
68+
def field_repr(field: Field) -> str:
6869
"""Return a pretty repr for Field"""
6970
out = f"""<{type(field).__name__}>
70-
grid : {field.grid!r }
71+
grid : {field.grid!r}
7172
extrapolate time: {field.allow_time_extrapolation!r}
72-
time_periodic : {field.time_periodic!r }
73-
gridindexingtype: {field.gridindexingtype!r }
74-
to_write : {field.to_write!r }
73+
time_periodic : {field.time_periodic!r}
74+
gridindexingtype: {field.gridindexingtype!r}
75+
to_write : {field.to_write!r}
7576
"""
76-
return dedent(out).strip()
77+
return textwrap.dedent(out).strip()
78+
79+
80+
def _format_list_items_multiline(items: list[str], level: int = 1) -> str:
81+
"""Given a list of strings, formats them across multiple lines.
82+
83+
Uses indentation levels of 4 spaces provided by ``level``.
84+
85+
Example
86+
-------
87+
>>> output = _format_list_items_multiline(["item1", "item2", "item3"], 4)
88+
>>> f"my_items: {output}"
89+
my_items: [
90+
item1,
91+
item2,
92+
item3,
93+
]
94+
"""
95+
if len(items) == 0:
96+
return "[]"
7797

98+
assert level >= 1, "Indentation level >=1 supported"
99+
indentation_str = level * 4 * " "
100+
indentation_str_end = (level - 1) * 4 * " "
78101

79-
def pretty_particleset(pset: ParticleSet) -> str:
102+
items = ",\n".join([textwrap.indent(i, indentation_str) for i in items])
103+
return f"[\n{items}\n{indentation_str_end}]"
104+
105+
106+
def particleset_repr(pset: ParticleSet) -> str:
80107
"""Return a pretty repr for ParticleSet"""
81108
if len(pset) < 10:
82-
particles = repr(list(pset))
109+
particles = [repr(p for p in pset)]
83110
else:
84-
lst = [repr(pset[i]) for i in range(7)] + ["..."]
85-
particles = f"[{', '.join(lst)}]"
111+
particles = [repr(pset[i]) for i in range(7)] + ["..."]
86112

87113
out = f"""<{type(pset).__name__}>
88114
fieldset : {pset.fieldset}
89115
pclass : {pset.pclass}
90116
repeatdt : {pset.repeatdt}
91117
# particles: {len(pset)}
92-
particles : {particles}
118+
particles : {_format_list_items_multiline(particles)}
119+
"""
120+
return textwrap.dedent(out).strip()
121+
122+
123+
def fieldset_repr(fieldset: FieldSet) -> str:
124+
"""Return a pretty repr for FieldSet"""
125+
fields_repr = "\n".join([repr(f) for f in fieldset.get_fields()])
126+
127+
out = f"""<{type(fieldset).__name__}>
128+
fields:
129+
{textwrap.indent(fields_repr, 8 * " ")}
93130
"""
94-
return dedent(out).strip()
131+
return textwrap.dedent(out).strip()
95132

96133

97134
def default_repr(obj: Any):

tests/test_reprs.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from tests.utils import create_fieldset_unit_mesh, create_simple_pset
1010

1111

12-
def validate_simple_repr(class_: type, kwargs: dict[str, Any]):
12+
def assert_simple_repr(class_: type, kwargs: dict[str, Any]):
1313
"""Test that the repr of an object contains all the arguments. This only works for objects where the repr matches the calling signature."""
1414
obj = class_(**kwargs)
1515
obj_repr = repr(obj)
@@ -55,13 +55,13 @@ def test_grid_repr():
5555
kwargs = dict(
5656
lon=np.array([1, 2, 3]), lat=np.array([4, 5, 6]), time=None, time_origin=TimeConverter(), mesh="spherical"
5757
)
58-
validate_simple_repr(Grid, kwargs)
58+
assert_simple_repr(Grid, kwargs)
5959

6060

6161
def test_variable_repr():
6262
"""Test arguments are in the repr of the Variable object."""
6363
kwargs = dict(name="test", dtype=np.float32, initial=0, to_write=False)
64-
validate_simple_repr(Variable, kwargs)
64+
assert_simple_repr(Variable, kwargs)
6565

6666

6767
def test_rectilineargrid_repr():
@@ -73,22 +73,27 @@ def test_rectilineargrid_repr():
7373
kwargs = dict(
7474
lon=np.array([1, 2, 3]), lat=np.array([4, 5, 6]), time=None, time_origin=TimeConverter(), mesh="spherical"
7575
)
76-
validate_simple_repr(RectilinearGrid, kwargs)
76+
assert_simple_repr(RectilinearGrid, kwargs)
7777

7878

7979
def test_particlefile_repr():
8080
pset = create_simple_pset()
8181
kwargs = dict(
8282
name="file.zarr", particleset=pset, outputdt=timedelta(hours=1), chunks=None, create_new_zarrfile=False
8383
)
84-
validate_simple_repr(ParticleFile, kwargs)
84+
assert_simple_repr(ParticleFile, kwargs)
8585

8686

8787
def test_field_repr():
8888
field = create_fieldset_unit_mesh().U
8989
assert valid_indentation(repr(field))
9090

9191

92+
def test_fieldset_repr():
93+
fieldset = create_fieldset_unit_mesh()
94+
assert valid_indentation(repr(fieldset))
95+
96+
9297
def test_particleset_repr():
9398
pset = create_simple_pset()
9499
valid_indentation(repr(pset))

tests/tools/test_helpers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
import pytest
22

3+
import parcels.tools._helpers as helpers
34
from parcels.tools._helpers import deprecated, deprecated_made_private
45

56

7+
def test_format_list_items_multiline():
8+
expected = """[
9+
item1,
10+
item2,
11+
item3
12+
]"""
13+
assert helpers._format_list_items_multiline(["item1", "item2", "item3"], 1) == expected
14+
assert helpers._format_list_items_multiline([], 1) == "[]"
15+
16+
617
def test_deprecated():
718
class SomeClass:
819
@deprecated()

0 commit comments

Comments
 (0)