# Buggy code
def disk_io_counters(perdisk=False):
    """Return disk I/O statistics for every disk installed on the
    system as a dict of raw tuples.
    """

    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,
            )

    def read_sysfs():
        for block in os.listdir("/sys/block"):
            for root, _, files in os.walk(os.path.join("/sys/block", block)):
                if "stat" not in files:
                    continue
                with open_text(os.path.join(root, "stat")) as f:
                    fields = f.read().strip().split()
                name = os.path.basename(root)
                (
                    reads,
                    reads_merged,
                    rbytes,
                    rtime,
                    writes,
                    writes_merged,
                    wbytes,
                    wtime,
                    _,
                    busy_time,
                    _,
                ) = map(int, fields)
                yield (
                    name,
                    reads,
                    writes,
                    rbytes,
                    wbytes,
                    rtime,
                    wtime,
                    reads_merged,
                    writes_merged,
                    busy_time,
                )

    if os.path.exists("%s/diskstats" % get_procfs_path()):
        gen = read_procfs()
    elif os.path.exists("/sys/block"):
        gen = read_sysfs()
    else:
        raise NotImplementedError(
            "%s/diskstats nor /sys/block filesystem are available on this "
            "system" % get_procfs_path()
        )

    retdict = {}
    for entry in gen:
        (
            name,
            reads,
            writes,
            rbytes,
            wbytes,
            rtime,
            wtime,
            reads_merged,
            writes_merged,
            busy_time,
        ) = entry
        if not perdisk and not is_storage_device(name):
            # perdisk=False means we want to calculate totals so we skip
            # partitions (e.g. 'sda1', 'nvme0n1p1') and only include
            # base disk devices (e.g. 'sda', 'nvme0n1'). Base disks
            # include a total of all their partitions + some extra size
            # of their own:
            #     $ cat /proc/diskstats
            #     259       0 sda 10485760 ...
            #     259       1 sda1 5186039 ...
            #     259       1 sda2 5082039 ...
            # See:
            # https://github.com/giampaolo/psutil/pull/1313
            continue

        rbytes *= DISK_SECTOR_SIZE
        wbytes *= DISK_SECTOR_SIZE
        retdict[name] = (
            reads,
            writes,
            rbytes,
            wbytes,
            rtime,
            wtime,
            reads_merged,
            writes_merged,
            busy_time,
        )

    return retdict


# 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 disk_io_counters(perdisk=False):
    """Return disk I/O statistics for every disk installed on the
    system as a dict of raw tuples.
    """

    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,
            )

    def read_sysfs():
        for block in os.listdir("/sys/block"):
            for root, _, files in os.walk(os.path.join("/sys/block", block)):
                if "stat" not in files:
                    continue
                with open_text(os.path.join(root, "stat")) as f:
                    fields = f.read().strip().split()
                name = os.path.basename(root)
                (
                    reads,
                    reads_merged,
                    rbytes,
                    rtime,
                    writes,
                    writes_merged,
                    wbytes,
                    wtime,
                    _,
                    busy_time,
                    _,
                ) = map(int, fields)
                yield (
                    name,
                    reads,
                    writes,
                    rbytes,
                    wbytes,
                    rtime,
                    wtime,
                    reads_merged,
                    writes_merged,
                    busy_time,
                )

    if os.path.exists("%s/diskstats" % get_procfs_path()):
        gen = read_procfs()
    elif os.path.exists("/sys/block"):
        gen = read_sysfs()
    else:
        raise NotImplementedError(
            "%s/diskstats nor /sys/block filesystem are available on this "
            "system" % get_procfs_path()
        )

    retdict = {}
    for entry in gen:
        (
            name,
            reads,
            writes,
            rbytes,
            wbytes,
            rtime,
            wtime,
            reads_merged,
            writes_merged,
            busy_time,
        ) = entry
        if not perdisk and not is_storage_device(name):
            # perdisk=False means we want to calculate totals so we skip
            # partitions (e.g. 'sda1', 'nvme0n1p1') and only include
            # base disk devices (e.g. 'sda', 'nvme0n1'). Base disks
            # include a total of all their partitions + some extra size
            # of their own:
            #     $ cat /proc/diskstats
            #     259       0 sda 10485760 ...
            #     259       1 sda1 5186039 ...
            #     259       1 sda2 5082039 ...
            # See:
            # https://github.com/giampaolo/psutil/pull/1313
            continue

        rbytes *= DISK_SECTOR_SIZE
        wbytes *= DISK_SECTOR_SIZE
        retdict[name] = (
            reads,
            writes,
            rbytes,
            wbytes,
            rtime,
            wtime,
            reads_merged,
            writes_merged,
            busy_time,
        )

    return retdict