编辑后如何在活动的 Julia 会话中重新加载模块?

Posted

技术标签:

【中文标题】编辑后如何在活动的 Julia 会话中重新加载模块?【英文标题】:How do I reload a module in an active Julia session after an edit? 【发布时间】:2014-09-21 15:01:00 【问题描述】:

2018 年更新:请务必查看所有回复,因为多年来,此问题的答案已多次更改。在本次更新时,Revise.jl 答案可能是最好的解决方案。

我有一个文件“/SomeAbsolutePath/ctbTestModule.jl”,其内容是:

module ctbTestModule
export f1
f1(x) = x + 1
end

我在运行“~/.juliarc.jl”的终端中启动 Julia。启动代码包括以下行:

push!(LOAD_PATH, "/SomeAbsolutePath/")

因此我可以立即在 Julia 控制台中输入:

using ctbTestModule

加载我的模块。正如预期的那样,f1(1) 返回2。现在我突然决定要编辑f1。我在编辑器中打开“/SomeAbsolutePath/ctbTestModule.jl”,并将内容更改为:

module ctbTestModule
export f1
f1(x) = x + 2
end

我现在尝试在我的活动 Julia 会话中重新加载模块。我试试

using ctbTestModule

f1(1) 仍然返回2。接下来我试试:

reload("ctbTestModule")

如建议的here,但f1(1) 仍返回2。最后,我尝试:

include("/SomeAbsolutePath/ctbTestModule.jl")

按照here 的建议,这不是理想的,因为我必须输入完整的绝对路径,因为当前目录可能不是“/SomeAbsolutePath”。我收到警告消息Warning: replacing module ctbTestModule,这听起来很有希望,但f1(1) 仍然返回2

如果我关闭当前的 Julia 会话,启动一个新会话,然后输入 using ctbTestModule,我现在会得到所需的行为,即 f1(1) 返回 3。但显然我想这样做 不重新启动 Julia。

那么,我做错了什么?

其他详细信息:Ubuntu 14.04 上的 Julia v0.2。

【问题讨论】:

感谢您提供 2018 年更新。仅仅因为 workspace() 在 Julia 中存在已经很长时间了,我现在甚至接受 miguelmorin 的答案,而不是以前接受的答案,该答案后来已被弃用。 @NoseKnowsAll 老实说,我不确定在这种情况下应该采取什么样的礼仪。接受的答案不一定是正确的。相反,常见问题解答指出它是提问者认为最有帮助的一个。对我来说,这是目前公认的答案。鉴于我在帖子顶部的 2018 年更新将读者引向了正确的方向,我认为最好保持现状。 【参考方案1】:

这个问题的基础是重新加载模块的汇合,但无法重新定义模块中的东西Main(see the documentation here)——即是at least until the new function workspace() was made available,2014 年 7 月 13 日。最新版本的 0.3 预发行版应该有它。

工作区()之前

考虑以下简单的模块

module TstMod
export f

function f()
   return 1
end

end

那就用吧....

julia> using TstMod

julia> f()
1

如果函数 f() 更改为 return 2 并重新加载模块,则 f 实际上已更新。但未在模块 Main 中重新定义。

julia> reload("TstMod")
Warning: replacing module TstMod

julia> TstMod.f()
2

julia> f()
1

以下警告使问题更清楚

julia> using TstMod
Warning: using TstMod.f in module Main conflicts with an existing identifier.

julia> using TstMod.f
Warning: ignoring conflicting import of TstMod.f into Main

使用工作区()

但是,新函数 workspace() 会清除 Main 以准备重新加载 TstMod

julia> workspace()

julia> reload("TstMod")

julia> using TstMod

julia> f()
2

此外,之前的 Main 存储为 LastMain

julia> whos()
Base                          Module
Core                          Module
LastMain                      Module
Main                          Module
TstMod                        Module
ans                           Nothing

julia> LastMain.f()
1

【讨论】:

+1+勾选 非常感谢。一旦 *** 允许我,就会回来奖励赏金:-) clear!()clear!(:TstMod) 对我来说失败了 ERROR: invalid redefinition of constant TstMod。我不知道如何使用Base.Distributed.clear! 的documentation。 @PatrickT,你会如何使用它? 2020年有什么办法解决这个问题?发布的答案似乎都不起作用(未定义重新加载功能,1.5.3 中的工作区也未定义)。似乎是 IDE 的一个非常基本且重要的功能,用于清除环境或重新加载特定模块。 有人知道@cbartondock 问题的答案吗? 包裹 Revise 帮了我大忙【参考方案2】:

使用包Revise,例如

Pkg.add("Revise") # do this only once

include("src/my_module.jl")
using Revise
import my_module

您可能需要在新的 REPL 会话中启动它。请注意使用import 而不是using,因为using 不会重新定义Main 模块中的函数(正如@Maciek Leks 和@waTeim 所解释的那样)。

其他解决方案:与workspace() 相比,Revise.jl 的两个优点是 (1) 它更快,以及 (2) 它是面向未来的,因为 workspace() 已被弃用在 0.7 中,如 this GitHub issue 中所述:

julia> VERSION
v"0.7.0-DEV.3089"

julia> workspace()
ERROR: UndefVarError: workspace not defined

还有一位 GitHub 贡献者推荐 Revise.jl:

我们是否应该添加一些消息,例如“工作区已弃用,请查看 Revise.jl”?

即使在 Julia 0.6.3 中,当一个模块调用其他模块时,workspace()importreload 之前的三个解决方案也会失败,例如 DataFrames。使用这三种方法,当我在同一个 REPL 中第二次调用该模块时,我得到了同样的错误:

ERROR: LoadError: MethodError: all(::DataFrames.##58#59, ::ArrayAny,1) is ambiguous. Candidates: ...

我也收到了很多警告信息,例如:

WARNING: Method definition macroexpand(Module, ANY) in module Compat at /Users/mmorin/.julia/v0.6/Compat/src/Compat.jl:87 overwritten in module Compat at /Users/mmorin/.julia/v0.6/Compat/src/Compat.jl:87.

重新启动 Julia 会话有效,但很麻烦。我找到了this issue in the Reexport package,有类似的错误信息:

MethodError: all(::Reexport.##2#6, ::ArrayAny,1) is ambiguous.

并听从了一位贡献者的建议:

不使用 workspace() 会发生这种情况吗?该函数因与包交互不佳而臭名昭著,这也是它在 0.7 中被弃用的部分原因。

【讨论】:

谢谢。我在问题顶部添加了一条横幅,建议读者查看所有答案(尤其是这个答案),因为过去几年最佳解决方案已多次更改。【参考方案3】:

以我的拙见,更好的方法是从一开始就使用import 而不是using 用于报告的问题。

考虑模块:

module ModuleX1
  export produce_text
  produce_text() = begin
    println("v1.0") 
  end
  println("v1.0 loaded")
end

然后在 REPL 中:

julia> import ModuleX1
v1.0 loaded

julia> ModuleX1.produce_text()
v1.0

更新模块代码并保存:

module ModuleX1
  export produce_text
  produce_text() = begin
    println("v2.0")  
  end
  println("v2.0 loaded")
end

接下来,在 REPL 中:

julia> reload("ModuleX1")
Warning: replacing module ModuleX1
v2.0 loaded

julia> ModuleX1.produce_text()
v2.0

使用import 相对于using 的优势:

避免函数调用中的歧义(调用什么:重新加载后的 ModuleX1.produce_text() 或produce_text()?) 不必为了消除歧义而调用workspace()

使用import 优于using 的缺点:

每个导出名称的每次调用都需要一个完全限定的名称

已编辑:根据下面的对话,从“缺点...”中丢弃“对模块的完全访问权限,甚至对未导出的名称”。

【讨论】:

这很有趣。一个快速的问题:我认为只要我完全限定调用,我就可以通过using 完全访问模块中的非导出名称。这不正确吗?【参考方案4】:

workspace() 已被弃用。

您可以在活动的 REPL 会话中 reload("MyModule"),它按预期工作:对包含 MyModule 的源文件所做的更改会反映在活动的 REPL 会话中。

这适用于import MyModuleusing MyModule 纳入范围的模块

【讨论】:

您需要使用reload("MyModule")(即带引号),因为reload() 将字符串作为参数而不是模块。 我在src/my_module.jl 中有一个源文件。调用reload("src/my_module.jl") 会得到ERROR: LoadError: use 'include' instead of 'reload' to load source files。这是因为斜线,因为source code 建议在源文件有斜线时包含它。没有斜线,它说ERROR: LoadError: ArgumentError: Module my_module not found in current path.@bizzy,你如何将reload()与子目录中的源文件一起使用? @bizzy:找到了,我得把src/加到LOAD_PATHpush!(LOAD_PATH, "src/"); reload("my_module")【参考方案5】:

我想从头开始创建一个新模块,并尝试了 1.0 的不同答案,但没有得到满意的结果,但我发现以下方法对我有用:

来自我要用于我运行的项目的目录中的 Julia REPL

pkg> generate MyModule

这将创建一个类似以下结构的子目录:

MyModule
├── Project.toml
└── src
    └── MyModule.jl

我将我的模块代码放在MyModule.jl 中。我切换到目录MyModule(或在我的IDE中打开它)并添加一个文件Scratch.jl,代码如下:

using Pkg
Pkg.activate(".")
using Revise
import MyModule # or using MyModule

然后我可以在下面添加我的代码进行测试,并且所有内容都会更新,而无需重新加载 REPL。

【讨论】:

这不意味着MyModule(更像MyPackage)现在将依赖Revise.jl(所以安装你的包会自动安装Revise),即使后者只用于发展?

以上是关于编辑后如何在活动的 Julia 会话中重新加载模块?的主要内容,如果未能解决你的问题,请参考以下文章

服务器断开连接后如何在 Spring MVC 中保持客户端会话处于活动状态

重新启动或不活动期间后,Django 站点加载速度非常慢

重新加载后设置 jplist-drop-down 的值

页面重新加载后,如何使用 twitter bootstrap 保持当前选项卡处于活动状态?

重新加载页面后如何突出显示导航栏中的活动菜单项

Grails Spring Security - 会话超时后重新登录时重新加载会话变量