How does one redefine io.stderr:write() function?

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

How does one redefine io.stderr:write() function?

Robert McLay-2
I would like to make calls to io.stderr:write() be silent sometimes but I am unable to redefine that function temporarily.   Below is a tiny example of what I tried.  This doesn't work.  What does?
-------------------------------------------------------------

local function nothing()
end

io.stderr:write("hello world!\n")

-- Trying to make io.stderr:write() be silent
local my_write = write
write = nothing
io.stderr:write("hello world yet again!\n")

-- Trying to make io.stderr:write() to work again
write = my_write
io.stderr:write("hello world yet again and again!\n")


--
Robert McLay, Ph.D.
Manager of Software Tools, HPC

Reply | Threaded
Open this post in threaded view
|

Re: How does one redefine io.stderr:write() function?

Luiz Henrique de Figueiredo
> I would like to make calls to io.stderr:write() be silent sometimes but I am unable to redefine that function temporarily.

The write function used in io.stderr:write() resides in a metatable.
Try this code:

-- Trying to make io.stderr:write() be silent
local my_write = getmetatable(io.stderr).write
getmetatable(io.stderr).write = nothing
io.stderr:write("hello world yet again!\n")
-- Trying to make io.stderr:write() to work again
getmetatable(io.stderr).write = my_write
io.stderr:write("hello world yet again and again!\n")

However, note that this affects *all* files: it will disable write for
all files. The code above is ok for disabling write while no other
writes happen.
Reply | Threaded
Open this post in threaded view
|

Re: How does one redefine io.stderr:write() function?

Andrew Gierth
In reply to this post by Robert McLay-2
>>>>> "Robert" == Robert McLay <[hidden email]> writes:

 Robert> I would like to make calls to io.stderr:write() be silent
 Robert> sometimes but I am unable to redefine that function
 Robert> temporarily. Below is a tiny example of what I tried. This
 Robert> doesn't work. What does?

This intercepts all file:write calls and allows through only the ones
not to io.stderr. Note that all file objects share the same metatable
and hence the same methods, and this can't be changed because it's the
identity of the metatable that is used to identify that it's a valid
file object in the first place.

local file_methods = getmetatable(io.stderr).__index  -- any file will do
local old_write = file_methods.write
file_methods.write =
    function(f,...)
        if f ~= io.stderr then
            return old_write(f,...)
        end
        return f
    end
-- note that errors here will leave the patched function in place,
-- you might need to use pcall to handle that
io.stderr:write("nope!")
io.stdout:write("yup!")
-- restore
file_methods.write = old_write
io.stderr:write("yup!")

--
Andrew.
Reply | Threaded
Open this post in threaded view
|

Re: How does one redefine io.stderr:write() function?

Robert McLay-2
I really appreciate the answers.  Thanks to you both, Andrew and Luis, for a very clear explanation.

Robert.

On Sat, Jun 20, 2020 at 10:07 AM Andrew Gierth <[hidden email]> wrote:
>>>>> "Robert" == Robert McLay <[hidden email]> writes:

 Robert> I would like to make calls to io.stderr:write() be silent
 Robert> sometimes but I am unable to redefine that function
 Robert> temporarily. Below is a tiny example of what I tried. This
 Robert> doesn't work. What does?

This intercepts all file:write calls and allows through only the ones
not to io.stderr. Note that all file objects share the same metatable
and hence the same methods, and this can't be changed because it's the
identity of the metatable that is used to identify that it's a valid
file object in the first place.

local file_methods = getmetatable(io.stderr).__index  -- any file will do
local old_write = file_methods.write
file_methods.write =
    function(f,...)
        if f ~= io.stderr then
            return old_write(f,...)
        end
        return f
    end
-- note that errors here will leave the patched function in place,
-- you might need to use pcall to handle that
io.stderr:write("nope!")
io.stdout:write("yup!")
-- restore
file_methods.write = old_write
io.stderr:write("yup!")

--
Andrew.
>> This message is from an external sender. Learn more about why this <<
>> matters at https://links.utexas.edu/rtyclf.                        <<



--
Robert McLay, Ph.D.
Manager of Software Tools, HPC

Reply | Threaded
Open this post in threaded view
|

Re: How does one redefine io.stderr:write() function?

Gé Weijers
In reply to this post by Robert McLay-2


On Sat, Jun 20, 2020 at 7:14 AM Robert McLay <[hidden email]> wrote:
I would like to make calls to io.stderr:write() be silent sometimes but I am unable to redefine that function temporarily.   Below is a tiny example of what I tried.  This doesn't work.  What does?

Here's another approach, you make 'io.stderr' point to a proxy object that forwards all method calls to the original stderr except when you override it in the proxy. The advantage is that you don't touch any other file's metatable or methods.

The __index metamethod automatically creates a forwarding function for any method of stderr that's actually being used, unless you override it.

local original_stderr = io.stderr

local proxy = setmetatable({}, {
    __index = function(proxy, method)
        local m = original_stderr[method]
        local function forward(proxy, ...)
            return m(original_stderr, ...)
        end
        proxy[method] = forward
        return forward
    end
})

io.stderr = proxy


io.stderr:write("Hello, world\n")

proxy.write = function() end

io.stderr:write("You can't read this\n")

proxy.write = nil

io.stderr:write("Goodbye\n")


--