File I/O in Incan¶
Use std.fs.Path as the entry point for files and directories. File operations return Result, so handle recoverable failures with match and propagate boundary failures with ? from functions that return Result.
from std.fs import IoError, Path
Build Paths¶
from std.fs import Path
def config_path(root: Path) -> Path:
return root / "config.toml"
def artifact_path(root: Path, name: str) -> Path:
return root.joinpath(name)
Path construction and joins are lexical. They do not prove that anything exists on disk.
Check Existence¶
Use bool predicates for quick branches:
from std.fs import Path
def has_cache(root: Path) -> bool:
return root.joinpath("cache.bin").is_file()
Use try_exists() when the difference between "missing" and "the check failed" matters:
from std.fs import IoError, Path
def require_input(path: Path) -> Result[Path, IoError]:
if path.try_exists()?:
return Ok(path)
return Err(IoError(path=path, kind="not_found", detail="input path does not exist"))
Create Directories¶
from std.fs import IoError, Path
def prepare_output_dir(path: Path) -> Result[None, IoError]:
path.mkdir(parents=True, exist_ok=True)?
return Ok(None)
parents=True creates missing parent directories. exist_ok=True treats an existing directory as success.
Read and Write Small Files¶
Whole-file helpers are convenient for configuration files, test fixtures, and payloads that comfortably fit in memory.
from std.fs import IoError, Path
def copy_small_file(source: Path, target: Path) -> Result[None, IoError]:
data = source.read_bytes()?
target.write_bytes(data)?
return Ok(None)
def save_text(path: Path, text: str) -> Result[None, IoError]:
path.write_text(text, "utf-8", "strict", None)?
return Ok(None)
Use read_text("utf-8", "strict") and write_text(..., "utf-8", "strict", None) for normal UTF-8 text files.
Stream Large Files¶
For large files, open a handle and read bounded chunks.
from std.fs import IoError, Path
def copy_in_chunks(source: Path, target: Path) -> Result[None, IoError]:
input = source.open("rb", -1, None, None, None)?
output = target.open("wb", -1, None, None, None)?
loop:
chunk = input.read_bytes(8192)?
if len(chunk) == 0:
break
output.write_bytes(chunk)?
output.sync_data()?
return Ok(None)
sync_data() requests durable file data. Use sync() when metadata durability matters too.
Read a Fixed Header¶
from std.fs import IoError, Path
def read_magic(path: Path) -> Result[bytes, IoError]:
file = path.open("rb", -1, None, None, None)?
return file.read_exact(4)
read_exact(size) fails on short reads, which is useful for file headers and binary protocol frames.
Seek Within a File¶
from std.fs import IoError, Path
def read_footer(path: Path, size: int) -> Result[bytes, IoError]:
file = path.open("rb", -1, None, None, None)?
file.seek(0 - size, 2)?
return file.read_exact(size)
Use tell() when you need to save or report the current cursor.
Copy, Move, and Clean Up¶
from std.fs import IoError, Path
def publish_tree(build_dir: Path, release_dir: Path) -> Result[Path, IoError]:
copied = build_dir.copy_into(release_dir, follow_symlinks=True, preserve_metadata=False)?
copied.joinpath("READY").touch(exist_ok=True)?
return Ok(copied)
def replace_file(source: Path, target: Path) -> Result[Path, IoError]:
return source.move(target)
def remove_workspace(path: Path) -> Result[None, IoError]:
path.remove_tree()?
return Ok(None)
remove_tree() is for directories. Use unlink() for files and symlinks.
Directory Listings¶
from std.fs import IoError, Path
def list_inputs(root: Path) -> Result[list[Path], IoError]:
return root.glob("*.incn")
def list_all_inputs(root: Path) -> Result[list[Path], IoError]:
return root.rglob("*.incn")
Use scandir() when you want directory entries that can answer is_file(), is_dir(), and metadata().
Temporary-File Layout¶
Temporary location creation belongs to std.tempfile; ordinary operations on those locations belong to std.fs.
from std.fs import IoError, Path
def write_temp_payload(tmp_dir: Path, data: bytes) -> Result[Path, IoError]:
path = tmp_dir.joinpath("payload.bin")
path.write_bytes(data)?
return Ok(path)
std.tempfile.SpooledTemporaryFile is a follow-up API for RFC 010. It should compose with std.fs paths and file handles instead of being folded into std.fs.