diff --git a/pvl/_collections.py b/pvl/_collections.py
index 2abaf3a..dcb4b91 100644
--- a/pvl/_collections.py
+++ b/pvl/_collections.py
@@ -41,6 +41,10 @@ class KeysView(MappingView):
         keys = [key for key, _ in self._mapping]
         return '%s(%r)' % (type(self).__name__, keys)
 
+    def index(self, key):
+        keys = [k for k, _ in self._mapping]
+        return keys.index(key)
+
 
 class ItemsView(MappingView):
     def __contains__(self, item):
@@ -54,6 +58,10 @@ class ItemsView(MappingView):
     def __getitem__(self, index):
         return self._mapping[index]
 
+    def index(self, item):
+        items = [i for i in self._mapping]
+        return items.index(item)
+
 
 class ValuesView(MappingView):
     def __contains__(self, value):
@@ -73,6 +81,10 @@ class ValuesView(MappingView):
         values = [value for _, value in self._mapping]
         return '%s(%r)' % (type(self).__name__, values)
 
+    def index(self, value):
+        values = [val for _, val in self._mapping]
+        return values.index(value)
+
 
 class OrderedMultiDict(dict, MutableMapping):
     """A ``dict`` like container.
@@ -250,6 +262,67 @@ class OrderedMultiDict(dict, MutableMapping):
     def copy(self):
         return type(self)(self)
 
+    def __insert_wrapper(func):
+        """Make sure the arguments given to the insert methods are correct"""
+        def check_func(self, key, new_item, instance=0):
+            if key not in self.keys():
+                raise KeyError("%s not a key in label" % (key))
+            if not isinstance(new_item, (list, OrderedMultiDict)):
+                raise TypeError("The new item must be a list or PVLModule")
+            if isinstance(new_item, OrderedMultiDict):
+                new_item = list(new_item)
+            return func(self, key, new_item, instance)
+        return check_func
+
+    def _get_index_for_insert(self, key, instance):
+        """Get the index of the key to insert before or after"""
+        if instance == 0:
+            # Index method will return the first occurence of the key
+            index = self.keys().index(key)
+        else:
+            occurrence = -1
+            for index, k in enumerate(self.keys()):
+                if k == key:
+                    occurrence += 1
+                    if occurrence == instance:
+                        # Found the key and the correct occurence of the key
+                        break
+
+            if occurrence != instance:
+                # Gone through the entire list of keys and the instance number
+                # given is too high for the number of occurences of the key
+                raise ValueError(
+                    (
+                        "Cannot insert before/after the %d "
+                        "instance of the key '%s' since there are "
+                        "only %d occurences of the key" % (
+                            instance, key, occurrence)
+                    ))
+        return index
+
+    def _insert_item(self, key, new_item, instance, is_after):
+        """Insert a new item before or after another item"""
+        index = self._get_index_for_insert(key, instance)
+        index = index + 1 if is_after else index
+        self.__items = self.__items[:index] + new_item + self.__items[index:]
+        # Make sure indexing works with new items
+        for new_key, new_value in new_item:
+            if new_key in self:
+                value_list = [val for k, val in self.__items if k == new_key]
+                dict_setitem(self, new_key, value_list)
+            else:
+                dict_setitem(self, new_key, [new_value])
+
+    @__insert_wrapper
+    def insert_after(self, key, new_item, instance=0):
+        """Insert an item after a key"""
+        self._insert_item(key, new_item, instance, True)
+
+    @__insert_wrapper
+    def insert_before(self, key, new_item, instance=0):
+        """Insert an item before a key"""
+        self._insert_item(key, new_item, instance, False)
+
 
 class PVLModule(OrderedMultiDict):
     pass

Inserting key/value pairs in PDS label
Is there a way to insert desired key/value lines at a specific point in a PDS label?

ex:
from
```
PDS_VERSION_ID = 
DD_VERSION_ID = 
LABEL_REVISION_NOTE =
^IMAGE
```

to
```
PDS_VERSION_ID = 
DD_VERSION_ID = 
LABEL_REVISION_NOTE =
DATA_SET_ID =
PRODUCT_ID = 
INSTRUMENT_HOST_NAME = 
^IMAGE
```
diff --git a/tests/test_collections.py b/tests/test_collections.py
index 50df73d..18b5a4d 100644
--- a/tests/test_collections.py
+++ b/tests/test_collections.py
@@ -358,14 +358,25 @@ def test_py2_items():
         ('a', 3),
     ])
 
-    assert isinstance(module.items(), list)
-    assert module.items() == [('a', 1), ('b', 2), ('a', 3)]
-
-    assert isinstance(module.keys(), list)
-    assert module.keys() == ['a', 'b', 'a']
-
-    assert isinstance(module.values(), list)
-    assert module.values() == [1, 2, 3]
+    items = module.items()
+    assert isinstance(items, list)
+    assert items == [('a', 1), ('b', 2), ('a', 3)]
+    assert items.index(('a', 1)) == 0
+    assert items.index(('b', 2)) == 1
+    assert items.index(('a', 3)) == 2
+
+    keys = module.keys()
+    assert isinstance(keys, list)
+    assert keys == ['a', 'b', 'a']
+    assert keys.index('a') == 0
+    assert keys.index('b') == 1
+
+    values = module.values()
+    assert isinstance(values, list)
+    assert values == [1, 2, 3]
+    assert values.index(1) == 0
+    assert values.index(2) == 1
+    assert values.index(3) == 2
 
 
 @pytest.mark.skipif(six.PY2, reason='requires python3')
@@ -391,14 +402,30 @@ def test_py3_items():
     ])
 
     assert isinstance(module.items(), pvl._collections.ItemsView)
-    assert module.items()[0] == ('a', 1)
+    items = module.items()
+    assert items[0] == ('a', 1)
+    assert items[1] == ('b', 2)
+    assert items[2] == ('a', 3)
+    assert items.index(('a', 1)) == 0
+    assert items.index(('b', 2)) == 1
+    assert items.index(('a', 3)) == 2
 
     assert isinstance(module.keys(), pvl._collections.KeysView)
-    assert module.keys()[0] == 'a'
+    keys = module.keys()
+    assert keys[0] == 'a'
+    assert keys[1] == 'b'
+    assert keys[2] == 'a'
+    assert keys.index('a') == 0
+    assert keys.index('b') == 1
 
     assert isinstance(module.values(), pvl._collections.ValuesView)
-    assert module.values()[0] == 1
-
+    values = module.values()
+    assert values[0] == 1
+    assert values[1] == 2
+    assert values[2] == 3
+    assert values.index(1) == 0
+    assert values.index(2) == 1
+    assert values.index(3) == 2
 
 
 if six.PY3:
@@ -549,3 +576,153 @@ def test_conversion():
 
     assert dict(module) == expected_dict
     assert list(module) == expected_list
+
+
+@pytest.mark.parametrize(
+    'expected_label, key, instance, expected_list, expected_value', [
+        ([
+            ('a', 4),
+            ('a', 1),
+            ('b', 2),
+            ('a', 3),
+            ('c', 5),
+        ], 'a', 0, [4, 1, 3], 4),
+        ([
+            ('a', 1),
+            ('a', 4),
+            ('b', 2),
+            ('a', 3),
+            ('c', 5),
+        ], 'b', 0, [1, 4, 3], 1),
+        ([
+            ('a', 1),
+            ('b', 2),
+            ('a', 4),
+            ('a', 3),
+            ('c', 5),
+        ], 'a', 1, [1, 4, 3], 1),
+        ([
+            ('a', 1),
+            ('b', 2),
+            ('a', 3),
+            ('a', 4),
+            ('c', 5),
+        ], 'c', 0, [1, 3, 4], 1)
+    ])
+def test_insert_before(expected_label, key, instance, expected_list,
+                        expected_value):
+    module1 = pvl.PVLModule([
+        ('a', 1),
+        ('b', 2),
+        ('a', 3),
+        ('c', 5),
+    ])
+    module2 = module1.copy()
+
+    expected_module = pvl.PVLModule(expected_label)
+
+    module1.insert_before(key, [('a', 4)], instance)
+    assert expected_module == module1
+    assert module1['a'] == expected_value
+    assert module1.getlist('a') == expected_list
+
+    module2.insert_before(key, pvl.PVLModule([('a', 4)]), instance)
+    assert module2 == expected_module
+    assert module1['a'] == expected_value
+    assert module1.getlist('a') == expected_list
+
+
+@pytest.mark.parametrize(
+    'expected_label, key, instance, expected_list, expected_value', [
+        ([
+            ('a', 1),
+            ('a', 4),
+            ('b', 2),
+            ('a', 3),
+            ('c', 5),
+        ], 'a', 0, [1, 4, 3], 1),
+        ([
+            ('a', 1),
+            ('b', 2),
+            ('a', 4),
+            ('a', 3),
+            ('c', 5),
+        ], 'b', 0, [1, 4, 3], 1),
+        ([
+            ('a', 1),
+            ('b', 2),
+            ('a', 3),
+            ('a', 4),
+            ('c', 5),
+        ], 'a', 1, [1, 3, 4], 1),
+        ([
+            ('a', 1),
+            ('b', 2),
+            ('a', 3),
+            ('c', 5),
+            ('a', 4),
+        ], 'c', 0, [1, 3, 4], 1)
+    ])
+def test_insert_after(expected_label, key, instance, expected_list,
+                        expected_value):
+    module1 = pvl.PVLModule([
+        ('a', 1),
+        ('b', 2),
+        ('a', 3),
+        ('c', 5),
+    ])
+    module2 = module1.copy()
+
+    expected_module = pvl.PVLModule(expected_label)
+
+    module1.insert_after(key, [('a', 4)], instance)
+    assert expected_module == module1
+    assert module1['a'] == expected_value
+    assert module1.getlist('a') == expected_list
+
+    module2.insert_after(key, pvl.PVLModule([('a', 4)]), instance)
+    assert module2 == expected_module
+    assert module1['a'] == expected_value
+    assert module1.getlist('a') == expected_list
+
+
+@pytest.mark.parametrize(
+    'key, instance, expected_index', [
+        ('a', 0, 0),
+        ('b', 0, 1),
+        ('a', 1, 2)
+    ])
+def test_get_index_for_insert(key, instance, expected_index):
+    module = pvl.PVLModule([
+        ('a', 1),
+        ('b', 2),
+        ('a', 3),
+    ])
+
+    module._get_index_for_insert(key, instance) == expected_index
+
+
+def test_insert_raises():
+    module = pvl.PVLModule([
+        ('a', 1),
+        ('b', 2),
+        ('a', 3),
+    ])
+
+    with pytest.raises(KeyError):
+        module.insert_before('error_key', [('foo', 'bar')])
+
+    with pytest.raises(KeyError):
+        module.insert_after('error_key', [('foo', 'bar')])
+
+    with pytest.raises(TypeError):
+        module.insert_before('a', ('foo', 'bar'))
+
+    with pytest.raises(TypeError):
+        module.insert_after('a', ('foo', 'bar'))
+
+    with pytest.raises(ValueError):
+        module.insert_before('a', [('foo', 'bar')], 2)
+
+    with pytest.raises(ValueError):
+        module.insert_after('a', [('foo', 'bar')], 2)
diff --git a/tests/test_encoder.py b/tests/test_encoder.py
index 1dee8ff..76fbf1e 100644
--- a/tests/test_encoder.py
+++ b/tests/test_encoder.py
@@ -111,3 +111,33 @@ def test_quoated_strings():
 
     encoder = pvl.encoder.PDSLabelEncoder
     assert module == pvl.loads(pvl.dumps(module, cls=encoder))
+
+
+def test_dump_to_file_insert_before():
+    tmpdir = tempfile.mkdtemp()
+
+    try:
+        for filename in PDS_LABELS:
+            label = pvl.load(filename)
+            if os.path.basename(filename) != 'empty.lbl':
+                label.insert_before('PDS_VERSION_ID', [('new', 'item')])
+            tmpfile = os.path.join(tmpdir, os.path.basename(filename))
+            pvl.dump(label, tmpfile)
+            assert label == pvl.load(tmpfile)
+    finally:
+        shutil.rmtree(tmpdir)
+
+
+def test_dump_to_file_insert_after():
+    tmpdir = tempfile.mkdtemp()
+
+    try:
+        for filename in PDS_LABELS:
+            label = pvl.load(filename)
+            if os.path.basename(filename) != 'empty.lbl':
+                label.insert_after('PDS_VERSION_ID', [('new', 'item')])
+            tmpfile = os.path.join(tmpdir, os.path.basename(filename))
+            pvl.dump(label, tmpfile)
+            assert label == pvl.load(tmpfile)
+    finally:
+        shutil.rmtree(tmpdir)

pip install -e .
parse_log_pytest
requirements.txt
pytest pytest-cov flake8
apt-get update apt-get install -y gcc
3.5
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
coverage==6.2
distlib==0.3.9
filelock==3.4.1
flake8==5.0.4
importlib-metadata==4.2.0
importlib-resources==5.4.0
iniconfig==1.1.1
mccabe==0.7.0
packaging==21.3
platformdirs==2.4.0
pluggy==1.0.0
-e git+https://github.com/planetarypy/pvl.git@27b4128d10b4dcc0f14c64fcc61bedc2e27030bf#egg=pvl
py==1.11.0
pycodestyle==2.9.1
pyflakes==2.5.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
pytz==2025.2
six==1.17.0
toml==0.10.2
tomli==1.2.3
tox==3.28.0
typing_extensions==4.1.1
virtualenv==20.16.2
zipp==3.6.0

name: pvl
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - coverage==6.2
      - distlib==0.3.9
      - filelock==3.4.1
      - flake8==5.0.4
      - importlib-metadata==4.2.0
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - mccabe==0.7.0
      - packaging==21.3
      - platformdirs==2.4.0
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytz==2025.2
      - six==1.17.0
      - toml==0.10.2
      - tomli==1.2.3
      - tox==3.28.0
      - typing-extensions==4.1.1
      - virtualenv==20.16.2
      - zipp==3.6.0
prefix: /opt/conda/envs/pvl

tests/test_collections.py::test_py3_items tests/test_collections.py::test_insert_before[expected_label0-a-0-expected_list0-4] tests/test_collections.py::test_insert_before[expected_label1-b-0-expected_list1-1] tests/test_collections.py::test_insert_before[expected_label2-a-1-expected_list2-1] tests/test_collections.py::test_insert_before[expected_label3-c-0-expected_list3-1] tests/test_collections.py::test_insert_after[expected_label0-a-0-expected_list0-1] tests/test_collections.py::test_insert_after[expected_label1-b-0-expected_list1-1] tests/test_collections.py::test_insert_after[expected_label2-a-1-expected_list2-1] tests/test_collections.py::test_insert_after[expected_label3-c-0-expected_list3-1] tests/test_collections.py::test_get_index_for_insert[a-0-0] tests/test_collections.py::test_get_index_for_insert[b-0-1] tests/test_collections.py::test_get_index_for_insert[a-1-2] tests/test_collections.py::test_insert_raises tests/test_encoder.py::test_dump_to_file_insert_before tests/test_encoder.py::test_dump_to_file_insert_after
tests/test_collections.py::test_conversion
tests/test_collections.py::test_empty tests/test_collections.py::test_list_creation tests/test_collections.py::test_dict_creation tests/test_collections.py::test_keyword_creation tests/test_collections.py::test_key_access tests/test_collections.py::test_index_access tests/test_collections.py::test_slice_access tests/test_collections.py::test_set tests/test_collections.py::test_delete tests/test_collections.py::test_clear tests/test_collections.py::test_discard tests/test_collections.py::test_pop tests/test_collections.py::test_popitem tests/test_collections.py::test_update tests/test_collections.py::test_append tests/test_collections.py::test_len tests/test_collections.py::test_repr tests/test_collections.py::test_iterators tests/test_collections.py::test_equlity tests/test_collections.py::test_copy tests/test_encoder.py::test_dump_stream tests/test_encoder.py::test_dump_to_file tests/test_encoder.py::test_default_encoder tests/test_encoder.py::test_cube_encoder tests/test_encoder.py::test_pds_encoder tests/test_encoder.py::test_special_values tests/test_encoder.py::test_special_strings tests/test_encoder.py::test_unkown_value tests/test_encoder.py::test_quoated_strings
pvl/_collections.py
pvl/_collections.py