Skip to content

Commit a40e464

Browse files
authored
Direct-access attributes (#134)
* Initial changes and fixes to provide simple-access attributes. * Change naming scheme .attributes/_attributes -> .attrvals/attributes. * Adjust AttrvalsDict printout. * Various docs updates, adopting 'attrvals'. * Report beyond 1st doctest error. * Add 'html-keeplog' makefile target. rename target. * Use 'doctest' in place of 'code-block'. * Various docs fixes + improvements. * Rename attrvals as avals. * Tests for AttrvalsDict. * Provide an AttrvalsDict.copy(). * Improve docs for '.avals'. * Added whatsnew. * Fix ncdump usage, broken in merge resolution.
1 parent b547c0b commit a40e464

24 files changed

Lines changed: 624 additions & 250 deletions

.github/workflows/ci-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ jobs:
8484
if: matrix.session == 'doctests-docs'
8585
run: |
8686
cd docs
87-
pytest --doctest-glob="*.rst"
87+
pytest --doctest-glob="*.rst" --doctest-continue-on-failure
8888
8989
- name: "Run doctests: API"
9090
if: matrix.session == 'doctests-api'
9191
run: |
9292
cd lib
93-
pytest --doctest-modules
93+
pytest --doctest-modules --doctest-continue-on-failure

docs/Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ allapi:
2121
towncrier:
2222
towncrier build --keep
2323

24+
25+
# Tweaked "make html", which restores the changelog state after docs build.
26+
html-keeplog: html
27+
git checkout HEAD change_log.rst changelog_fragments/
28+
29+
2430
# Catch-all target: route all unknown targets to Sphinx using the new
2531
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
2632
%: Makefile allapi towncrier
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Added the ".avals" property as an easier way of managing attributes:
2+
This provides a simple "name: value" map, bypassing the NcAttribute objects and converting values to and from simple Python equivalents.
3+
This effectively replaces the older 'set_attrval' and 'get_attrval', which will eventually be removed.

docs/details/character_handling.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ slash "/", since in some technical cases a component name needs to specified as
1717
"path-like" compound.
1818

1919

20+
.. _character-attributes:
21+
2022
Characters in Attribute Values
2123
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2224
Character data in string *attribute* values can likewise be read and written simply as

docs/userdocs/getting_started/installation.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Like this:
1717

1818
.. code-block:: bash
1919
20-
pip install ncdata
20+
$ pip install ncdata
2121
2222
2323
Check install

docs/userdocs/getting_started/introduction.rst

Lines changed: 29 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ a dataset or group. It contains :attr:`~ncdata.NcData.dimensions`,
3333
:attr:`~ncdata.NcData.variables`, :attr:`~ncdata.NcData.groups`,
3434
and :attr:`~ncdata.NcData.attributes`:
3535

36-
.. code-block:: python
36+
.. doctest:: python
3737

3838
>>> from ncdata import NcData, NcDimension, NcVariable
3939
>>> data = NcData("myname")
@@ -75,13 +75,12 @@ NetCDF files via the `netcdf4-python package <http://unidata.github.io/netcdf4-p
7575

7676
Simple example:
7777

78-
.. code-block:: python
78+
.. doctest:: python
7979

8080
>>> from ncdata.netcdf4 import to_nc4, from_nc4
8181
>>> filepath = "./tmp.nc"
8282
>>> to_nc4(data, filepath)
83-
84-
>>> ncdump("tmp.nc") # utility calling 'ncdump -h' (not shown)
83+
>>> ncdump("tmp.nc") # utility which calls 'ncdump' command (not shown)
8584
netcdf tmp {
8685
dimensions:
8786
x = 3 ;
@@ -107,7 +106,7 @@ Variables
107106
Variables live in a :attr:`ncdata.NcData.variables` attribute,
108107
which behaves like a dictionary:
109108

110-
.. code-block:: python
109+
.. doctest:: python
111110

112111
>>> data.variables
113112
{'vx': <ncdata._core.NcVariable object at ...>}
@@ -131,80 +130,54 @@ which behaves like a dictionary:
131130

132131
Attributes
133132
^^^^^^^^^^
134-
Attributes live in the ``attributes`` property of a :class:`~ncdata.NcData`
135-
or :class:`~ncdata.NcVariable`:
133+
Attributes are held in the ``.attributes`` property of a :class:`~ncdata.NcData`
134+
or :class:`~ncdata.NcVariable`. However, they are more easily accessed via the ``.avals``
135+
property, which provides a simple name:value mapping :
136136

137-
.. code-block:: python
137+
.. doctest:: python
138138

139139
>>> var = data.variables["vx"]
140-
>>> var.set_attrval('a', 1)
141-
NcAttribute('a', 1)
142-
>>> var.set_attrval('b', 'this')
143-
NcAttribute('b', 'this')
140+
>>> var.avals['a'] = 1
141+
>>> var.avals['b'] = 'this'
144142

145143
>>> print(var)
146144
<NcVariable(float64): vx(x)
147145
vx:a = 1
148146
vx:b = 'this'
149147
>
150148

151-
>>> print(var.attributes)
152-
{'a': NcAttribute('a', 1), 'b': NcAttribute('b', 'this')}
153-
154-
>>> print(data)
155-
<NcData: myname
156-
dimensions:
157-
x = 3
158-
<BLANKLINE>
159-
variables:
160-
<NcVariable(float64): vx(x)
161-
vx:a = 1
162-
vx:b = 'this'
163-
>
149+
>>> var.avals['b'] = 7777
150+
>>> print(var)
151+
<NcVariable(float64): vx(x)
152+
vx:a = 1
153+
vx:b = 7777
164154
>
165155

166-
For technical reasons, each attribute is represented as an independent python
167-
:class:`ncdata.NcAttribute` object, i.e. they are *not* simply stored as a
168-
values in a name/value map.
169-
170-
Attribute values are actually :mod:`numpy.ndarray`, and hence have a ``dtype``.
171-
To make this easier, you can use regular python numbers and strings with
172-
:meth:`ncdata.NcAttribute.as_python_value` and the
173-
:meth:`~ncdata.NcVariable.set_attrval`
174-
and :meth:`~ncdata.NcVariable.get_attrval` of NcData/NcVariable.
156+
Attribute values are actually stored as :mod:`numpy.ndarray` arrays, and hence have a
157+
definite ``dtype``. However, ``.avals`` allows you to treat them mostly as ordinary
158+
python values (numbers and strings).
175159

176160

177161
Deletion and Renaming
178162
^^^^^^^^^^^^^^^^^^^^^
179-
Use python 'del' operation to remove:
163+
Use python 'del' operation to remove items:
180164

181-
.. code-block:: python
165+
.. doctest:: python
182166

183167
>>> del var.attributes['a']
184168
>>> print(var)
185169
<NcVariable(float64): vx(x)
186-
vx:b = 'this'
170+
vx:b = 7777
187171
>
188172

189173
There is also a 'rename' method of variables/attributes/groups:
190174

191-
.. code-block:: python
175+
.. doctest:: python
192176

193177
>>> var.attributes.rename("b", "qq")
194178
>>> print(var)
195179
<NcVariable(float64): vx(x)
196-
vx:qq = 'this'
197-
>
198-
199-
>>> print(data)
200-
<NcData: myname
201-
dimensions:
202-
x = 3
203-
<BLANKLINE>
204-
variables:
205-
<NcVariable(float64): vx(x)
206-
vx:qq = 'this'
207-
>
180+
vx:qq = 7777
208181
>
209182

210183
.. warning::
@@ -246,18 +219,18 @@ at :ref:`interface_support`.
246219

247220
Example code snippets :
248221

249-
.. code-block:: python
222+
.. doctest:: python
250223

251224
>>> # (make sure that Iris and Ncdata won't conflict over netcdf access)
252225
>>> from ncdata.threadlock_sharing import enable_lockshare
253226
>>> enable_lockshare(iris=True, xarray=True)
254227

255-
.. code-block:: python
228+
.. doctest:: python
256229

257230
>>> from ncdata.netcdf4 import from_nc4
258231
>>> data = from_nc4("tmp.nc")
259232

260-
.. code-block:: python
233+
.. doctest:: python
261234

262235
>>> from ncdata.iris import to_iris, from_iris
263236
>>> from iris import FUTURE
@@ -281,7 +254,7 @@ Example code snippets :
281254
>>> print(vv)
282255
v_mag / (m.s-1) (-- : 3)
283256

284-
.. code-block:: python
257+
.. doctest:: python
285258

286259
>>> from ncdata.xarray import to_xarray
287260
>>> xrds = to_xarray(from_iris([vx, vy, vv]))
@@ -296,7 +269,7 @@ Example code snippets :
296269
Attributes:
297270
Conventions: CF-1.7
298271

299-
.. code-block:: python
272+
.. doctest:: python
300273

301274
>>> from ncdata.iris_xarray import cubes_from_xarray
302275
>>> readback = cubes_from_xarray(xrds)
@@ -318,7 +291,7 @@ Thread safety
318291
prevent possible errors when computing or saving lazy data.
319292
For example:
320293

321-
.. code-block:: python
294+
.. doctest:: python
322295

323296
>>> from ncdata.threadlock_sharing import enable_lockshare
324297
>>> enable_lockshare(iris=True, xarray=True)

docs/userdocs/user_guide/common_operations.rst

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,31 @@ i.e. the operations of extract/remove/insert/rename/copy on the ``.dimensions``,
88

99
Most of these are hopefully "obvious" Pythonic methods of the container objects.
1010

11+
.. Note::
12+
13+
The special ``.avals`` property of :class:`NcData` and :class:`NcVariable` also
14+
provides key common operations associated with ``.attributes``, notably ``rename`` and
15+
the ``del`` operator. But not those needing NcAttribute objects -- so ``add`` and
16+
``addall` are not available.
17+
18+
1119
Extract and Remove
1220
------------------
1321
These are implemented as :meth:`~ncdata.NameMap.__delitem__` and :meth:`~ncdata.NameMap.pop`
1422
methods, which work in the usual way.
1523
16-
Examples :
24+
For Example:
25+
26+
.. testsetup::
27+
28+
>>> from ncdata import NcData, NcVariable
29+
>>> dataset = NcData(variables=[NcVariable('x'), NcVariable('y')])
30+
>>> data = dataset
31+
32+
.. doctest:: python
1733
18-
* ``var_x = dataset.variables.pop("x")``
19-
* ``del data.variables["x"]``
34+
>>> var_x = dataset.variables.pop("x")
35+
>>> del data.variables["y"]
2036
2137
Insert / Add
2238
------------
@@ -25,18 +41,31 @@ A new content (component) can be added under its own name with the
2541
2642
Example : ``dataset.variables.add(NcVariable("x", dimensions=["x"], data=my_data))``
2743

28-
An :meth:`~ncdata.NcAttribute` can also be added or set (if already present) with the special
29-
:meth:`~ncdata.NameMap.set_attrval` method.
44+
:meth:`~ncdata.NcAttribute`s can be treated in the same way, as a :class:`ncdata.NameMap`
45+
component of the parent object. But it is more usual to add or set attributes
46+
using ``.avals`` rather than ``.attributes``.
3047

31-
Example : ``dataset.variables["x"].set_attrval("units", "m s-1")``
48+
Example :
49+
50+
.. testsetup::
51+
52+
>>> dataset = NcData(variables=[NcVariable("x")])
53+
54+
.. doctest:: python
55+
56+
>>> dataset.variables["x"].avals["units"] = "m s-1"
3257

3358
Rename
3459
------
3560
A component can be renamed with the :meth:`~ncdata.NameMap.rename` method. This changes
3661
both the name in the container **and** the component's own name -- it is not recommended
3762
ever to set ``component.name`` directly, as this obviously can become inconsistent.
3863

39-
Example : ``dataset.variables.rename("x", "y")``
64+
Example :
65+
66+
.. doctest:: python
67+
68+
>>> dataset.variables.rename("x", "y")
4069

4170
.. warning::
4271
Renaming a dimension will not rename references to it (i.e. in variables), which
@@ -51,7 +80,7 @@ All core objects support a ``.copy()`` method. See for instance
5180
These however do *not* copy variable data arrays (either real or lazy), but produce new
5281
(copied) variables referencing the same arrays. So, for example:
5382

54-
.. code-block::
83+
.. doctest:: python
5584

5685
>>> # Construct a simple test dataset
5786
>>> import numpy as np
@@ -114,7 +143,7 @@ dataset which must "make sense". see : :ref:`correctness-checks` .
114143
Hence, there is no great need to install things in the 'right' order (e.g. dimensions
115144
before variables which need them). You can create objects in one go, like this :
116145

117-
.. code-block::
146+
.. doctest:: python
118147

119148
data = NcData(
120149
dimensions=[
@@ -131,7 +160,7 @@ before variables which need them). You can create objects in one go, like this
131160

132161
or iteratively, like this :
133162

134-
.. code-block::
163+
.. doctest:: python
135164

136165
data = NcData()
137166
dims = [("y", 2), ("x", 3)]

0 commit comments

Comments
 (0)