Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compat) add a 5.4 compatible "warn" function #366

Merged
merged 1 commit into from
Jan 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ see [CONTRIBUTING.md](CONTRIBUTING.md#release-instructions-for-a-new-version) fo
[#360](https://github.com/lunarmodules/Penlight/pull/360)
- feat: `permute.list_table` generate table with different sets of values
[#360](https://github.com/lunarmodules/Penlight/pull/360)
- feat: Lua 5.4 'warn' compatibility function
[#366](https://github.com/lunarmodules/Penlight/pull/366)
- feat: deprecation functionality `utils.raise_deprecation`
[#361](https://github.com/lunarmodules/Penlight/pull/361)
- fix: `dir.rmtree` failed to remove symlinks to directories
Expand Down
29 changes: 29 additions & 0 deletions lua/pl/compat.lua
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,33 @@ if not package.searchpath then
end
end

--- Global exported functions (for Lua < 5.4)
-- @section lua54

--- raise a warning message.
-- This functions mimics the `warn` function added in Lua 5.4.
-- @function warn
-- @param ... any arguments
if not warn then -- luacheck: ignore
local enabled = false
function warn(arg1, ...) -- luacheck: ignore
if type(arg1) == "string" and arg1:sub(1, 1) == "@" then
-- control message
if arg1 == "@on" then
enabled = true
return
end
if arg1 == "@off" then
enabled = false
return
end
return -- ignore unknown control messages
end
if enabled then
io.stderr:write("Lua warning: ", arg1, ...)
io.stderr:write("\n")
end
end
end

return compat
8 changes: 4 additions & 4 deletions lua/pl/permute.lua
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,11 @@ end
-- @param ...
-- @see permute.order_iter
function permute.iter(...)
utils.deprecation_warning {
utils.raise_deprecation {
source = "Penlight " .. utils._VERSION,
message = "function 'iter' was renamed to 'order_iter'",
version_removed = "2.0.0",
version_deprecated = "1.9.2",
deprecated_after = "1.9.2",
}

return permute.order_iter(...)
Expand All @@ -180,11 +180,11 @@ end
-- @param ...
-- @see permute.order_iter
function permute.table(...)
utils.deprecation_warning {
utils.raise_deprecation {
source = "Penlight " .. utils._VERSION,
message = "function 'table' was renamed to 'order_table'",
version_removed = "2.0.0",
version_deprecated = "1.9.2",
deprecated_after = "1.9.2",
}

return permute.order_table(...)
Expand Down
138 changes: 81 additions & 57 deletions lua/pl/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -613,72 +613,96 @@ end
--- Deprecation
-- @section deprecation

do
-- the default implementation
local deprecation_func = function(msg, trace)
warn(msg, "\n", trace) -- luacheck: ignore
end

--- A deprecation warning function, to be overridden.
-- An application can override this function to support proper output of
-- deprecation warnings. The warnings can be generated from libraries or
-- functions by calling `utils.raise_deprecation`. By default this function
-- doesn't do anything.
--
-- Note: only applications should override this function, libraries should not.
-- @string msg the message to display/log
-- @string trace the traceback from where the deprecated element was invoked
-- @usage
-- function utils.deprecation_warning(msg, trace)
-- io.stderr:write(msg .. "\n" .. trace .."\n")
-- end
function utils.deprecation_warning(msg, trace)
-- this does nothing by default
end
--- Sets a deprecation warning function.
-- An application can override this function to support proper output of
-- deprecation warnings. The warnings can be generated from libraries or
-- functions by calling `utils.raise_deprecation`. The default function
-- will write to the 'warn' system (introduced in Lua 5.4, or the compatibility
-- function from the `compat` module for earlier versions).
--
-- Note: only applications should set/change this function, libraries should not.
-- @function a callback with signature: `function(msg, trace)` both arguments are strings.
-- @see utils.raise_deprecation
-- @usage
-- -- write to the Nginx logs with OpenResty
-- utils.set_deprecation_func(function(msg, trace)
-- ngx.log(ngx.WARN, msg, " ", trace)
-- end)
--
-- -- disable deprecation warnings
-- utils.deprecation_warning()
function utils.set_deprecation_func(func)
if func == nil then
deprecation_func = function() end
else
utils.assert_arg(1, func, "function")
deprecation_func = func
end
end

--- raises a deprecation warning.
-- For options see the usage example below.
--
-- Note: the `opts.deprecated_after` field is the last version in which
-- a feature or option was NOT YET deprecated! Because when writing the code it
-- is quite often not known in what version the code will land. But the last
-- released version is usually known.
-- @param opts options table
-- @see utils.set_deprecation_func
-- @usage
-- warn("@on") -- enable Lua warnings, they are usually off by default
--
-- function stringx.islower(str)
-- raise_deprecation {
-- source = "Penlight " .. utils._VERSION, -- optional
-- message = "function 'islower' was renamed to 'is_lower'" -- required
-- version_removed = "2.0.0", -- optional
-- deprecated_after = "1.2.3", -- optional
-- }
-- return stringx.is_lower(str)
-- end
-- -- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)"
function utils.raise_deprecation(opts)
utils.assert_arg(1, opts, "table")
if type(opts.message) ~= "string" then
error("field 'message' of the options table must be a string", 2)
end
local trace = debug.traceback("", 2):match("[\n%s]*(.-)$")
local msg
if opts.deprecated_after and opts.version_removed then
msg = (" (deprecated after %s, scheduled for removal in %s)"):format(
tostring(opts.deprecated_after), tostring(opts.version_removed))
elseif opts.deprecated_after then
msg = (" (deprecated after %s)"):format(tostring(opts.deprecated_after))
elseif opts.version_removed then
msg = (" (scheduled for removal in %s)"):format(tostring(opts.version_removed))
else
msg = ""
end

--- raises a deprecation warning.
-- For options see the usage example below.
--
-- Note: the `opts.version_deprecated` field is the last version in which
-- a feature or option was NOT YET deprecated! Because when writing the code it
-- is quite often not known in what version the code will land. But the last
-- released version is usually known.
-- @param opts options table
-- @see utils.deprecation_warning
-- @usage
-- function stringx.islower(str)
-- deprecation_warning {
-- source = "Penlight " .. utils._VERSION, -- optional
-- message = "function 'islower' was renamed to 'is_lower'" -- required
-- version_removed = "2.0.0", -- optional
-- version_deprecated = "1.2.3", -- optional
-- }
-- return stringx.is_lower(str)
-- end
-- -- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)"
function utils.raise_deprecation(opts)
utils.assert_arg(1, opts, "table")
if type(opts.message) ~= "string" then
error("field 'message' of the options table must be a string", 2)
end
local trace = debug.traceback("", 2):match("[\n%s]*(.-)$")
local msg
if opts.version_deprecated and opts.version_removed then
msg = (" (deprecated after %s, scheduled for removal in %s)"):format(
tostring(opts.version_deprecated), tostring(opts.version_removed))
elseif opts.version_deprecated then
msg = (" (deprecated after %s)"):format(tostring(opts.version_deprecated))
elseif opts.version_removed then
msg = (" (scheduled for removal in %s)"):format(tostring(opts.version_removed))
else
msg = ""
end
msg = opts.message .. msg

msg = opts.message .. msg
if opts.source then
msg = "[" .. opts.source .."] " .. msg
else
if msg:sub(1,1) == "@" then
-- in Lua 5.4 "@" prefixed messages are control messages to the warn system
error("message cannot start with '@'", 2)
end
end

if opts.source then
msg = "[" .. opts.source .."] " .. msg
deprecation_func(msg, trace)
end

utils.deprecation_warning(msg, trace)
end


return utils


40 changes: 33 additions & 7 deletions spec/utils-deprecate_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ describe("pl.utils", function ()
local old_fn, last_msg, last_trace

before_each(function()
old_fn = utils.deprecation_warning
old_fn = function() end
last_msg = nil
last_trace = nil
utils.deprecation_warning = function(msg, trace)
utils.set_deprecation_func(function(msg, trace)
last_msg = msg
last_trace = trace
end
end)
end)


Expand All @@ -21,6 +21,32 @@ describe("pl.utils", function ()



describe("set_deprecation_func", function ()

it("accepts nil as callback", function()
assert.has.no.error(function()
utils.set_deprecation_func()
end)
end)


it("accepts function as callback", function()
assert.has.no.error(function()
utils.set_deprecation_func(function() end)
end)
end)


it("fails on non-functions", function()
assert.has.error(function()
utils.set_deprecation_func("not a function")
end, "argument 1 expected a 'function', got a 'string'")
end)

end)



describe("raise_deprecation", function ()

it("requires the opts table", function()
Expand All @@ -46,7 +72,7 @@ describe("pl.utils", function ()
it("should output the deprecated version", function ()
utils.raise_deprecation {
message = "hello world",
version_deprecated = "2.0.0",
deprecated_after = "2.0.0",
}
assert.equal("hello world (deprecated after 2.0.0)", last_msg)
end)
Expand All @@ -64,7 +90,7 @@ describe("pl.utils", function ()
it("should output the deprecated and removal versions", function ()
utils.raise_deprecation {
message = "hello world",
version_deprecated = "2.0.0",
deprecated_after = "2.0.0",
version_removed = "3.0.0",
}
assert.equal("hello world (deprecated after 2.0.0, scheduled for removal in 3.0.0)", last_msg)
Expand All @@ -75,7 +101,7 @@ describe("pl.utils", function ()
utils.raise_deprecation {
source = "MyApp 1.2.3",
message = "hello world",
version_deprecated = "2.0.0",
deprecated_after = "2.0.0",
version_removed = "3.0.0",
}
assert.equal("[MyApp 1.2.3] hello world (deprecated after 2.0.0, scheduled for removal in 3.0.0)", last_msg)
Expand All @@ -87,7 +113,7 @@ describe("pl.utils", function ()
utils.raise_deprecation {
source = "MyApp 1.2.3",
message = "hello world",
version_deprecated = "2.0.0",
deprecated_after = "2.0.0",
version_removed = "3.0.0",
}
end
Expand Down
1 change: 1 addition & 0 deletions tests/test-strict.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'pl.compat' -- require this one before loading strict
local strict = require 'pl.strict'
local test = require 'pl.test'
local app = require 'pl.app'
Expand Down