gpt4 book ai didi

julia - 制作宏以将函数标记为已弃用

转载 作者:行者123 更新时间:2023-12-04 08:38:00 24 4
gpt4 key购买 nike

在我之前的 question我发现标准库(Julia v1.5)宏@deprecate用于替换其他功能。
我想做一个宏mark_deprecated应用于函数时具有以下效果:

  • 调用目标函数时打印可定制的弃用警告(如果可能,仅在第一次调用时)。
  • 修改函数的文档(可查看为 julia>? function_name )以包含弃用警告。

  • 当然,稍后可能会包含许多其他方便的选项,例如指定替换功能的能力,生成错误而不是警告的选项等。
    我主要是在 Julia 元编程中做这个练习 ,到目前为止我的经验为零(有点担心这作为第一个任务可能太难了)。
    试图理解@deprecate
    作为第一步,我查看了当前的标准库 @deprecate 宏。它是这样的:
    # julia v1.5
    # quoted from deprecated.jl, included by Base.jl

    macro deprecate(old, new, ex=true)
    meta = Expr(:meta, :noinline)
    if isa(old, Symbol)
    oldname = Expr(:quote, old)
    newname = Expr(:quote, new)
    Expr(:toplevel,
    ex ? Expr(:export, esc(old)) : nothing,
    :(function $(esc(old))(args...)
    $meta
    depwarn($"`$old` is deprecated, use `$new` instead.", Core.Typeof($(esc(old))).name.mt.name)
    $(esc(new))(args...)
    end))
    elseif isa(old, Expr) && (old.head === :call || old.head === :where)
    remove_linenums!(new)
    oldcall = sprint(show_unquoted, old)
    newcall = sprint(show_unquoted, new)
    # if old.head is a :where, step down one level to the :call to avoid code duplication below
    callexpr = old.head === :call ? old : old.args[1]
    if callexpr.head === :call
    if isa(callexpr.args[1], Symbol)
    oldsym = callexpr.args[1]::Symbol
    elseif isa(callexpr.args[1], Expr) && callexpr.args[1].head === :curly
    oldsym = callexpr.args[1].args[1]::Symbol
    else
    error("invalid usage of @deprecate")
    end
    else
    error("invalid usage of @deprecate")
    end
    Expr(:toplevel,
    ex ? Expr(:export, esc(oldsym)) : nothing,
    :($(esc(old)) = begin
    $meta
    depwarn($"`$oldcall` is deprecated, use `$newcall` instead.", Core.Typeof($(esc(oldsym))).name.mt.name)
    $(esc(new))
    end))
    else
    error("invalid usage of @deprecate")
    end
    end
    我试图理解这件事(如果你理解宏观,就不需要阅读):
  • :meta事情在 documentation 中有解释.
  • 变量 oldnamenewname宏里面从来没有使用过。我认为这是由于开发人员的疏忽造成的(与尽管未使用变量但具有一些不明显效果的声明相反)。我删除它们。
  • 我不知道如何处理 a(...) where B表达式(这样的表达式进入顶层 elseif 块)。暂时不会担心那部分。好像是where无论如何,表达式都被剥离了。同 :curly表达式中的括号。似乎在任何情况下,函数符号(oldsym)都是从表达式(第一个参数)中提取的。
  • 我不明白什么Base.show_unquoted确实如此。似乎它只是为了输出将表达式“打印”成字符串,所以我不会担心细节。
  • 宏的主要内容当然是返回的Expr .它断言它是在顶级评估的。导出的事情我不在乎。
  • 我不知道是什么 Core.Typeof($(esc(oldsym))).name.mt.name是。似乎是实际的Symbol的函数(而不是包含符号的字符串)。 Core.Typeof好像和typeof一样.你可以做typeof(some_function).name.mt.name并从 mt::Core.MethodTable 中取出符号.有趣的是,Tab-Completion 似乎不适用于这些低级数据结构及其领域。

  • 朝向我的宏
    试图抄袭以上内容:
    # julia v1.5
    module MarkDeprecated

    using Markdown
    import Base.show_unquoted, Base.remove_linenums!


    """
    @mark_deprecated old msg
    Mark method `old` as deprecated.
    Print given `msg` on method call and prepend `msg` to the method's documentation.
    MACRO IS UNFINISHED AND NOT WORKING!!!!!
    """
    macro mark_deprecated(old, msg="Default deprecation warning.", new=:())
    meta = Expr(:meta, :noinline)
    if isa(old, Symbol)
    # if called with only function symbol, e.g. f, declare method f(args...)
    Expr(:toplevel,
    :(
    @doc( # This syntax is riddiculous, right?!?
    "$(Markdown.MD($"`$old` is deprecated, use `$new` instead.",
    @doc($(esc(old)))))",
    function $(esc(old))(args...)
    $meta
    warn_deprecated($"`$old` is deprecated, use `$new` instead.",
    Core.Typeof($(esc(old))).name.mt.name)
    $(esc(new))(args...)
    end
    )
    )
    )
    elseif isa(old, Expr) && (old.head === :call || old.head === :where)
    # if called with a "call", e.g. f(a::Int), or with where, e.g. f(a:A) where A <: Int,
    # try to redeclare that method
    error("not implemented yet.")
    remove_linenums!(new)
    # if old.head is a :where, step down one level to the :call to avoid code duplication below
    callexpr = old.head === :call ? old : old.args[1]
    if callexpr.head === :call
    if isa(callexpr.args[1], Symbol)
    oldsym = callexpr.args[1]::Symbol
    elseif isa(callexpr.args[1], Expr) && callexpr.args[1].head === :curly
    oldsym = callexpr.args[1].args[1]::Symbol
    else
    error("invalid usage of @mark_deprecated")
    end
    else
    error("invalid usage of @mark_deprecated")
    end
    Expr(:toplevel,
    :($(esc(old)) = begin
    $meta
    warn_deprecated($"`$oldcall` is deprecated, use `$newcall` instead.",
    Core.Typeof($(esc(oldsym))).name.mt.name)
    $(esc(old)) # TODO: this replaces the deprecated function!!!
    end))
    else
    error("invalid usage of @mark_deprecated")
    end
    end


    function warn_deprecated(msg, funcsym)
    @warn """
    Warning! Using deprecated symbol $funcsym.
    $msg
    """
    end

    end # Module MarkDeprecated
    用于检测:
    module Testing

    import ..MarkDeprecated # (if in the same file)

    a(x) = "Old behavior"
    MarkDeprecated.@mark_deprecated a "Message" print

    a("New behavior?")

    end
    问题
    到目前为止,我没有做我想做的两件事中的任何一件:
  • 调用方不导入的情况如何处理 Markdown ,我用它来连接文档字符串? (编辑:显然这不是问题?出于某种原因,尽管模块 Markdown 没有被导入到 Testing 模块中,但修改似乎仍然有效。但我不完全理解为什么。很难理解每个部分的位置宏生成的代码被执行...)
  • 我如何真正避免替换该功能?从内部调用它会创建一个无限循环。我基本上需要一个 Python 风格的装饰器?也许这样做的方法是只允许添加 @mark_deprecated到实际的函数定义? (这样的宏实际上是我期望在标准库中找到的,并且在我掉下这个兔子洞之前使用)
  • 宏(对于 @deprecate 也是如此)不影响方法 a(x)在我的例子中,因为它只创建了一个带有签名的方法 a(args...) ,当单独在函数符号上调用宏时,它对一个参数调用具有较低的优先级。虽然对我来说并不明显,但这似乎是 @deprecate 的理想行为。 .但是,是否可以将宏默认应用于裸函数符号以弃用所有方法?
  • 最佳答案

    我觉得你想达到的和Base.@deprecate 不是一回事做。如果我理解正确:

  • 您不希望宏为已弃用的方法创建定义;您更想对手写定义进行注释
  • 你想修改文档字符串,其中 @deprecate

  • 并且由于您将此作为学习元编程的练习,也许您可​​以尝试逐步编写自己的宏,而不是了解如何 Base.@deprecate工作并试图适应它。
    至于你的具体问题:
    1.调用方没有导入Markdown的情况如何处理?
    也许下面的例子有助于解释事情是如何运作的:
    module MyModule

    # Markdown.MD, Markdown.Paragraph and msg are only available from this module
    import Markdown
    msg(name) = "Hello $name"

    macro greet(name)
    quote
    # function names (e.g. Markdown.MD or msg) are interpolated
    # => evaluated at macro expansion time in the scope of the macro itself
    # => refer to functions available from within the module
    $(Markdown.MD)($(Markdown.Paragraph)($msg($name)))

    # (But these functions are not called at macro expansion time)
    end
    end
    end
    特别查看如何 msg正确指的是 Main.MyModule.msg ,这就是您必须从“外部”上下文中调用它的方式:
    julia> @macroexpand MyModule.@greet "John"
    quote
    #= REPL[8]:8 =#
    (Markdown.MD)((Markdown.Paragraph)((Main.MyModule.msg)("John")))
    end

    julia> MyModule.@greet "John"
    Hello John
    2. 也许这样做的方法是只允许将@mark_deprecated 添加到实际的函数定义中?
    是的,这就是我要做的。
    3. 是否可以将宏默认应用于裸函数符号以弃用所有方法?
    我想在技术上可以弃用给定函数的所有方法……或者至少是弃用代码运行时存在的所有方法。但是之后定义的方法呢​​?
    我个人不会那样做,只标记方法定义。

    也许像这样的东西可能是一个 stub ,用作更复杂的宏的起点,正是你想要的:
    module MarkDeprecate
    using Markdown
    using MacroTools

    function mark_docstring(docstring, message)
    push!(docstring,
    Markdown.Paragraph("Warning: this method is deprecated! $message"))
    docstring
    end

    function warn_if_necessary(message)
    @warn "This method is deprecated! $message"
    end

    macro mark_deprecate(msg, expr)
    fundef = splitdef(expr)
    prototype = :($(fundef[:name])($(fundef[:args]...);
    $(fundef[:kwargs]...)) where {$(fundef[:whereparams]...)})

    fundef[:body] = quote
    $warn_if_necessary($msg)
    $(fundef[:body])
    end

    quote
    Base.@__doc__ $(esc(MacroTools.combinedef(fundef)))
    Base.@doc $mark_docstring(@doc($prototype), $msg) $prototype
    end
    end
    end
    julia> """
    bar(x::Number)

    some help
    """
    MarkDeprecate.@mark_deprecate "Use foo instead" function bar(x::Number)
    42
    end
    bar

    julia> """
    bar(s::String)

    This one is not deprecated
    """
    bar(s::String) = "not deprecated"
    bar

    julia> methods(bar)
    # 2 methods for generic function "bar":
    [1] bar(s::String) in Main at REPL[4]:6
    [2] bar(x::Number) in Main at REPL[1]:23

    julia> @doc(bar)
    bar(x::Number)

    some help

    Warning: this method is deprecated! Use foo instead

    bar(s::String)

    This one is not deprecated

    julia> bar("hello")
    "not deprecated"

    julia> bar(5)
    ┌ Warning: This method is deprecated! Use foo instead
    └ @ Main.MarkDeprecate REPL[1]:12
    42

    关于julia - 制作宏以将函数标记为已弃用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64707416/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com