Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

421 lines
17 KiB

4 years ago
  1. ## NOTE ON S3 METHODS: New S3 methods are not automatically registered. You can
  2. ## register them manually after you have inserted method_name.my_class into your
  3. ## package environment using ess-developer, like follows:
  4. ##
  5. ## registerS3method("method_name", "my_class", my_package:::method_name.my_class)
  6. ##
  7. ## If an S3 methods already exists in a package, ESS-developer will do the right
  8. ## thing.
  9. ## evaluate the STRING by saving into a file and calling .ess.ns_source
  10. .ess.ns_eval <- function(string, visibly, output, package,
  11. file = tempfile("ESSDev"), verbose = FALSE,
  12. fallback_env = NULL, local_env = parent.frame()) {
  13. cat(string, file = file)
  14. on.exit(.ess.file.remove(file))
  15. .ess.ns_source(file, visibly, output, package = package,
  16. verbose = verbose, fake.source = TRUE,
  17. fallback_env = fallback_env, local_env = local_env)
  18. }
  19. ##' Source FILE into an environment. After having a look at each new object in
  20. ##' the environment, decide what to do with it. Handles plain objects,
  21. ##' functions, existing S3 methods, S4 classes and methods.
  22. ##' @param fallback_env environment to assign objects which don't exist in the
  23. ##' package namespace
  24. .ess.ns_source <- function(file, visibly, output, expr,
  25. package = "", verbose = FALSE,
  26. fake.source = FALSE,
  27. fallback_env = NULL,
  28. local_env = NULL) {
  29. oldopts <- options(warn = 2)
  30. on.exit(options(oldopts))
  31. pname <- paste("package:", package, sep = "")
  32. envpkg <- tryCatch(as.environment(pname), error = function(cond) NULL)
  33. if (is.null(envpkg))
  34. if (require(package, quietly = TRUE, character.only = TRUE)) {
  35. envpkg <- tryCatch(as.environment(pname), error = function(cond) NULL)
  36. } else {
  37. ## no such package; source in current (local) user environment
  38. return(.ess.source(file, visibly = visibly,
  39. output = output, local = local_env,
  40. fake.source = fake.source))
  41. }
  42. envns <- tryCatch(asNamespace(package), error = function(cond) NULL)
  43. if (is.null(envns))
  44. stop(gettextf("Can't find a namespace environment corresponding to package name '%s\"",
  45. package), domain = NA)
  46. ## Here we know that both envns and envpkg exists and are environments
  47. if (is.null(fallback_env))
  48. fallback_env <- .ess.ns_insert_essenv(envns)
  49. ## Get all Imports envs where we propagate objects
  50. pkgEnvNames <- Filter(.ess.is_package, search())
  51. packages <- lapply(pkgEnvNames, function(envName) substring(envName, 9))
  52. importsEnvs <- lapply(packages, function(pkgName) parent.env(asNamespace(pkgName)))
  53. ## Evaluate the FILE into new ENV
  54. env <- .ess.ns_evalSource(file, visibly, output, substitute(expr), package, fake.source)
  55. envPackage <- getPackageName(env, FALSE)
  56. if (nzchar(envPackage) && envPackage != package)
  57. warning(gettextf("Supplied package, %s, differs from package inferred from source, %s",
  58. sQuote(package), sQuote(envPackage)), domain = NA)
  59. ## Get all sourced objects, methods and classes
  60. allObjects <- objects(envir = env, all.names = TRUE)
  61. allObjects <- allObjects[!(allObjects %in% c(".cacheOnAssign", ".packageName"))]
  62. MetaPattern <- methods:::.TableMetaPattern()
  63. ClassPattern <- methods:::.ClassMetaPattern()
  64. allPlainObjects <- allObjects[!(grepl(MetaPattern, allObjects) |
  65. grepl(ClassPattern, allObjects))]
  66. allMethodTables <- allObjects[grepl(MetaPattern, allObjects)]
  67. allClassDefs <- allObjects[grepl(ClassPattern, allObjects)]
  68. ## PLAIN OBJECTS and FUNCTIONS:
  69. funcNs <- funcPkg <- newFunc <- newNs <- newObjects <- newPkg <- objectsNs <- objectsPkg <- character()
  70. dependentPkgs <- list()
  71. for (this in allPlainObjects) {
  72. thisEnv <- get(this, envir = env)
  73. thisNs <- NULL
  74. ## NS
  75. if (exists(this, envir = envns, inherits = FALSE)){
  76. thisNs <- get(this, envir = envns)
  77. if(is.function(thisNs) || is.function(thisEnv)){
  78. if(is.function(thisNs) && is.function(thisEnv)){
  79. if(.ess.differs(thisEnv, thisNs)){
  80. environment(thisEnv) <- environment(thisNs)
  81. .ess.assign(this, thisEnv, envns)
  82. funcNs <- c(funcNs, this)
  83. if(exists(".__S3MethodsTable__.", envir = envns, inherits = FALSE)){
  84. S3_table <- get(".__S3MethodsTable__.", envir = envns)
  85. if(exists(this, envir = S3_table, inherits = FALSE))
  86. .ess.assign(this, thisEnv, S3_table)
  87. }
  88. }
  89. }else{
  90. newNs <- c(newNs, this)
  91. }
  92. }else{
  93. if(!identical(thisEnv, thisNs)){
  94. .ess.assign(this, thisEnv, envns)
  95. objectsNs <- c(objectsNs, this)
  96. }
  97. }
  98. }else{
  99. newNs <- c(newNs, this)
  100. }
  101. ## PKG
  102. if (exists(this, envir = envpkg, inherits = FALSE)){
  103. thisPkg <- get(this, envir = envpkg)
  104. if(is.function(thisPkg) || is.function(thisEnv)){
  105. if(is.function(thisPkg) && is.function(thisEnv)){
  106. if(.ess.differs(thisPkg, thisEnv)){
  107. environment(thisEnv) <- environment(thisPkg)
  108. .ess.assign(this, thisEnv, envpkg)
  109. funcPkg <- c(funcPkg, this)
  110. }
  111. }else{
  112. newPkg <- c(newPkg, this)
  113. }
  114. }else{
  115. if(!identical(thisPkg, thisEnv)){
  116. .ess.assign(this, thisEnv, envpkg)
  117. objectsPkg <- c(objectsPkg, this)
  118. }
  119. }
  120. }else{
  121. newPkg <- c(newPkg, this)
  122. }
  123. if (!is.null(thisNs)) {
  124. isDependent <- .ess.ns_propagate(thisEnv, this, importsEnvs)
  125. newDeps <- stats::setNames(list(packages[isDependent]), this)
  126. dependentPkgs <- c(dependentPkgs, newDeps)
  127. }
  128. }
  129. ## deal with new plain objects and functions
  130. for (this in intersect(newPkg, newNs)) {
  131. thisEnv <- get(this, envir = env, inherits = FALSE)
  132. if (exists(this, envir = fallback_env, inherits = FALSE)){
  133. thisGl <- get(this, envir = fallback_env)
  134. if (.ess.differs(thisEnv, thisGl)) {
  135. if (is.function(thisEnv)) {
  136. environment(thisEnv) <- envns
  137. newFunc <- c(newFunc, this)
  138. } else {
  139. newObjects <- c(newObjects, this)
  140. }
  141. .ess.assign(this, thisEnv, fallback_env)
  142. if (.is.essenv(fallback_env))
  143. .ess.assign(this, thisEnv, .GlobalEnv)
  144. }
  145. } else {
  146. if (is.function(thisEnv)) {
  147. environment(thisEnv) <- envns
  148. newFunc <- c(newFunc, this)
  149. } else {
  150. newObjects <- c(newObjects, this)
  151. }
  152. .ess.assign(this, thisEnv, fallback_env)
  153. if (.is.essenv(fallback_env))
  154. .ess.assign(this, thisEnv, .GlobalEnv)
  155. }
  156. }
  157. if(length(funcNs))
  158. objectsNs <- c(objectsNs, sprintf("FUN[%s]", paste(funcNs, collapse = ", ")))
  159. if(length(funcPkg))
  160. objectsPkg <- c(objectsPkg, sprintf("FUN[%s]", paste(funcPkg, collapse = ", ")))
  161. if(length(newFunc))
  162. newObjects <- c(newObjects, sprintf("FUN[%s]", paste(newFunc, collapse = ", ")))
  163. ## CLASSES
  164. classesPkg <- classesNs <- newClasses <- character()
  165. for(this in allClassDefs){
  166. newPkg <- newNs <- FALSE
  167. thisEnv <- get(this, envir = env)
  168. if(exists(this, envir = envpkg, inherits = FALSE)){
  169. if(!.ess.identicalClass(thisEnv, get(this, envir = envpkg))){
  170. .ess.assign(this, thisEnv, envir = envpkg)
  171. classesPkg <- c(classesPkg, this)
  172. }
  173. }else{
  174. newPkg <- TRUE
  175. }
  176. if(exists(this, envir = envns, inherits = FALSE)){
  177. if(!.ess.identicalClass(thisEnv, get(this, envir = envns))){
  178. .ess.assign(this, thisEnv, envir = envns)
  179. classesNs <- c(classesNs, this)
  180. }
  181. }else{
  182. newNs <- TRUE
  183. }
  184. if(newNs && newPkg){
  185. if(exists(this, envir = fallback_env, inherits = FALSE)){
  186. if(!.ess.identicalClass(thisEnv, get(this, envir = fallback_env))){
  187. .ess.assign(this, thisEnv, envir = fallback_env)
  188. newClasses <- c(newClasses, this)
  189. }
  190. }else{
  191. .ess.assign(this, thisEnv, envir = fallback_env)
  192. newClasses <- c(newClasses, this)
  193. }
  194. }
  195. }
  196. if(length(classesPkg))
  197. objectsPkg <- gettextf("CLS[%s]", sub(ClassPattern, "", paste(classesPkg, collapse = ", ")))
  198. if(length(classesNs))
  199. objectsNs <- gettextf("CLS[%s]", sub(ClassPattern, "", paste(classesNs, collapse = ", ")))
  200. if(length(newClasses))
  201. newObjects <- gettextf("CLS[%s]", sub(ClassPattern, "", paste(newClasses, collapse = ", ")))
  202. ## METHODS:
  203. ## Method internals: For efficiency reasons setMethod() caches
  204. ## method definition into a global table which you can get with
  205. ## 'getMethodsForDispatch' function, and when a method is dispatched that
  206. ## table is used. When ess-developer is used to source method definitions the
  207. ## two copies of the functions are identical up to the environment. The
  208. ## environment of the cached object has namespace:foo as it's parent but the
  209. ## environment of the object in local table is precisely namspace:foo. This
  210. ## does not cause any difference in evaluation.
  211. methodNames <- allMethodTables
  212. methods <- sub(methods:::.TableMetaPrefix(), "", methodNames)
  213. methods <- sub(":.*", "", methods)
  214. methodsNs <- newMethods <- character()
  215. for (i in seq_along(methods)){
  216. table <- methodNames[[i]]
  217. tableEnv <- get(table, envir = env)
  218. if(exists(table, envir = envns, inherits = FALSE)){
  219. inserted <- .ess.ns_insertMethods(tableEnv, get(table, envir = envns), envns)
  220. if(length(inserted))
  221. methodsNs <- c(methodsNs, gettextf("%s{%s}", methods[[i]], paste(inserted, collapse = ", ")))
  222. }else if(exists(table, envir = fallback_env, inherits = FALSE)){
  223. inserted <- .ess.ns_insertMethods(tableEnv, get(table, envir = fallback_env), envns)
  224. if(length(inserted))
  225. newMethods <- c(newMethods, gettextf("%s{%s}", methods[[i]], paste(inserted, collapse = ", ")))
  226. }else{
  227. .ess.assign(table, tableEnv, envir = fallback_env)
  228. newMethods <- c(newMethods, gettextf("%s{%s}", methods[[i]], paste(objects(envir = tableEnv, all.names = T), collapse = ", ")))
  229. }
  230. }
  231. if(length(methodsNs))
  232. objectsNs <- c(objectsNs, gettextf("METH[%s]", paste(methodsNs, collapse = ", ")))
  233. if(length(newMethods))
  234. newObjects <- c(newObjects, gettextf("METH[%s]", paste(newMethods, collapse = ", ")))
  235. if (verbose) {
  236. msgs <- unlist(list(
  237. if(length(objectsPkg))
  238. sprintf("PKG: %s", paste(objectsPkg, collapse = ", ")),
  239. if(length(objectsNs))
  240. sprintf("NS: %s", paste(objectsNs, collapse = ", ")),
  241. if(length(dependentPkgs))
  242. .ess.ns_format_deps(dependentPkgs),
  243. if(length(newObjects)) {
  244. env_name <- .ess.ns_env_name(fallback_env)
  245. sprintf("%s: %s", env_name, paste(newObjects, collapse = ", "))
  246. }))
  247. if(length(msgs))
  248. .ess_mpi_message(paste(msgs, collapse = " "))
  249. }
  250. invisible(env)
  251. }
  252. .ess.ns_insertMethods <- function(tableEnv, tablePkg, envns) {
  253. inserted <- character()
  254. for(m in ls(envir = tableEnv, all.names = T)){
  255. if(exists(m, envir = tablePkg, inherits = FALSE)){
  256. thisEnv <- get(m, envir = tableEnv)
  257. thisPkg <- get(m, envir = tablePkg)
  258. if(is(thisEnv, "MethodDefinition") && is(thisPkg, "MethodDefinition") &&
  259. .ess.differs(thisEnv@.Data, thisPkg@.Data)){
  260. environment(thisEnv@.Data) <- envns
  261. ## environment of cached method in getMethodsForDispatch table is still env
  262. ## not a problem as such, but might confuse users
  263. .ess.assign(m, thisEnv, tablePkg)
  264. inserted <- c(inserted, m)
  265. }}}
  266. inserted
  267. }
  268. ## our version of R's evalSource
  269. .ess.ns_evalSource <- function(file, visibly, output, expr, package = "",
  270. fake.source = FALSE) {
  271. envns <- tryCatch(asNamespace(package), error = function(cond) NULL)
  272. if(is.null(envns))
  273. stop(gettextf("Package \"%s\" is not attached and no namespace found for it",
  274. package), domain = NA)
  275. env <- new.env(parent = envns)
  276. env[[".packageName"]] <- package
  277. methods:::setCacheOnAssign(env, TRUE)
  278. if (missing(file))
  279. eval(expr, envir = env)
  280. else if (is(file, "character"))
  281. for (f in file) {
  282. .ess.source(f, local = env, visibly = visibly,
  283. output = output, keep.source = TRUE,
  284. max.deparse.length = 300,
  285. fake.source = fake.source,
  286. message.prefix = sprintf("[%s] ", package))
  287. }
  288. else stop(gettextf("Invalid file argument: got an object of class \"%s\"",
  289. class(file)[[1]]), domain = NA)
  290. env
  291. }
  292. .ess.assign <- function(x, value, envir) {
  293. ## Cannot add bindings to locked environments
  294. exists <- exists(x, envir = envir, inherits = FALSE)
  295. if (exists && bindingIsLocked(x, envir)) {
  296. unlockBinding(x, envir)
  297. assign(x, value, envir = envir, inherits = FALSE)
  298. op <- options(warn = -1)
  299. on.exit(options(op))
  300. lockBinding(x, envir)
  301. } else if (exists || !environmentIsLocked(envir)) {
  302. assign(x, value, envir = envir, inherits = FALSE)
  303. } else {
  304. warning(sprintf("Cannot assign `%s` in locked environment", x),
  305. call. = FALSE)
  306. }
  307. invisible(NULL)
  308. }
  309. .ess.identicalClass <- function(cls1, cls2, printInfo = FALSE) {
  310. slots1 <- slotNames(class(cls1))
  311. slots2 <- slotNames(class(cls2))
  312. if(identical(slots1, slots2)){
  313. vK <- grep("versionKey", slots1)
  314. if(length(vK))
  315. slots1 <- slots2 <- slots1[-vK]
  316. out <- sapply(slots1, function(nm) identical(slot(cls1, nm), slot(cls2, nm)))
  317. if(printInfo) print(out)
  318. all(out)
  319. }
  320. }
  321. .ess.differs <- function(f1, f2) {
  322. if (is.function(f1) && is.function(f2)){
  323. !(identical(body(f1), body(f2)) && identical(args(f1), args(f2)))
  324. }else
  325. !identical(f1, f2)
  326. }
  327. .ess.is_package <- function(envName) {
  328. isPkg <- identical(substring(envName, 0, 8), "package:")
  329. isPkg && (envName != "package:base")
  330. }
  331. .ess.ns_propagate <- function(obj, name, importsEnvs) {
  332. containsObj <- vapply(importsEnvs, logical(1), FUN = function(envs) {
  333. name %in% names(envs)
  334. })
  335. lapply(importsEnvs[containsObj], .ess.assign,
  336. x = name, value = obj)
  337. containsObj
  338. }
  339. .ess.ns_format_deps <- function(dependentPkgs) {
  340. pkgs <- unique(unlist(dependentPkgs, use.names = FALSE))
  341. lapply(pkgs, function(pkg) {
  342. isDep <- vapply(dependentPkgs, function(deps) pkg %in% deps, logical(1))
  343. pkgDependentObjs <- names(dependentPkgs[isDep])
  344. sprintf("DEP:%s [%s] ", pkg, paste(pkgDependentObjs, collapse = ", "))
  345. })
  346. }
  347. .ess.ns_env_name <- function(env) {
  348. name <- environmentName(env)
  349. name <-
  350. if (name == "") "Local"
  351. else if (grepl("^essenv:", name)) "NEW"
  352. else name
  353. name
  354. }
  355. .ess.ns_insert_essenv <- function(nsenv) {
  356. if (is.character(nsenv))
  357. nsenv <- base::asNamespace(nsenv)
  358. stopifnot(isNamespace(nsenv))
  359. if (identical(nsenv, .BaseNamespaceEnv))
  360. return(.GlobalEnv)
  361. essenv_name <- sprintf("essenv:%s", environmentName(nsenv))
  362. nsenv_parent <- parent.env(nsenv)
  363. if (environmentName(nsenv_parent) == essenv_name) {
  364. return(nsenv_parent)
  365. }
  366. essenv <- new.env(parent = nsenv_parent)
  367. essenv[[".__ESSENV__."]] <- TRUE
  368. attr(essenv, "name") <- essenv_name
  369. nssym <- ".__NAMESPACE__."
  370. nssym_val <- get(nssym, envir = nsenv, inherits = FALSE)
  371. unlockBinding(nssym, nsenv)
  372. nsenv[[nssym]] <- NULL
  373. on.exit({
  374. nsenv[[nssym]] <- nssym_val
  375. lockBinding(nssym, nsenv)
  376. })
  377. parent.env(nsenv) <- essenv
  378. essenv
  379. }
  380. .is.essenv <- function(env) {
  381. exists(".__ESSENV__.", envir = env, inherits = FALSE)
  382. }