Author: Pawel Salawa <pawelsalawa@gmail.com>
State: Draft
Type: Project
Vote: Pending
Created: 31-Aug-2012
Post-History:
Tcl-Version: 8.7
Abstract
This document describes new subcommand for chan, chan info, that provides a unified interface to deeper introspection of information about a particular channel.
Rationale
When working with Tcl channels sometimes it happens that we got the channel, but we don't know if it's a file channel, socket, or reflected channel. This information can be very useful. Also some additional information, depending of the channel type, like file path for file channel, host and port for sockets (it's already available, but could get unified within new chan subcommand), or any metadata provided by reflected channels.
An example where it could be used is the package with an API that accepts just a channel on input call and the inside routines need to do something with the file (in file system), so they have to learn the name of the file related to given channel.
Specification
A new subcommand for chan is introduced:
chan info channelId
Also a new optional command is introduced for reflected channels API:
cmdPrefix chaninfo channelId
The info Subcommand of chan
The chan info command will take a single mandatory argument, channelId, which will be the name of a channel to retrieve information about. This operation will always fail in a safe interpreter. The result of the new chan info command would be a dictionary with following keys always present:
type: indicating a type of channel. Possible values are "file", "socket", "process" (result of [open "|..."]), empty string (in case of channel that doesn't support this information), or any custom type, depending of refchan implementations. This is a mandatory key.
The remainder of the keys are optional and depend on the type.
For file channels, the dictionary shall include these:
path: full, normalized path to the file, including the file name.
new: boolean value indicating whether file already existed while opening, or it was created.
For socket channels, the dictionary shall include these:
host: peer hostname, or local hostname for listening socket. This is partially equivalent to getting the first value returned by [chan configure channelId -peername] for connected sockets.
port: peer port, or listening port (for listening socket). This is partially equivalent to getting the third value returned by [chan configure channelId -peername] for connected sockets.
side: one of the following: "client", "accepted", or "listening".
For process channels, the dictionary shall include these:
cmdline: copy of the command passed to open.
pid: PID of a spawned process, as produced by pid.
Any key could be produced by other channel types, notably including reflected channels.
The chaninfo Operation of Reflected Channel Implementations
The chaninfo subcommand of a reflected channel implementation command returns a dict that is provided in response to a chan info request. If the dictionary does not include the mandatory type member, the reflected channel baseline implementation will add it and set it to refchan. It is an error to return a non-dictionary.
Since reflected channels are free to set the type to anything, they can simulate standard channels, like "file", as well as create completely new types.
If the operation is not supported, the baseline implementation will treat it the same as if the operation returned an empty dictionary.
Internals
Channel structure in Tcl core would require another API level indicating channels that have a function returning an "info" dict. All core channels are expected to migrate to this level, although it's possible to stay at current API version - it will just cause the type in chan info dict to be the typeName field of the channel's Tcl_ChannelType structure, with no additional keys in the dict.
Examples
This is a a pure Tcl implementation of file type channel, so it supports new information in chan info:
oo::class create filechan {
variable path fd created filemode
constructor {fpath mode} {
set filemode $mode
set path $fpath
}
method initialize {ch mode} {
set exists [file exists $path]
set fd [open $path $filemode]
set created [expr { [file exists $path] && !$exists}]
return "initialize finalize watch read seek chaninfo"
}
method finalize {ch} {
::close $fd
my destroy
}
method watch {ch events} {
foreach event [list read write] method [list readable writable] {
if {$event in $events} {
fileevent $fd $method [list chan postevent $ch $event]
}
}
}
# Must be present on a readable channel
method read {ch count} {
::read $fd $count
}
# This method is optional, but useful for the example below
method seek {ch offset base} {
::seek $fd $offset $base
}
method chaninfo {ch} {
dict create type file path $path new $created
}
}
proc openfile {file mode} {
# lets not bother of what modes should be passed to [chan create],
# it's just an example...
chan create [list read write] [filechan new $file $mode]
}
set fd [openfile "myfile.txt" r]
puts [chan info $fd]
close $fd
Reference Implementation
http://sqlitestudio.pl/tcl/patches/tip-411-chan\_info.patch
Patch made against 8.6.0 (just before final release).
Copyright
This document has been placed in the public domain.