topmark.pipeline.steps.writer¶
topmark / pipeline / steps / writer
Writer step for committing updated content to a sink.
This step is the canonical place where TopMark writes results to a destination (filesystem, stdout, or dry-run). It avoids implementation drift between the CLI and public API.
This step is responsible for the final I/O after all other steps have computed
ctx.views.updated and selected the intended PlanStatus (INSERTED, REPLACED, REMOVED,
or PREVIEWED for stdout output).
It also applies policy gates (e.g., header mutation mode) so that command-line intent
and config policies are centralized here.
Sinks¶
- InplaceFileSink: writes in-place to the file path.
- AtomicFileSink: writes through a same-directory temporary file and atomically replaces the target.
POSIX-only durability and permission helpers such as
os.fchmod()andos.O_DIRECTORYare used only when available; Windows falls back to best-effort path-based permission handling and skips directoryfsync(). - StdoutSink: writes the updated content to stdout (stdin-content mode).
- NullSink: no-op (dry-run).
The step respects
WriterStep.may_proceed()
and the tri-state intent/feasibility via ProcessingContext.would_change
and ProcessingContext.can_change.
WriteSink ¶
Bases: Protocol
Behavioral protocol for writer-step sinks.
Sink implementations encapsulate the final destination-specific I/O for a processing context, such as writing to disk, emitting to stdout, or performing a dry-run no-op. The protocol intentionally exposes only behavior and does not require shared sink state.
write ¶
Write the updated content for ctx to the target sink.
Implementations perform the final write operation for a processed file, such as writing to disk, emitting to stdout, or performing no operation in dry-run mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
Context that holds updated content and write status. |
required |
Returns:
| Type | Description |
|---|---|
WriteResult
|
Structured result indicating the write status and byte count when |
WriteResult
|
the sink can report one. |
Source code in src/topmark/pipeline/steps/writer.py
WriteResult
dataclass
¶
Structured result of a writer sink operation.
Attributes:
| Name | Type | Description |
|---|---|---|
status |
WriteStatus
|
Final status reported by the sink. |
bytes_written |
int
|
Number of bytes written when the sink can report one; zero for dry-run, skipped, failed, or unreported file writes. |
NullSink ¶
Dry-run sink: does not write anything.
write ¶
No-op write for dry-run mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
Processing context for the current file. |
required |
Returns:
| Type | Description |
|---|---|
WriteResult
|
The current |
Source code in src/topmark/pipeline/steps/writer.py
StdoutSink ¶
Standard-output sink (stdin-content mode).
write ¶
Emit updated content to standard output.
This sink is used when the CLI/API is configured to read a single file's content from STDIN and emit the updated result to STDOUT.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
Processing context containing the updated lines. |
required |
Returns:
| Type | Description |
|---|---|
WriteResult
|
|
WriteResult
|
otherwise |
Source code in src/topmark/pipeline/steps/writer.py
InplaceFileSink ¶
Bases: WriteSink
Write updated content by truncating the original file and writing in place.
Pros
- Keeps inode identity stable.
- Minimal I/O.
Cons: - Risk of partial/truncated files on crash. - Live readers may observe mid-write changes.
write ¶
Write updated content directly into the original file (in-place).
Opens the file in binary write mode, truncates its contents, and writes
ctx.views.updated.lines directly. This operation preserves the inode identity
but may leave a truncated file if the process is interrupted mid-write.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
The active processing context, expected to contain |
required |
Returns:
| Type | Description |
|---|---|
WriteResult
|
Structured write result containing |
WriteResult
|
or |
Source code in src/topmark/pipeline/steps/writer.py
AtomicFileSink ¶
Bases: WriteSink
Write updated content to a temp file and atomically replace the target.
This sink writes to a temporary file in the same directory as the
target, fsync()s it, then calls os.replace() to atomically swap it in.
Permission preservation and directory durability are best-effort and platform-aware:
POSIX uses os.fchmod() and directory fsync() when available, while Windows falls back to
path-based chmod() and skips directory fsync() because os.fchmod and os.O_DIRECTORY are
not exposed there.
Pros
- Atomic visibility; crash-safe (old file remains until replace).
Cons:
- New inode/ID on POSIX; slightly more I/O.
- Directory fsync() is POSIX-only and therefore skipped on Windows.
write ¶
Atomically replace the target file by writing to a temp file first.
Writes ctx.views.updated.lines to a temporary file in the same directory,
calls os.fsync() to ensure durability, and performs os.replace() to
atomically swap it in place. The operation guarantees that readers will
either see the old file or the complete new file, never a partial write.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
The active processing context, expected to
contain |
required |
Returns:
| Type | Description |
|---|---|
WriteResult
|
Structured write result with |
WriteResult
|
|
Source code in src/topmark/pipeline/steps/writer.py
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | |
WriterStep ¶
Bases: BaseStep
Commit updated content to a sink (filesystem/stdout/null).
Applies policy gates and the intended write action (insert/replace/remove) to produce a final write result. Performs the only I/O in the pipeline.
Axes written
- write
Sets
- WriteStatus: {PENDING, WRITTEN, SKIPPED, FAILED}
Source code in src/topmark/pipeline/steps/writer.py
may_proceed ¶
Return True if the writer is allowed to commit changes.
The writer should only run when
- The pipeline has not requested an early halt (
ctx.flow.haltis False); - The caller explicitly enabled applying changes (
run_options.apply_changesis True); - The updater selected a concrete write action (
INSERTED/REPLACED/REMOVED); - We have an updated image to write and the engine deemed the change safe
(
ctx.views.updated.linesis present andcan_change(ctx) is True).
Policy and intent have already been enforced by the updater. Re-checking
header/comparison/strip intent here can drift from the authoritative
UpdateStatus and cause double-gating, so we avoid it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
The processing context for the current file. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if processing can proceed to the write step, False otherwise. |
Source code in src/topmark/pipeline/steps/writer.py
run ¶
Writer step: commit updates to the selected sink.
This step executes only when may_proceed() returns True.
Otherwise it converts a preview status to a non-mutating terminal status.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
The processing context with update intent. |
required |
Mutations
Updates ctx.status.write and may append diagnostics when policy or
sink failures prevent writing.
Source code in src/topmark/pipeline/steps/writer.py
hint ¶
Attach write hints (non-binding).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ctx
|
ProcessingContext
|
The processing context. |
required |