js -d:nodejs now supports osenv: getEnv, putEnv, envPairs, delEnv, existsEnv (v2) (#15826)

* js -d:nodejs now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`

* refactor to osenv

* fix for js (without -d:nodejs) + VM

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
Timothee Cour 2020-11-12 07:36:57 -06:00 • committed by GitHub
commit cc882917fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 272 additions and 208 deletions

View file

@ -31,6 +31,8 @@
- Removed deprecated `iup` module from stdlib, it has already moved to - Removed deprecated `iup` module from stdlib, it has already moved to
[nimble](https://github.com/nim-lang/iup). [nimble](https://github.com/nim-lang/iup).
- Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup).
- nodejs now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`
- `doAssertRaises` now correctly handles foreign exceptions. - `doAssertRaises` now correctly handles foreign exceptions.
## Language changes ## Language changes

View file

@ -3,29 +3,65 @@
when not declared(os) and not declared(ospaths): when not declared(os) and not declared(ospaths):
{.error: "This is an include file for os.nim!".} {.error: "This is an include file for os.nim!".}
when defined(windows): when defined(nodejs):
proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
var ret: cstring
let key2 = key.cstring
{.emit: "`ret` = process.env[`key2`];".}
result = $ret
proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
var key2 = key.cstring
var ret: bool
{.emit: "`ret` = `key2` in process.env;".}
result = ret
proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
var key2 = key.cstring
var val2 = val.cstring
{.emit: "process.env[`key2`] = `val2`;".}
proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
var key2 = key.cstring
{.emit: "delete process.env[`key2`];".}
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
var num: int
var keys: RootObj
{.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".}
for i in 0..<num:
var key, value: cstring
{.emit: "`key` = `keys`[`i`]; `value` = process.env[`key`];".}
yield ($key, $value)
# commented because it must keep working with js+VM
# elif defined(js):
# {.error: "requires -d:nodejs".}
else:
when defined(windows):
from parseutils import skipIgnoreCase from parseutils import skipIgnoreCase
proc c_getenv(env: cstring): cstring {. proc c_getenv(env: cstring): cstring {.
importc: "getenv", header: "<stdlib.h>".} importc: "getenv", header: "<stdlib.h>".}
proc c_putenv(env: cstring): cint {. proc c_putenv(env: cstring): cint {.
importc: "putenv", header: "<stdlib.h>".} importc: "putenv", header: "<stdlib.h>".}
proc c_unsetenv(env: cstring): cint {. proc c_unsetenv(env: cstring): cint {.
importc: "unsetenv", header: "<stdlib.h>".} importc: "unsetenv", header: "<stdlib.h>".}
# Environment handling cannot be put into RTL, because the ``envPairs`` # Environment handling cannot be put into RTL, because the ``envPairs``
# iterator depends on ``environment``. # iterator depends on ``environment``.
var var
envComputed {.threadvar.}: bool envComputed {.threadvar.}: bool
environment {.threadvar.}: seq[string] environment {.threadvar.}: seq[string]
when defined(nimV2): when defined(nimV2):
proc unpairedEnvAllocs*(): int = proc unpairedEnvAllocs*(): int =
result = environment.len result = environment.len
if result > 0: inc result if result > 0: inc result
when defined(windows) and not defined(nimscript): when defined(windows) and not defined(nimscript):
# because we support Windows GUI applications, things get really # because we support Windows GUI applications, things get really
# messy here... # messy here...
when useWinUnicode: when useWinUnicode:
@ -66,7 +102,7 @@ when defined(windows) and not defined(nimscript):
discard freeEnvironmentStringsA(env) discard freeEnvironmentStringsA(env)
envComputed = true envComputed = true
else: else:
const const
useNSGetEnviron = (defined(macosx) and not defined(ios) and not defined(emscripten)) or defined(nimscript) useNSGetEnviron = (defined(macosx) and not defined(ios) and not defined(emscripten)) or defined(nimscript)
@ -98,7 +134,7 @@ else:
inc(i) inc(i)
envComputed = true envComputed = true
proc findEnvVar(key: string): int = proc findEnvVar(key: string): int =
getEnvVarsC() getEnvVarsC()
var temp = key & '=' var temp = key & '='
for i in 0..high(environment): for i in 0..high(environment):
@ -108,7 +144,7 @@ proc findEnvVar(key: string): int =
if startsWith(environment[i], temp): return i if startsWith(environment[i], temp): return i
return -1 return -1
proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} =
## Returns the value of the `environment variable`:idx: named `key`. ## Returns the value of the `environment variable`:idx: named `key`.
## ##
## If the variable does not exist, `""` is returned. To distinguish ## If the variable does not exist, `""` is returned. To distinguish
@ -135,7 +171,7 @@ proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].}
if env == nil: return TaintedString(default) if env == nil: return TaintedString(default)
result = TaintedString($env) result = TaintedString($env)
proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
## Checks whether the environment variable named `key` exists. ## Checks whether the environment variable named `key` exists.
## Returns true if it exists, false otherwise. ## Returns true if it exists, false otherwise.
## ##
@ -153,7 +189,7 @@ proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
if c_getenv(key) != nil: return true if c_getenv(key) != nil: return true
else: return findEnvVar(key) >= 0 else: return findEnvVar(key) >= 0
proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
## Sets the value of the `environment variable`:idx: named `key` to `val`. ## Sets the value of the `environment variable`:idx: named `key` to `val`.
## If an error occurs, `OSError` is raised. ## If an error occurs, `OSError` is raised.
## ##
@ -187,7 +223,7 @@ proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
if c_putenv(environment[indx]) != 0'i32: if c_putenv(environment[indx]) != 0'i32:
raiseOSError(osLastError()) raiseOSError(osLastError())
proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
## Deletes the `environment variable`:idx: named `key`. ## Deletes the `environment variable`:idx: named `key`.
## If an error occurs, `OSError` is raised. ## If an error occurs, `OSError` is raised.
## ##
@ -212,7 +248,7 @@ proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
raiseOSError(osLastError()) raiseOSError(osLastError())
environment.delete(indx) environment.delete(indx)
iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} =
## Iterate over all `environments variables`:idx:. ## Iterate over all `environments variables`:idx:.
## ##
## In the first component of the tuple is the name of the current variable stored, ## In the first component of the tuple is the name of the current variable stored,

View file

@ -19,3 +19,29 @@ block:
if not isWindows: if not isWindows:
doAssert cwd.isAbsolute doAssert cwd.isAbsolute
doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo" doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo"
import std/sequtils
template main =
putEnv("foo", "bar")
doAssert getEnv("foo") == "bar"
doAssert existsEnv("foo")
putEnv("foo", "")
doAssert existsEnv("foo")
putEnv("foo", "bar2")
doAssert getEnv("foo") == "bar2"
when nimvm:
discard
else:
# need support in vmops: envPairs, delEnv
let s = toSeq(envPairs())
doAssert ("foo", "bar2") in s
doAssert ("foo", "bar") notin s
delEnv("foo")
doAssert not existsEnv("foo")
static: main()
main()