# Buggy code
def read_procfs():
    # OK, this is a bit confusing. The format of /proc/diskstats can
    # have 3 variations.
    # On Linux 2.4 each line has always 15 fields, e.g.:
    # "3     0   8 hda 8 8 8 8 8 8 8 8 8 8 8"
    # On Linux 2.6+ each line *usually* has 14 fields, and the disk
    # name is in another position, like this:
    # "3    0   hda 8 8 8 8 8 8 8 8 8 8 8"
    # ...unless (Linux 2.6) the line refers to a partition instead
    # of a disk, in which case the line has less fields (7):
    # "3    1   hda1 8 8 8 8"
    # See:
    # https://www.kernel.org/doc/Documentation/iostats.txt
    # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
    with open_text("%s/diskstats" % get_procfs_path()) as f:
        lines = f.readlines()
    for line in lines:
        fields = line.split()
        flen = len(fields)
        if flen == 15:
            # Linux 2.4
            name = fields[3]
            reads = int(fields[2])
            (
                reads_merged,
                rbytes,
                rtime,
                writes,
                writes_merged,
                wbytes,
                wtime,
                _,
                busy_time,
                _,
            ) = map(int, fields[4:14])
        elif flen == 14:
            # Linux 2.6+, line referring to a disk
            name = fields[2]
            (
                reads,
                reads_merged,
                rbytes,
                rtime,
                writes,
                writes_merged,
                wbytes,
                wtime,
                _,
                busy_time,
                _,
            ) = map(int, fields[3:14])
        elif flen == 7:
            # Linux 2.6+, line referring to a partition
            name = fields[2]
            reads, rbytes, writes, wbytes = map(int, fields[3:])
            rtime = wtime = reads_merged = writes_merged = busy_time = 0
        else:
            raise ValueError("not sure how to interpret line %r" % line)
        yield (
            name,
            reads,
            writes,
            rbytes,
            wbytes,
            rtime,
            wtime,
            reads_merged,
            writes_merged,
            busy_time,
        )


# Traceback (ValueError)
(psutil_venv) -bash-4.2$ python
Python 3.6.2 (default, Apr 24 2018, 04:27:15)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
import psutil
psutil.__version__
'5.4.7'
psutil.disk_io_counters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/9ld/psutil_venv/lib/python3.6/site-packages/psutil/__init__.py", line 2017, in disk_io_counters
rawdict = _psplatform.disk_io_counters(**kwargs)
File "/home/9ld/psutil_venv/lib/python3.6/site-packages/psutil/_pslinux.py", line 1117, in disk_io_counters
for entry in gen:
File "/home/9ld/psutil_venv/lib/python3.6/site-packages/psutil/_pslinux.py", line 1090, in read_procfs
raise ValueError("not sure how to interpret line %r" % line)
ValueError: not sure how to interpret line '   8      16 sdb 147 0 9545 368 3 0 4096 17 0 352 385 0 0 0 0\n'

# Fixed code
def read_procfs():
    # OK, this is a bit confusing. The format of /proc/diskstats can
    # have 3 variations.
    # On Linux 2.4 each line has always 15 fields, e.g.:
    # "3     0   8 hda 8 8 8 8 8 8 8 8 8 8 8"
    # On Linux 2.6+ each line *usually* has 14 fields, and the disk
    # name is in another position, like this:
    # "3    0   hda 8 8 8 8 8 8 8 8 8 8 8"
    # ...unless (Linux 2.6) the line refers to a partition instead
    # of a disk, in which case the line has less fields (7):
    # "3    1   hda1 8 8 8 8"
    # 4.18+ has 4 fields added:
    # "3    0   hda 8 8 8 8 8 8 8 8 8 8 8 0 0 0 0"
    # See:
    # https://www.kernel.org/doc/Documentation/iostats.txt
    # https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
    with open_text("%s/diskstats" % get_procfs_path()) as f:
        lines = f.readlines()
    for line in lines:
        fields = line.split()
        flen = len(fields)
        if flen == 15:
            # Linux 2.4
            name = fields[3]
            reads = int(fields[2])
            (
                reads_merged,
                rbytes,
                rtime,
                writes,
                writes_merged,
                wbytes,
                wtime,
                _,
                busy_time,
                _,
            ) = map(int, fields[4:14])
        elif flen == 14 or flen == 18:
            # Linux 2.6+, line referring to a disk
            name = fields[2]
            (
                reads,
                reads_merged,
                rbytes,
                rtime,
                writes,
                writes_merged,
                wbytes,
                wtime,
                _,
                busy_time,
                _,
            ) = map(int, fields[3:14])
        elif flen == 7:
            # Linux 2.6+, line referring to a partition
            name = fields[2]
            reads, rbytes, writes, wbytes = map(int, fields[3:])
            rtime = wtime = reads_merged = writes_merged = busy_time = 0
        else:
            raise ValueError("not sure how to interpret line %r" % line)
        yield (
            name,
            reads,
            writes,
            rbytes,
            wbytes,
            rtime,
            wtime,
            reads_merged,
            writes_merged,
            busy_time,
        )