[GRASS-dev] Re: [GRASS-CVS] michael: grass6/gui/tcltk/gis.m mapcanvas.tcl, 1.52, 1.53

Excellent, Michael.
Now we no longer see the "parts()" error in gis.m. I have backported it
to 6.2-CVS
for 6.2.1.

Markus

grass@intevation.de wrote on 11/28/2006 12:14 AM:

Author: michael

Update of /grassrepository/grass6/gui/tcltk/gis.m
In directory doto:/tmp/cvs-serv16736

Modified Files:
  mapcanvas.tcl
Log Message:
Added error trap for g.region failing at GUI startup. Now it exits
GUI with a message about g.region failing.

Index: mapcanvas.tcl

RCS file: /grassrepository/grass6/gui/tcltk/gis.m/mapcanvas.tcl,v
  

...

grass intevation.de wrote on 11/28/2006 12:14 AM:
> Author: michael
>
> Update of /grassrepository/grass6/gui/tcltk/gis.m
> In directory doto:/tmp/cvs-serv16736
>
> Modified Files:
> mapcanvas.tcl
> Log Message:
> Added error trap for g.region failing at GUI startup. Now it exits
> GUI with a message about g.region failing.
>
> Index: mapcanvas.tcl
> ===================================================================
> RCS file: /grassrepository/grass6/gui/tcltk/gis.m/mapcanvas.tcl,v

Markus wrote:

Excellent, Michael.
Now we no longer see the "parts()" error in gis.m. I have backported
it to 6.2-CVS for 6.2.1.

This does not work for me unfortunately. (both wish 8.3 and 8.4)

I moved /usr/lib/libgdal.so out of the way to simulate breakage, then:
G63> g.region -p
g.region: error while loading shared libraries: libgdal1.3.1.so.1: cannot open shared object file: No such file or directory
G63> echo $?
127

I changed:

- if {![catch {open [concat "|g.region" "-ug" $args] r} input]} {
+ set regcmd [concat "|g.region" "-ug" $args]
+ set retval [catch {open $regcmd r} input]
+ if {! $retval } {

But catch returns 0 and the return stream ($input) is not filled
with the stderr message (it's empty).

To get it to fail nicely I need to add some more checking:

if {! $retval } {

  set havedata 0
  while {[gets $input line] >= 0} {
    if { ! $havdata } {
      set havedata 1
    }
    regexp -nocase {^([a-z]+)=(.*)$} $line trash key value
    set parts($key) $value
  }

  if { ! $havedata } {
    # eof check probably redundant
    if { [eof $input] } {
      puts "Startup error: check 'g.region -p'"
      exit 1
    }
  }
  catch {close $input}

  ...
}

I can get it to pass through the "missing libgdal.so" message with:
set regcmd [list exec "g.region" "-ug" $args]
set retval [catch $regcmd result]
if { $retval } {
   puts "Startup error:\n $result"
   exit 1
}
puts "result is: $result"

but when I restore libgdal it then fails with:
Failed to run command:
ERROR: region <> not found (that's from g.region main.c region=)

???

Hamish

On 11/28/06 6:58 PM, "Hamish" <hamish_nospam@yahoo.com> wrote:

I changed:

- if {![catch {open [concat "|g.region" "-ug" $args] r} input]} {
+ set regcmd [concat "|g.region" "-ug" $args]
+ set retval [catch {open $regcmd r} input]
+ if {! $retval } {

I think this is problematic. The original code says in essence, if g.region
does *not* fail, do the following.

Looking at the catch command, it should store any error returned in the
variable input.

Trying replacing the line in the else clause...

puts "GRASS command g.region failed. Check to see if you've install GRASS
correctly."

With...

puts $input

When I rename g.region to g.region_x, this produces the following error
message on the command line...

couldn't execute "g.region": no such file or directory

How's this?

Michael

__________________________________________
Michael Barton, Professor of Anthropology
School of Human Evolution & Social Change
Center for Social Dynamics & Complexity
Arizona State University

phone: 480-965-6213
fax: 480-965-7671
www: http://www.public.asu.edu/~cmbarton

Michael Barton wrote:

On 11/28/06 6:58 PM, "Hamish" <hamish_nospam@yahoo.com> wrote:

> I changed:
>
> - if {![catch {open [concat "|g.region" "-ug" $args] r} input]} {
> + set regcmd [concat "|g.region" "-ug" $args]
> + set retval [catch {open $regcmd r} input]
> + if {! $retval } {
>

I think this is problematic. The original code says in essence, if
g.region does *not* fail, do the following.

g.region returns "0" for EXIT_SUCCESS.
So testing !return_value is the same; if{!0}=if{TRUE}; no problem.

Looking at the catch command, it should store any error returned in
the variable input.

Trying replacing the line in the else clause...

puts "GRASS command g.region failed. Check to see if you've install
GRASS correctly."

With...

puts $input

I never get there as catch always returns 0 for me, even when g.region
failed.

If I move "puts $input" before if {! $retval } above, that prints
"file8" or "file9", presumably the name of the $input stream that gets()
works on.

Hamish

On 11/28/06 10:20 PM, "Hamish" <hamish_nospam@yahoo.com> wrote:

I never get there as catch always returns 0 for me, even when g.region
failed.

This means that catch is not returning an error when g.region fails.

If I move "puts $input" before if {! $retval } above, that prints
"file8" or "file9", presumably the name of the $input stream that gets()
works on.

Yes, you are correct. Again, this suggests that g.region does not fail.
Maybe the problem is that your failure is not the same as my failure. To
simulate failure, I renamed g.region to g.region_x. See if this does fail on
your system. If so, then we need to figure out where the gdal problem causes
failure. There is another g.region statement in the code. So perhaps it is
there.

Michael

__________________________________________
Michael Barton, Professor of Anthropology
School of Human Evolution & Social Change
Center for Social Dynamics & Complexity
Arizona State University

phone: 480-965-6213
fax: 480-965-7671
www: http://www.public.asu.edu/~cmbarton

Hamish:

> I never get there as catch always returns 0 for me, even when
> g.region failed.

This means that catch is not returning an error when g.region fails.

yes. This is what I am trying to figure out.

> If I move "puts $input" before if {! $retval } above, that prints
> "file8" or "file9", presumably the name of the $input stream that
> gets() works on.
>

Yes, you are correct. Again, this suggests that g.region does not
fail.

It fails.

catch{open "|cmd"} doesn't return the exit code of "|cmd", while
catch {exec "cmd"} does. The only thing that comes to mind is that in
the "open" case catch is catching the channel_id of the open{} function,
not the result of "|cmd":

open(n) "returns a channel identifier"
  http://www.astro.princeton.edu/~rhl/Tcl-Tk_docs/tcl/open.n.html

so the catch currently tests if a channel identifier could be opened ?

exec(n) returns:
  "If standard output has not been redirected then the exec command
returns the standard output from the last command in the pipeline. If
any of the commands in the pipeline exit abnormally or are killed or
suspended, then exec will return an error and the error message will
include the pipeline's output followed by error messages describing the
abnormal terminations; the errorCode variable will contain additional
information about the last abnormal termination encountered. If any of
the commands writes to its standard error file and that standard error
isn't redirected, then exec will return an error; the error message will
include the pipeline's standard output, followed by messages about
abnormal terminations (if any), followed by the standard error output."
  http://www.astro.princeton.edu/~rhl/Tcl-Tk_docs/tcl/exec.n.html

If we could get open{} to pipe stderr to the channel_id stream we might
be ok.

Maybe the problem is that your failure is not the same as my
failure. To simulate failure, I renamed g.region to g.region_x. See if
this does fail on your system.

(too late now, will try tomorrow)

I was making it fail by renaming /usr/lib/libgdal.* to something else.

If so, then we need to figure out where the gdal problem causes
failure. There is another g.region statement in the code. So perhaps
it is there.

No, it is the same place. I put lots of puts"" around it to see exactly
what was happening.

Hamish

Hamish wrote on 11/29/2006 02:58 AM:

grass intevation.de wrote on 11/28/2006 12:14 AM:
    

Author: michael

Update of /grassrepository/grass6/gui/tcltk/gis.m
In directory doto:/tmp/cvs-serv16736

Modified Files:
  mapcanvas.tcl
Log Message:
Added error trap for g.region failing at GUI startup. Now it exits
GUI with a message about g.region failing.

Index: mapcanvas.tcl

RCS file: /grassrepository/grass6/gui/tcltk/gis.m/mapcanvas.tcl,v
      
Markus wrote:
  
Excellent, Michael.
Now we no longer see the "parts()" error in gis.m. I have backported
it to 6.2-CVS for 6.2.1.
    
This does not work for me unfortunately. (both wish 8.3 and 8.4)

I moved /usr/lib/libgdal.so out of the way to simulate breakage, then:
G63> g.region -p
g.region: error while loading shared libraries: libgdal1.3.1.so.1: cannot open shared object file: No such file or directory
G63> echo $?
127
  

oh, I tested incorrectly, since I moved g.region away which is the wrong
test.

Markus

Hamish wrote:

catch{open "|cmd"} doesn't return the exit code of "|cmd", while
catch {exec "cmd"} does. The only thing that comes to mind is that in
the "open" case catch is catching the channel_id of the open{} function,
not the result of "|cmd":

open(n) "returns a channel identifier"
  http://www.astro.princeton.edu/~rhl/Tcl-Tk_docs/tcl/open.n.html

so the catch currently tests if a channel identifier could be opened ?

Errors in child processes are normally indicated by the corresponding
"close" throwing an exception.

Most errors (returning a non-zero exit code or writing to stderr)
can't be detected at the time that "open" is called, as the error
hasn't happened at that point.

An error from the execve() call (e.g. a missing executable) could be
caught at that point, but I don't think that would apply to errors
loading shared libraries (which is the responsibility of the program
rather than the OS kernel).

Even so, Tcl may defer reporting such errors to the corresponding
"close" for simplicity (you only need to catch exceptions in one place
rather than two). The open(n) manpage says:

  If the command (or one of the commands) executed in the
  command pipeline returns an error (according to the definition
  in exec), a Tcl error is generated when close is called on the
  channel (similar to the close command.)

--
Glynn Clements <glynn@gclements.plus.com>

On 11/29/06 1:45 PM, "Glynn Clements" <glynn@gclements.plus.com> wrote:

Even so, Tcl may defer reporting such errors to the corresponding
"close" for simplicity (you only need to catch exceptions in one place
rather than two). The open(n) manpage says:

Yes. I was able to trap the gdal error with a catch on the close command.
However, the catch on the open command is needed to trap an error caused by
a problem with g.region--maybe because it only reports one line of text???

Michael
__________________________________________
Michael Barton, Professor of Anthropology
School of Human Evolution & Social Change
Center for Social Dynamics and Complexity
Arizona State University

phone: 480-965-6213
fax: 480-965-7671
www: http://www.public.asu.edu/~cmbarton

Hamish:

> I was making it fail by renaming /usr/lib/libgdal.* to something
> else.

Michael:

I got the GIS Manager to fail differently when I messed with GDAL
instead of messing with g.region. The place to trap this seems to be
the line.

catch {close $input}

Replace this line with...

        if {[catch {close $input} error]} {
            puts $error
            exit
        }

We still need to keep the other else clause with 'puts $input' to
catch cases where there is a different problem with g.region.

Great work. For me the latest version in CVS fails nicely for both the
missing libgdal.so and missing g.region cases. (I only tested 6.3-cvs)

I made a slight change in 6.3-cvs: exit with return code of "1" on
error. (unlike bash, tcl's "exit" defaults to "0" if not given; bash
defaults to the return code of the last command run if not given)

As the gis.m script runs gm.tcl via "exec" this doesn't really have any
effect AFAICT, but it's good practice.

oh, and FWIW I figured out why my tcl exec solution was failing with:
ERROR: region <> not found
It was trying to run `g.region ""` (expands to `g.region region=""`)

It's redundant now, but for the record adding a "concat" fixed it:

-set regcmd [list exec "g.region" "-ug" $args]
+set regcmd [list exec "g.region" [concat "-ug " $args]]
set retval [catch $regcmd result]
if { $retval } {
    puts "Failed to run command:\n $result"
    exit 1
}
puts "result is: $result"

Hamish

ps- always "cvs diff -u" before "cvs commit" !!

On 11/29/06 11:45 PM, "Hamish" <hamish_nospam@yahoo.com> wrote:

Great work. For me the latest version in CVS fails nicely for both the
missing libgdal.so and missing g.region cases. (I only tested 6.3-cvs)

I made a slight change in 6.3-cvs: exit with return code of "1" on
error. (unlike bash, tcl's "exit" defaults to "0" if not given; bash
defaults to the return code of the last command run if not given)

Great. I'm glad it gives a more informative error now. Hopefully that will
help the batch of people who are having trouble with gdal.

As the gis.m script runs gm.tcl via "exec" this doesn't really have any
effect AFAICT, but it's good practice.

What is the difference between exit 0, exit 1, etc? I've seen this in a
number of places but can't tell what it does.

oh, and FWIW I figured out why my tcl exec solution was failing with:
ERROR: region <> not found
It was trying to run `g.region ""` (expands to `g.region region=""`)

It's redundant now, but for the record adding a "concat" fixed it:

Watch out. You're becoming a TclTk expert. :wink:

Michael
__________________________________________
Michael Barton, Professor of Anthropology
School of Human Evolution & Social Change
Center for Social Dynamics & Complexity
Arizona State University

phone: 480-965-6213
fax: 480-965-7671
www: http://www.public.asu.edu/~cmbarton

Hamish wrote:

oh, and FWIW I figured out why my tcl exec solution was failing with:
ERROR: region <> not found
It was trying to run `g.region ""` (expands to `g.region region=""`)

It's redundant now, but for the record adding a "concat" fixed it:

-set regcmd [list exec "g.region" "-ug" $args]
+set regcmd [list exec "g.region" [concat "-ug " $args]]
set retval [catch $regcmd result]

This is bogus. It will fail if $args isn't empty:

  % set args {foo bar baz}
  % set regcmd [list exec "g.region" [concat "-ug " $args]]
  % puts $regcmd
  exec g.region {-ug foo bar baz}
  % set args foo
  % set regcmd [list exec "g.region" [concat "-ug " $args]]
  % puts $regcmd
  exec g.region {-ug foo}

IOW, the entire list of arguments (including the "-ug") will be passed
to g.region as a single argument.

I'm guessing that you're focusing on the case where $args is empty:

  % set args ""
  % set regcmd [list exec "g.region" "-ug" $args]
  % puts $regcmd
  exec g.region -ug {}
  % set args ""
  % set regcmd [list exec "g.region" [concat "-ug " $args]]
  % puts $regcmd
  exec g.region -ug

If so, the correct fix is:

  set regcmd [concat exec g.region -ug $args]

E.g.:

  % set args ""
  % set regcmd [concat exec g.region -ug $args]
  % puts $regcmd
  exec g.region -ug
  % set args foo
  % set regcmd [concat exec g.region -ug $args]
  % puts $regcmd
  exec g.region -ug foo
  % set args {foo bar baz}
  % set regcmd [concat exec g.region -ug $args]
  % puts $regcmd
  exec g.region -ug foo bar baz

NOTE: if a procedure's last formal argument is named "args", it is
treated specially: $args will be a list containing all of the
remaining arguments. This allows variadic procedures to be
implemented.

If you are passing a list of arguments as a list (rather than as
individual arguments), use a name other than "args". Otherwise, the
list will get "wrapped" inside another list.

Only use "args" as an argument variable if the procedure is supposed
to be variadic.

--
Glynn Clements <glynn@gclements.plus.com>