hankcs/HanLP · error · ValueError

Expected a file (`.fileno()`) or a file descriptor

Error message

Expected a file (`.fileno()`) or a file descriptor

What it means

fileno() normalizes files and file descriptors for stdout redirection; after calling .fileno() on the object, the result must be an int fd. If the object exposes a fileno() that returns something else (or a non-file, non-int object sneaks through), it raises ValueError.

Source

Thrown at hanlp/utils/io_util.py:539

            if r[2 * sid] <= ratio < r[2 * sid + 1]:
                if isinstance(sample, list):
                    sample = '\n'.join('\t'.join(x) for x in sample) + '\n\n'
                out.write(sample)
                break
    if not filepath.endswith('.tsv'):
        src.close()
    for out in outs:
        out.close()
    return filenames


def fileno(file_or_fd):
    try:
        fd = getattr(file_or_fd, 'fileno', lambda: file_or_fd)()
    except:
        return None
    if not isinstance(fd, int):
        raise ValueError("Expected a file (`.fileno()`) or a file descriptor")
    return fd


@contextmanager
def stdout_redirected(to=os.devnull, stdout=None):
    """Redirect stdout to else where.
    Copied from https://stackoverflow.com/questions/4675728/redirect-stdout-to-a-file-in-python/22434262#22434262

    Args:
      to:  Target device.
      stdout:  Source device.

    """
    if windows():  # This doesn't play well with windows
        yield None
        return
    if stdout is None:
        stdout = sys.stdout

View on GitHub (pinned to ddb1299bdd)

Solutions

  1. Pass a real file object or an integer fd to stdout_redirected.
  2. For in-memory capture, create a real temp file (open(tempfile.mkstemp()[1])) or use contextlib.redirect_stdout instead.
  3. If wrapping, ensure your wrapper's fileno() delegates to the underlying file's fileno.

Example fix

# before
with stdout_redirected(io.StringIO()): ...
# after
import tempfile, os
fd, tmp = tempfile.mkstemp()
with stdout_redirected(os.fdopen(fd, 'w')): ...
os.remove(tmp)
Defensive patterns

Strategy: type-guard

Validate before calling

fd = getattr(f, 'fileno', lambda: f)()
assert isinstance(fd, int) and fd >= 0, 'need a real file or fd'

Type guard

def is_real_file(f):
    fd = getattr(f, 'fileno', lambda: None)()
    return isinstance(fd, int) and fd >= 0

Prevention

When it happens

Trigger: Passing an object to fileno()/stdout_redirected/_get_file_size whose fileno() returns a non-int (e.g. a mock, io.StringIO in some wrappers returning None, or an object whose __getattr__ yields junk).

Common situations: Redirecting stdout to a StringIO/captured buffer in tests; substituting fake file objects; Python 2/3 differences in file-like wrappers.


AI-assisted analysis of hankcs/HanLP@ddb1299bdd (2026-08-27). Data as JSON: /api/errors/db705d8bd5a3b6df. Report an issue: GitHub.