TIP:            411
Title:          Improved Channel Introspection via "chan info"
Version:        $Revision: 1.4 $
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.
