diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 2d4cf26..8798ca0 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -1,6 +1,55 @@ List of changes =============================== +Unreleased changes (Available on GitHub) +---------------------------------------- +- Fix `#322`_ - provide a way to consistently retrieve header items which may + or may not be present in the header: + + If you try ordinary item-style access, + as is normal in Python, a KeyError exception will be raised if it is missing: + + .. code-block:: python + + >>> permit = las.well['PRMT'] + Traceback (most recent call last): + File "", line 1, in + File "c:\devapps\kinverarity\projects\lasio\lasio\las_items.py", line 313, in __getitem__ + raise KeyError("%s not in %s" % (key, self.keys())) + KeyError: "PRMT not in ['STRT', 'STOP', 'STEP', 'NULL', 'COMP', 'WELL', 'FLD', 'LOC', 'PROV', 'SRVC', 'DATE', 'UWI']" + + A better pattern is to use the :meth:`lasio.SectionItems.get` method, which + allows you to specify a default value in the case of it missing: + + .. code-block:: python + + >>> permit = las.well.get('PRMT', 'unknown') + >>> permit + HeaderItem(mnemonic="PRMT", unit="", value="unknown", descr="") + + You can use the ``add=True`` keyword argument if you would like this + header item to be added, as well as returned: + + .. code-block:: python + + >>> permit = las.well.get('PRMT', 'unknown', add=True) + >>> las.well + [HeaderItem(mnemonic="STRT", unit="M", value="0.05", descr="FIRST INDEX VALUE"), + HeaderItem(mnemonic="STOP", unit="M", value="136.6", descr="LAST INDEX VALUE"), + HeaderItem(mnemonic="STEP", unit="M", value="0.05", descr="STEP"), + HeaderItem(mnemonic="NULL", unit="", value="-99999", descr="NULL VALUE"), + HeaderItem(mnemonic="COMP", unit="", value="", descr="COMP"), + HeaderItem(mnemonic="WELL", unit="", value="Scorpio E1", descr="WELL"), + HeaderItem(mnemonic="FLD", unit="", value="", descr=""), + HeaderItem(mnemonic="LOC", unit="", value="Mt Eba", descr="LOC"), + HeaderItem(mnemonic="SRVC", unit="", value="", descr=""), + HeaderItem(mnemonic="CTRY", unit="", value="", descr=""), + HeaderItem(mnemonic="STAT", unit="", value="SA", descr="STAT"), + HeaderItem(mnemonic="CNTY", unit="", value="", descr=""), + HeaderItem(mnemonic="DATE", unit="", value="15/03/2015", descr="DATE"), + HeaderItem(mnemonic="UWI", unit="", value="6038-187", descr="WUNT"), + HeaderItem(mnemonic="PRMT", unit="", value="unknown", descr="")] + Version 0.30 (12 May 2022) -------------------------- - Fixes for `#446`_ (Implement a numpy-based reader for the data section; `#452`_) and @@ -308,6 +357,7 @@ Version 0.2 (2015-07-08) .. _#318: https://github.com/kinverarity1/lasio/issues/318 .. _#319: https://github.com/kinverarity1/lasio/issues/319 .. _#321: https://github.com/kinverarity1/lasio/issues/321 +.. _#322: https://github.com/kinverarity1/lasio/issues/322 .. _#325: https://github.com/kinverarity1/lasio/issues/325 .. _#326: https://github.com/kinverarity1/lasio/issues/326 .. _#327: https://github.com/kinverarity1/lasio/issues/327 @@ -387,7 +437,7 @@ Version 0.2 (2015-07-08) .. _#268: https://github.com/kinverarity1/lasio/issues/268 .. _#451: https://github.com/kinverarity1/lasio/issues/451 .. _#453: https://github.com/kinverarity1/lasio/issues/453 -.. _#455: https://github.com/kinverarity1/lasio/issues/455.. _#83: https://github.com/kinverarity1/lasio/issues/83 +.. _#455: https://github.com/kinverarity1/lasio/issues/455 .. _#265: https://github.com/kinverarity1/lasio/issues/265 .. _#332: https://github.com/kinverarity1/lasio/issues/332 .. _#375: https://github.com/kinverarity1/lasio/issues/375 diff --git a/docs/source/conf.py b/docs/source/conf.py index 9d8a8f0..beb3cf2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -26,7 +26,7 @@ master_doc = "index" project = u"lasio" -copyright = u"2013-2021, lasio contributors" +copyright = u"2013-2022, lasio contributors" author = u"lasio contributors" from pkg_resources import get_distribution diff --git a/docs/source/header-section.rst b/docs/source/header-section.rst index 730e39d..9b86752 100644 --- a/docs/source/header-section.rst +++ b/docs/source/header-section.rst @@ -258,6 +258,51 @@ There are methods intended for removing curves. Say you want to remove the PR cu CurveItem(mnemonic="DFAR", unit="G/CM3", value="", descr="DFAR", original_mnemonic="DFAR", data.shape=(121,)), CurveItem(mnemonic="DNEAR", unit="G/CM3", value="", descr="DNEAR", original_mnemonic="DNEAR", data.shape=(121,))] +Another common task is to retrieve a header item that may or may not be in the +file. If you try ordinary item-style access, +as is normal in Python, a KeyError exception will be raised if it is missing: + +.. code-block:: python + + >>> permit = las.well['PRMT'] + Traceback (most recent call last): + File "", line 1, in + File "c:\devapps\kinverarity\projects\lasio\lasio\las_items.py", line 313, in __getitem__ + raise KeyError("%s not in %s" % (key, self.keys())) + KeyError: "PRMT not in ['STRT', 'STOP', 'STEP', 'NULL', 'COMP', 'WELL', 'FLD', 'LOC', 'PROV', 'SRVC', 'DATE', 'UWI']" + +A better pattern is to use the :meth:`lasio.SectionItems.get` method, which +allows you to specify a default value in the case of it missing: + +.. code-block:: python + + >>> permit = las.well.get('PRMT', 'unknown') + >>> permit + HeaderItem(mnemonic="PRMT", unit="", value="unknown", descr="") + +You can use the ``add=True`` keyword argument if you would like this +header item to be added, as well as returned: + +.. code-block:: python + + >>> permit = las.well.get('PRMT', 'unknown', add=True) + >>> las.well + [HeaderItem(mnemonic="STRT", unit="M", value="0.05", descr="FIRST INDEX VALUE"), + HeaderItem(mnemonic="STOP", unit="M", value="136.6", descr="LAST INDEX VALUE"), + HeaderItem(mnemonic="STEP", unit="M", value="0.05", descr="STEP"), + HeaderItem(mnemonic="NULL", unit="", value="-99999", descr="NULL VALUE"), + HeaderItem(mnemonic="COMP", unit="", value="", descr="COMP"), + HeaderItem(mnemonic="WELL", unit="", value="Scorpio E1", descr="WELL"), + HeaderItem(mnemonic="FLD", unit="", value="", descr=""), + HeaderItem(mnemonic="LOC", unit="", value="Mt Eba", descr="LOC"), + HeaderItem(mnemonic="SRVC", unit="", value="", descr=""), + HeaderItem(mnemonic="CTRY", unit="", value="", descr=""), + HeaderItem(mnemonic="STAT", unit="", value="SA", descr="STAT"), + HeaderItem(mnemonic="CNTY", unit="", value="", descr=""), + HeaderItem(mnemonic="DATE", unit="", value="15/03/2015", descr="DATE"), + HeaderItem(mnemonic="UWI", unit="", value="6038-187", descr="WUNT"), + HeaderItem(mnemonic="PRMT", unit="", value="unknown", descr="")] + Handling special cases of header lines -------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 24a0c4a..a51f70f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -45,8 +45,7 @@ The final version of lasio with Python 2.7 support is v0.26. encodings lasio contributing - -.. include:: changelog.rst + changelog * :ref:`genindex` * :ref:`search` diff --git a/docs/source/lasio.rst b/docs/source/lasio.rst index 355693f..1365082 100644 --- a/docs/source/lasio.rst +++ b/docs/source/lasio.rst @@ -41,6 +41,11 @@ Reading data .. autoattribute:: lasio.LASFile.data .. automethod:: lasio.LASFile.stack_curves +Reading and modifying header data +--------------------------------- +.. autoclass:: lasio.SectionItems + :members: + Modifying data -------------- .. automethod:: lasio.LASFile.set_data diff --git a/lasio/las_items.py b/lasio/las_items.py index bf314f4..3e77095 100644 --- a/lasio/las_items.py +++ b/lasio/las_items.py @@ -312,6 +312,72 @@ def __getitem__(self, key): else: raise KeyError("%s not in %s" % (key, self.keys())) + def get(self, mnemonic, default="", add=False): + """Get an item, with a default value for the situation when it is missing. + + Arguments: + mnemonic (str): mnemonic of item to retrieve + default (str, HeaderItem, or CurveItem): default to provide + if *mnemonic* is missing from the section. If a string is + provided, it will be used as the ``value`` attribute of a new + HeaderItem or the ``descr`` attribute of a new CurveItem. + add (bool): if True, the returned HeaderItem/CurveItem will also + be appended to the SectionItems. By default this is not done. + + Returns: + :class:`lasio.HeaderItem`/:class:`lasio.CurveItem`: item from + the section, if it is in there, or + a new item, if it is not. If a CurveItem is returned, the + ``data`` attribute will contain ``numpy.nan`` values. + + """ + if mnemonic in self: + return self[mnemonic] + else: + if not ( + isinstance(default, HeaderItem) + or isinstance(default, CurveItem) + ): + default = str(default) + # Determine appropriate type of item to create (HeaderItem + # or CurveItem). + + if len(self): + first_item = self[0] + item_type = type(first_item) + else: + item_type = HeaderItem + + if item_type is CurveItem: + new_data = np.asarray(first_item.data) + new_data = new_data * np.nan + + item = CurveItem( + mnemonic=mnemonic, + descr=default, + data=new_data + ) + else: + item = HeaderItem( + mnemonic=mnemonic, + value=default + ) + else: + assert type(default) in (HeaderItem, CurveItem) + + item = type(default)( + mnemonic=mnemonic, + unit=default.unit, + value=default.value, + descr=default.descr + ) + if type(item) is CurveItem: + item.data = np.array(default.data) + + if add: + self.append(item) + return item + def __delitem__(self, key): """Delete item by either mnemonic or index. diff --git a/tests/test_sectionitems.py b/tests/test_sectionitems.py index 5ff71ad..252e0e0 100644 --- a/tests/test_sectionitems.py +++ b/tests/test_sectionitems.py @@ -154,3 +154,44 @@ def test_mnemonic_rename_1(): las = lasio.read(egfn("sample.las")) las.curves[-1].mnemonic = "" assert las.curves[-1].mnemonic == "UNKNOWN" + + +def test_get_exists(): + sitems = lasio.SectionItems() + sitems.append( + lasio.HeaderItem('WELL', value='1') + ) + item = sitems.get('WELL', default='2') + assert item.value == '1' + +def test_get_missing_default_str(): + sitems = lasio.SectionItems() + item = sitems.get('WELL', default='2') + assert item.value == '2' + +def test_get_missing_default_int(): + sitems = lasio.SectionItems() + item = sitems.get('WELL', default=2) + assert item.value == '2' + +def test_get_missing_default_item(): + sitems = lasio.SectionItems() + item = sitems.get('WELL', default=lasio.HeaderItem(mnemonic='XXX', value='3')) + assert item.mnemonic == 'WELL' + assert item.value == '3' + +def test_get_missing_curveitem(): + sitems = lasio.SectionItems() + sitems.append( + lasio.CurveItem('DEPT', data=[1, 2, 3]) + ) + item = sitems.get('GAMMA') + assert type(item) is lasio.CurveItem + assert np.isnan(item.data).all() + +def test_get_missing_add(): + sitems = lasio.SectionItems() + item = sitems.get('WELL', default='3', add=True) + existing_item = sitems[0] + assert existing_item.mnemonic == 'WELL' + assert existing_item.value == '3' \ No newline at end of file