Skip to content

Commit

Permalink
Merge pull request #10914 from stevengj/with_env
Browse files Browse the repository at this point in the history
export Base.withenv
  • Loading branch information
stevengj committed Apr 20, 2015
2 parents f65befe + 468f640 commit a49ae53
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 15 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ Library improvements
* `code_llvm` now outputs stripped IR without debug info or other attached metadata.
Use `code_llvm_raw` for the unstripped output ([#10747]).

* New `withenv(var=>val, ...) do ... end` function to temporarily
modify environment variables ([#10914]).

Deprecated or removed
---------------------

Expand Down Expand Up @@ -1371,3 +1374,5 @@ Too numerous to mention.
[#10747]: https://github.com/JuliaLang/julia/issues/10747
[#10844]: https://github.com/JuliaLang/julia/issues/10844
[#10870]: https://github.com/JuliaLang/julia/issues/10870
[#10885]: https://github.com/JuliaLang/julia/issues/10885
[#10914]: https://github.com/JuliaLang/julia/issues/10914
2 changes: 2 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ end
@deprecate cholfact(A::AbstractMatrix, β::Number) cholfact(A, shift=β)
@deprecate ldltfact(A::AbstractMatrix, β::Number) ldltfact(A, shift=β)

@deprecate with_env(f::Function, key::AbstractString, val) withenv(f, key=>val)

# 0.4 discontinued functions

@noinline function subtypetree(x::DataType, level=-1)
Expand Down
16 changes: 10 additions & 6 deletions base/env.jl
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,20 @@ function show(io::IO, ::EnvHash)
end

# temporarily set and then restore an environment value
function with_env(f::Function, key::AbstractString, val)
old = get(ENV,key,nothing)
val != nothing ? (ENV[key]=val) : _unsetenv(key)
function withenv{T<:AbstractString}(f::Function, keyvals::Pair{T}...)
old = Dict{T,Any}()
for (key,val) in keyvals
old[key] = get(ENV,key,nothing)
val != nothing ? (ENV[key]=val) : _unsetenv(key)
end
try f()
finally
old != nothing ? (ENV[key]=old) : _unsetenv(key)
catch
rethrow()
for (key,val) in old
val != nothing ? (ENV[key]=val) : _unsetenv(key)
end
end
end
withenv(f::Function) = f() # handle empty keyvals case; see #10853

## misc environment-related functionality ##

Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,7 @@ export
setenv,
spawn,
success,
withenv,

# C interface
cfunction,
Expand Down
2 changes: 1 addition & 1 deletion base/pkg/entry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ function update(branch::AbstractString)
Git.run(`checkout -q $branch`)
end
# TODO: handle merge conflicts
Base.with_env("GIT_MERGE_AUTOEDIT","no") do
Base.withenv("GIT_MERGE_AUTOEDIT"=>"no") do
Git.run(`pull --rebase -q`, out=DevNull)
end
end
Expand Down
1 change: 1 addition & 0 deletions base/process.jl
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ detach(cmd::Cmd) = (cmd.detach=true; cmd)

setenv{S<:ByteString}(cmd::Cmd, env::Array{S}; dir="") = (cmd.env = ByteString[x for x in env]; setenv(cmd, dir=dir); cmd)
setenv(cmd::Cmd, env::Associative; dir="") = (cmd.env = ByteString[string(k)*"="*string(v) for (k,v) in env]; setenv(cmd, dir=dir); cmd)
setenv{T<:AbstractString}(cmd::Cmd, env::Pair{T}...; dir="") = (cmd.env = ByteString[k*"="*string(v) for (k,v) in env]; setenv(cmd, dir=dir); cmd)
setenv(cmd::Cmd; dir="") = (cmd.dir = dir; cmd)

(&)(left::AbstractCmd, right::AbstractCmd) = AndCmds(left, right)
Expand Down
17 changes: 12 additions & 5 deletions doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -676,12 +676,19 @@ System

.. function:: setenv(command, env; dir=working_dir)

Set environment variables to use when running the given command. ``env`` is either
a dictionary mapping strings to strings, or an array of strings of the form
``"var=val"``.
Set environment variables to use when running the given
command. ``env`` is either a dictionary mapping strings to strings,
an array of strings of the form ``"var=val"``, or zero or more
``"var"=>val`` pair arguments. In order to modify (rather than
replace) the existing environment, create ``env`` by ``copy(ENV)``
and then setting ``env["var"]=val`` as desired, or use ``withenv``.

The ``dir`` keyword argument can be used to specify a working directory for the
command.
The ``dir`` keyword argument can be used to specify a working
directory for the command.

.. function:: withenv(f::Function, kv::Pair...)

Execute ``f()`` in an environment that is temporarily modified (not replaced as in ``setenv``) by zero or more ``"var"=>val`` arguments ``kv``. ``withenv`` is generally used via the ``withenv(kv...) do ... end`` syntax. A value of ``nothing`` can be used to temporarily unset an environment variable (if it is set). When ``withenv`` returns, the original environment has been restored.

.. function:: pipe(from, to, ...)

Expand Down
9 changes: 6 additions & 3 deletions test/spawn.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ rm(file)
end
end

readall(setenv(`sh -c "echo \$TEST"`,["TEST=Hello World"])) == "Hello World\n"
readall(setenv(`sh -c "echo \$TEST"`,Dict("TEST"=>"Hello World"))) == "Hello World\n"
readall(setenv(`sh -c "pwd"`;dir="/")) == readall(setenv(`sh -c "cd / && pwd"`))
@test readall(setenv(`sh -c "echo \$TEST"`,["TEST=Hello World"])) == "Hello World\n"
@test readall(setenv(`sh -c "echo \$TEST"`,Dict("TEST"=>"Hello World"))) == "Hello World\n"
@test readall(setenv(`sh -c "echo \$TEST"`,"TEST"=>"Hello World")) == "Hello World\n"
@test (withenv("TEST"=>"Hello World") do
readall(`sh -c "echo \$TEST"`); end) == "Hello World\n"
@test readall(setenv(`sh -c "pwd"`;dir="..")) == readall(setenv(`sh -c "cd .. && pwd"`))

This comment has been minimized.

Copy link
@vtjnash

vtjnash Apr 21, 2015

Member

this test fails if the cwd includes a symlink:

exception on 1: ERROR: LoadError: test failed:
("/Volumes/Lion/Users/jameson/Documents/julia\n" == "/Users/jameson/Documents/no-backup/julia\n")
 in expression: readall(setenv(@cmd "sh -c \"pwd\""; dir="..")) == readall(setenv(@cmd "sh -c \"cd .. && pwd\""))

even though they are the same location:

julia> Base.samefile("/Volumes/Lion/Users/jameson/Documents/julia\n","/Users/jameson/Documents/no-backup/julia\n")
true

but in the test, the location of the left has been normalized to the actual location, whereas the "no-backup" folder on the right is a soft link

is the test wrong or is the code wrong?

This comment has been minimized.

Copy link
@tkelman

tkelman Apr 22, 2015

Contributor

I guess the code is wrong if the dir kwarg is not supposed to do any normalization? Otherwise the test should probably use Base.samefile ?

This comment has been minimized.

Copy link
@stevengj

stevengj Apr 22, 2015

Author Member

Probably the test is wrong; I'm not sure how to correct the test, though. We could use dir="/", but that would fail on Windows.

This comment has been minimized.

Copy link
@tkelman

tkelman May 3, 2015

Contributor

Am trying pwd -P in #11116, which should be more consistent with the way libuv handles cwd in uv_spawn.


# Here we test that if we close a stream with pending writes, we don't lose the writes.
str = ""
Expand Down

0 comments on commit a49ae53

Please sign in to comment.