Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Procedure failes when externalized to script library.

54 views
Skip to first unread message

rianmonnahan

unread,
Mar 23, 2017, 7:47:59 PM3/23/17
to
Hi All,

I'm perplexed. The readCSV works in the my main tcl file.

#/bin/sh
# \
exec wish "$0" "$@"


package require csv
package require struct::matrix


proc readCSV {args} {

struct::matrix data
set chan [open $args ]
csv::read2matrix $chan data , auto
close $chan

set rows [data rows]

for {set row 0} {$row < $rows} {incr row} {

puts [data get row $row ]

}
}

readCSV myfile.csv

However, when I attempt to externalize the procedure to a script library, it fails with the errors:

This is a procedure in clib

Error in startup script: invalid command name "data"
while executing
"$m columns"
(procedure "Split2matrix" line 25)
invoked from within
"Split2matrix $alternate $m $data $sepChar $expand"
(procedure "csv::read2matrix" line 87)
invoked from within
"csv::read2matrix $chan data , auto "
(procedure "clib::ReadCSV" line 4)
invoked from within
"clib::ReadCSV myfile.csv"
(file "c2.tcl" line 24)

The c2.tcl file is coded as follows:


#/bin/sh
# \
exec wish "$0" "$@"

set T_TOP ~RianMonnahan/Desktop/Playground/TKompta
set T_BIN $T_TOP/Bin
set T_LIB $T_TOP/TclLib
set T_CURRENT .


lappend auto_path $T_LIB

puts $auto_path

package require clib 1.0
package require csv
package require struct::matrix


###Main

clib::DoTest

clib::ReadCSV myfile.csv


The clib.tcl under $T_LIB is coded as follows:

#Register the package

package provide clib 1.0


#Create the namespaces

namespace eval ::clib {

namespace export DoTest ReadCSV

}

#Procedures

proc ::clib::DoTest {} {

puts "This is a procedure in clib\n"

}

proc ::clib::ReadCSV {args} {

set chan [open $args ]
csv::read2matrix $chan data , auto
close $chan

set rows [data rows]

for {set row 0} {$row < $rows} {incr row} {

puts [data get row $row]

}

}


The tclIndex and PkgIndex files are OK.

The myfile.csv is in the $T_BIN directory and looks like this:

"1","1","VE","x","401","Empty","100","0","100"
"2","1","VE","x","7000","Empty","0","100","0"
"3","2","VE","x","401","Empty","200","0","200"
"4","2","VE","x","7000","Empty","0","200","0"
"5","3","VE","x","401","Empty","100","0","100"
"6","3","VE","x","7000","Empty","0","100","0"
"7","4","VE","x","401","Empty","100","0","100"
"8","4","VE","x","7000","Empty","0","100","0"
"9","5","VE","x","401","Empty","100","0","100"
"10","5","VE","x","7000","Empty","0","100","0"

There is some reason why the interpreter cannot find the ::csv scripts. I don't get it.




Rich

unread,
Mar 23, 2017, 8:09:52 PM3/23/17
to
rianmonnahan <rian.m...@gmail.com> wrote:
> Hi All,
>
> I'm perplexed. The readCSV works in the my main tcl file.

If you carefully compare the two procedures, you should see the error:

Main: clib:
> proc readCSV {args} { proc ::clib::ReadCSV {args} {
>
> struct::matrix data
> set chan [open $args ] set chan [open $args ]
> csv::read2matrix $chan data , auto csv::read2matrix $chan data , auto
> close $chan close $chan
>
> set rows [data rows] set rows [data rows]
>
> for {set row 0} \ for {set row 0} \
> {$row < $rows} \ {$row < $rows} \
> {incr row} { {incr row} {
>
> puts [data get row $row ] puts [data get row $row]
>
> } }
> } }

Note, I've reformatted your 'for loop' to make the side-by-side better
fit on screen.

What do you see that is missing between those two? The difference
should be quite obvious and stick out very well above.



Also, you have a latent data-dependent logic bug in your code. The
"args" parameter is a list, but in your call to open you use args as a
string. This will work, until the day you pass in a filename
containing a character that is special to a list, at which point you'll
get an error message about not being able to open a file with a name
that is different from what you pass in (because of the escaping of
list special characters when treating a list as a string).

You should either give a real parameter:

proc readCSV {filename} {

Or you need to perform a lindex on args:

set chan [open [lindex $args 0]]

to prevent this one from happening.

rianmonnahan

unread,
Mar 24, 2017, 7:52:54 AM3/24/17
to
Rich,

Thanks for looking at the code.

Also thanks for the tip on using lindex $args 0.

Indeed, I did forget to copy over to the library the struct::matrix data line. I've copied it over now.

The problem persists:

Error in startup script: invalid command name "data"
while executing
"$m columns"
(procedure "Split2matrix" line 25)
invoked from within
"Split2matrix $alternate $m $data $sepChar $expand"
(procedure "csv::read2matrix" line 87)
invoked from within
"csv::read2matrix $chan data , auto "
(procedure "clib::ReadCSV" line 6)
invoked from within
"clib::ReadCSV myfile.csv"
(file "c2.tcl" line 24)

For me, the interpreter just can't find the csv routines. If they were some of my own routines in script lib somewhere, I would say, "ok, Iforgot to update auto_path or tclIndex or PkgIndex.tcl..."

Since the procedure works in the main code file but not in the lib, ther has to be a path issue of some sort. I just can figure it out.

Some more info:

I am running ActiveTcl binaries on a MacOSX 10.10.5 system.

/Users/RianMonnahan/Desktop/Playground/TKompta/TclLib>wish
% puts [info patchlevel]
8.6.4

% puts $auto_path
/Library/Frameworks/Tcl.framework/Versions/8.6/Resources/Scripts /Library/Frameworks/Tcl.framework/Versions/8.6/Resources /usr/local/bin/../../../Library/Frameworks/Tk.framework/Versions/8.6/Resources/Wish.app/Contents/lib ~/Library/Tcl /Library/Tcl /System/Library/Tcl ~/Library/Frameworks /Library/Frameworks /System/Library/Frameworks /Library/Tcl/teapot/package/macosx10.5-i386-x86_64/lib /Library/Tcl/teapot/package/tcl/lib /Library/Frameworks/Tk.framework/Versions/8.6/Resources/Scripts /Library/Frameworks/Tk.framework/Versions/8.6/Resources/Scripts/ttk








Rich

unread,
Mar 24, 2017, 9:49:51 AM3/24/17
to
rianmonnahan <rian.m...@gmail.com> wrote:
>> What do you see that is missing between those two? The difference
>> should be quite obvious and stick out very well above.
>
> Thanks for looking at the code.
>
> Also thanks for the tip on using lindex $args 0.

You are welcome

> Indeed, I did forget to copy over to the library the struct::matrix
> data line. I've copied it over now.
>
> The problem persists:
>
> Error in startup script: invalid command name "data"
> while executing
> "$m columns"
> ...

It is interesting that you provided code for your first post (which is
the only effective way to get help) yet you did not provide the a copy
of the new code that continues to fail.

> For me, the interpreter just can't find the csv routines.

No, that is not what your error message is telling you. It is telling
you that the matrix command "data" still does not exist (which is
exactly the problem you had before) and so it seems you did not "copy"
the code correctly somehow. But without the new code, I can only
guess.

> Since the procedure works in the main code file but not in the lib,
> ther has to be a path issue of some sort.

Nope, the error message is "invalid command "data"". I.e., no command
named "data" has been defined prior to trying to use the "data"
command. Which is exactly what you had the first time.

New updated code please....

> I just can figure it out.

Provide a copy of the new code that is failing and we here on Usenet
will have a *much* easier time of providing help.

Also, you should be aware that your original (both main and library)
examples also contain a memory leak. You never 'destroy' the matrix
command you create in order to read in the CSV data. The
struct::matrix module creates commands that perform the work, and they
do not get auto-garbage collected when their enclosing scope goes away.
You have to explicitly destroy them (best to use their 'destroy'
sub-command) to clean them up.

rianmonnahan

unread,
Mar 24, 2017, 1:06:23 PM3/24/17
to
c.tcl -> works fine

#/bin/sh
# \
exec wish "$0" "$@"


package require csv
package require struct::matrix


proc readCSV {args} {

struct::matrix data
set chan [open [ lindex $args 0 ] ]
csv::read2matrix $chan data , auto
close $chan

set rows [data rows]

for {set row 0} {$row < $rows} {incr row} {

puts [data get row $row ]

}
}

readCSV myfile.csv

c2.sql -> fails with the error message given above.

#/bin/sh
# \
exec wish "$0" "$@"

set T_TOP ~RianMonnahan/Desktop/Playground/TKompta
set T_BIN $T_TOP/Bin
set T_LIB $T_TOP/TclLib
set T_CURRENT .


lappend auto_path $T_LIB

puts $auto_path

package require clib 1.0
package require csv
package require struct::matrix


###Main

clib::DoTest

clib::ReadCSV myfile.csv

clib.tcl modified as per your original suggestions:

#Register the package

package provide clib 1.0


#Create the namespaces

namespace eval ::clib {

namespace export DoTest ReadCSV

}

#Procedures

proc ::clib::DoTest {} {

puts "This is a procedure in clib\n"

}

proc ::clib::ReadCSV {args} {


struct::matrix data
set chan [open [ lindex $args 0 ] ]
csv::read2matrix $chan data , auto
close $chan

set rows [data rows]

for {set row 0} {$row < $rows} {incr row} {

puts [data get row $row]

}

}


I'll work on the memory leak later. Thanks for the tip, by the way. There are probably a lot of things wrong with the code I write. ;) This a contrived example to isolate the problem.

Rich

unread,
Mar 24, 2017, 1:59:16 PM3/24/17
to
rianmonnahan <rian.m...@gmail.com> wrote:
> On Friday, March 24, 2017 at 2:49:51 PM UTC+1, Rich wrote:
>> New updated code please....
> clib.tcl modified as per your original suggestions:
>
> #Register the package
> package provide clib 1.0
>
> #Create the namespaces
> namespace eval ::clib {
> namespace export DoTest ReadCSV
> }
>
> #Procedures
> proc ::clib::DoTest {} {
> puts "This is a procedure in clib\n"
> }
>
> proc ::clib::ReadCSV {args} {
> struct::matrix data
> set chan [open [ lindex $args 0 ] ]
> csv::read2matrix $chan data , auto
> close $chan
> set rows [data rows]
> for {set row 0} {$row < $rows} {incr row} {
> puts [data get row $row]
> }
> }
>
> I'll work on the memory leak later.

It's easy:

data destroy

at the end of "ReadCSV" and the matrix is cleaned up.

You'll want to do this anyway, due to the fact that a matrix is a
command. You'll get an error the second time you call ReadCSV for
"data" already exists unless you do clean things up.

> This a contrived example to isolate the problem.

Appreciated - just enough to cause the issue is way easier than having
to wade through pages of non-issue code to find a problem.

Here's what you need to change to both fix the "data, can't find" error
as well as to fix the memory leak:

--- clib.tcl.orig 2017-03-24 13:53:08.927642103 -0400
+++ clib.tcl 2017-03-24 13:53:33.760980424 -0400
@@ -22,9 +22,9 @@
proc ::clib::ReadCSV {args} {


- struct::matrix data
+ set data [struct::matrix data]
set chan [open [ lindex $args 0 ] ]
- csv::read2matrix $chan data , auto
+ csv::read2matrix $chan $data , auto
close $chan

set rows [data rows]
@@ -34,5 +34,5 @@
puts [data get row $row]

}
-
+ $data destroy
}

The reason it was failing for you when you tried to "lib-ize" it is
that struct::matrix creates its commands in the current namespace where
it is executed.

So when you created the "data" matrix, the full command name was
::clib::data.

But, when you passed just "data" over to the CSV module, it was in a
different namespace (the ::csv namespace) and there is no local "data"
in the ::csv namespace, nor is there a global "data" (because
struct::matrix created ::clib::data).

The fix is to capture the return command name from struct::matrix into
a data variable, and then to pass that variables contents into the
csv::read2matrix command. The ::csv::read2matrix can call
"::clib::data" and make use of the matrix.

The destroy at the end just cleans up after itself.

rianmonnahan

unread,
Mar 31, 2017, 11:27:50 AM3/31/17
to
Rich,

Many thanks for taking the time to look at this issue.

Rian

Rich

unread,
Mar 31, 2017, 11:34:25 AM3/31/17
to
rianmonnahan <rian.m...@gmail.com> wrote:
> Rich,
>
> Many thanks for taking the time to look at this issue.
>
> Rian

Please, when using google groups (preferably, don't use google groups,
it is an *awful* interface to usenet [1] but if you must) please quote
the actual message you are replying to, not the original in the thread.

Quoting the original puts your statement completely out of context
because the parts you are responding to do not appear in the quote
text.

Also, trim the quote to just the relevant context, don't just quote
everything.


[1] Instead of google groups, register an eternal september
(https://www.eternal-september.org/) account or use AIOE
(http://news.aioe.org/) and a real Usenet news reader
(https://en.wikipedia.org/wiki/List_of_Usenet_newsreaders).
0 new messages