File handling klyn.io Paths

# File Handling with klyn.io

The `klyn.io` package provides path abstractions, text readers and writers, byte streams, and file/folder helpers. It is the foundation used by CSV, JSON, XML, archive, and XLSX APIs.

Imports

Only the base `klyn` package is imported automatically. File code must import `klyn.io` explicitly.

    import klyn.io

Paths

`Path` represents a filesystem location. Use `FilePath` when you know the target is a file, and `FolderPath` when you know it is a directory. The static properties `Path.cwd`, `Path.userHome`, `Path.klynHome`, and `Path.tempDir` are convenient anchors for portable code.

    import klyn.io

    root = Path.cwd
    logs = FolderPath(root, "logs")
    report = FilePath(logs, "startup.log")

    print(root.toString())
    print(report.parent())

Prefer Path APIs over string concatenation

Path helpers keep separators, normalization, and platform-specific filesystem rules in one place. Build paths with constructors, `resolve`, `Path.file`, or `Path.folder` instead of manually appending `/` or `\`.

Text Files

For concise full-file text operations, `Path.read()` and `Path.write(...)` read or replace the complete file content. For explicit resource control, streaming, or line-by-line processing, use `FileReader` and `FileWriter`.

    import klyn.io

    file = Path.tempDir.resolve("klyn-notes.txt")
    try:
        file.write("First line\nSecond line")
        print(file.read())
    finally:
        if file.exists:
            file.remove()

The next form is more verbose but gives explicit control over resource lifetime. A `FileWriter` truncates the target when it opens, then `FileReader` reads the content back. The nested `try writer = ...:` and `try reader = ...:` blocks close the resources deterministically.

    import klyn.io

    file = Path.tempDir.resolve("klyn-notes.txt") as FilePath
    try:
        try writer = FileWriter(file):
            writer.write("First line\nSecond line")

        assert file.exists
        assert file.isRegular

        try reader = FileReader(file):
            print(reader.readAllText())
    finally:
        if file.exists:
            file.remove()

For line-oriented files, keep the reader and writer APIs. `writeLine(...)` appends the platform line terminator, and `readLines()` returns the lines from the current reader position. This is the preferred style for logs, configuration files, and data formats that are naturally processed one line at a time.

    import klyn.io

    path = Path.tempDir.resolve("klyn-lines.txt")
    try:
        try writer = FileWriter(path):
            writer.writeLine("alpha")
            writer.writeLine("beta")

        try reader = FileReader(path):
            for line in reader.readLines():
                print(line)
    finally:
        if path.exists:
            path.remove()

Folders

`FolderPath` can create one directory with `mkdir()`, create a whole tree with `mkdirs()`, list children, and remove a tree with `removeAll()`.

    import klyn.io

    tmp = FolderPath(Path.tempDir, "klyn-export", "nested")
    try:
        tmp.mkdirs()
        try writer = FileWriter(FilePath(tmp, "data.txt")):
            writer.write("payload")

        for name in tmp.listNames():
            print(name)
    finally:
        if tmp.exists:
            tmp.removeAll()

File Watcher

`FileWatcher` observes a single file and emits typed events when its existence, size, or modification timestamp changes. Its events carry a `FileWatcherEvent` payload. The watcher starts its background polling thread immediately after construction.

    import klyn.io

    watcher = FileWatcher("app.log", 250)
    watcher.created += lambda(e: FileWatcherEvent): print("created: " + e.path)
    watcher.updated += lambda(e: FileWatcherEvent): print("updated: " + e.path)
    watcher.deleted += lambda(e: FileWatcherEvent): print("deleted: " + e.path)

    # Later, when the watcher is no longer needed:
    watcher.close()

The generic `changed` event receives every change kind. For deterministic code paths such as command-line tools, stop and join the automatically started thread, then call `poll()` manually.

    import klyn.io

    file = Path.tempDir.resolve("watched.txt")
    watcher = FileWatcher(file)
    watcher.stop()
    watcher.join()
    watcher.changed += lambda(e: FileWatcherEvent): print(e.kind)

    file.write("first")
    watcher.poll()

    file.write("second")
    watcher.poll()

Resource Safety

`Path.read()` and `Path.write(...)` are convenient full-file helpers. Readers, writers, and streams should still be opened with `try resource = ...:` when you need incremental I/O, so the runtime closes them even if an exception is raised. `FileReader`, `FileWriter`, `FileInputStream`, and `FileOutputStream` provide that explicit control.