"""Reproduce a file descriptor leak, then compare scoped file reads on Linux."""
import argparse
import os
from pathlib import Path
import resource
import sys
import time


def main():
    parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
    parser.add_argument("file", type=Path, help="existing input file, read only")
    parser.add_argument("--mode", choices=("leak", "fixed"), default="fixed")
    parser.add_argument("--iterations", type=int, default=200)
    parser.add_argument("--hold", type=int, default=0, help="inspection pause, 0-60 seconds")
    args = parser.parse_args()
    if not 1 <= args.iterations <= 10000:
        parser.error("iterations must be from 1 to 10000")
    if not 0 <= args.hold <= 60:
        parser.error("hold must be from 0 to 60")

    soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
    finite = [64] + [n for n in (soft, hard) if n != resource.RLIM_INFINITY]
    limit = min(finite)
    if limit < 16:
        parser.error("demo needs an existing descriptor limit of at least 16")
    try:
        resource.setrlimit(resource.RLIMIT_NOFILE, (limit, hard))
    except (OSError, ValueError) as exc:
        print(f"ERROR: cannot set demo limit: {exc}", file=sys.stderr, flush=True)
        return 1
    print(f"pid={os.getpid()} mode={args.mode} soft={limit} hard={hard}", flush=True)

    retained = []
    processed = status = 0
    try:
        try:
            for _ in range(args.iterations):
                if args.mode == "leak":
                    file = args.file.open("rb")
                    retained.append(file)
                    file.read(1)
                else:
                    with args.file.open("rb") as file:
                        file.read(1)
                processed += 1
        except OSError as exc:
            print(f"ERROR: errno={exc.errno}: {exc}", file=sys.stderr, flush=True)
            status = 1
        print(f"processed={processed} retained={len(retained)} status={status}", flush=True)
        if args.hold:
            print(f"READY: holding for {args.hold}s", flush=True)
            time.sleep(args.hold)
    finally:
        for file in retained:
            file.close()
    return status


if __name__ == "__main__":
    raise SystemExit(main())
