Skip to content

Encourage Imports not Depends #3076

Description

@mattdowle

CRAN + BioC: Depends Imports


I've only recently realized how bad Depends: is, thanks to Jan's importing vignette. I just made its discouragement stronger : 51590bc

We could disallow Depends. This would also be beneficial to cedta()'s awkward implementation on its last line where it needs to do the tryCatch just for packages which Depend; that line could be removed.

But before we disallow Depends, we'd need to ask 69 CRAN packages to change from Depends to Imports. (Most revdeps already Import.) The longer we leave it, the greater the potential for new packages using Depends to be added to CRAN and the harder it will be to change.

Package Host Last Upload Issue? PR? Fixed in dev
1 DiSCos CRAN 2026-07-12
2 KMD CRAN 2026-07-09
3 unvs.med CRAN 2026-06-30
4 OmicFlow CRAN 2026-05-19 2026-06-25
5 poissonsuperlearner CRAN 2026-05-18
6 CoSMoS CRAN 2026-05-07
7 pep725 CRAN 2026-04-24
8 RapidoPGS CRAN 2026-04-03
9 circhelp CRAN 2026-04-02 2024-09-04
10 rMIDAS CRAN 2026-03-13
11 didimputation CRAN 2026-03-09 2026-08-18
12 partools CRAN 2026-02-06
13 rTLS CRAN 2026-01-19
14 normfluodbf CRAN 2025-10-10
15 tma CRAN 2025-09-19
16 colocPropTest CRAN 2025-09-16
17 FORD CRAN 2025-05-30
18 damr CRAN 2025-04-21
19 behavr CRAN 2025-03-27
20 HospitalNetwork CRAN 2024-12-22 2025-07-08
21 ZIM4rv CRAN 2024-12-19
22 lori CRAN 2024-11-18
23 SOMnmR CRAN 2024-07-04
24 LKT CRAN 2024-07-01
25 SeaVal CRAN 2024-06-14
26 limexhub CRAN 2024-05-06
27 rblt CRAN 2024-02-19
28 nosoi CRAN 2024-02-09
29 tablecompare CRAN 2023-11-14
30 sqlHelpers CRAN 2023-10-14
31 PreProcessRecordLinkage CRAN 2023-09-13
32 fplyr CRAN 2023-08-23
33 IBRtools CRAN 2023-08-14
34 TrumpetPlots CRAN 2023-06-13
35 DiDforBigData CRAN 2023-04-03
36 pgTools CRAN 2023-03-24
37 rasterDT CRAN 2022-12-15
38 libbib CRAN 2022-11-05
39 mind CRAN 2022-10-27
40 acdcR CRAN 2022-06-27
41 chicane CRAN 2021-11-06
42 FeatureImpCluster CRAN 2021-10-20
43 edl CRAN 2021-09-20
44 ZIprop CRAN 2021-06-09
45 FOCI CRAN 2021-03-18
46 shinyML CRAN 2021-02-24
47 textTools CRAN 2021-02-05
48 dbWebForms CRAN 2021-01-28
49 classifierplots CRAN 2020-10-13
50 intervalaverage CRAN 2020-07-23
51 LSPFP CRAN 2020-05-13
52 eyeTrackR CRAN 2020-03-29
53 birankr CRAN 2020-03-23
54 GenomicTools.fileHandler CRAN 2020-03-05
55 gdxdt CRAN 2019-11-30
56 cdparcoord CRAN 2019-08-04
57 twl CRAN 2018-08-24
58 RWildbook CRAN 2018-04-06
59 miLineage CRAN 2018-03-23
60 Ac3net CRAN 2018-02-26
61 bea.R CRAN 2018-02-23
62 heims CRAN 2018-01-25
63 slim CRAN 2017-05-15
64 orgR CRAN 2014-12-20
65 Chicago Bioconductor 2026-04-28
66 GOTHiC Bioconductor 2026-04-28
67 GladiaTOX Bioconductor 2026-04-28
68 HMMcopy Bioconductor 2026-04-28
69 ModCon Bioconductor 2026-04-28 2026-07-16
70 RCAS Bioconductor 2026-04-28
71 TIN Bioconductor 2026-04-28
72 chimeraviz Bioconductor 2026-04-28 2026-08-18
73 deconvR Bioconductor 2026-04-28 2025-07-13
74 methrix Bioconductor 2026-04-28 2025-12-01
75 rBiopaxParser Bioconductor 2026-04-28
76 sangeranalyseR Bioconductor 2026-04-28 2026-05-05
77 SNPhood Bioconductor 2025-11-04
Repro script to re-generate this table
library(tools)
library(data.table)

# 1. Fetch CRAN metadata and reverse dependencies (Depends: data.table)
cran_db = as.data.table(CRAN_package_db())
cran_deps = package_dependencies("data.table", db = as.matrix(cran_db), reverse = TRUE, which = "Depends")$data.table
cran_sub = cran_db[Package %in% cran_deps]

# 2. Fetch Bioconductor metadata and reverse dependencies (Depends: data.table)
bioc_views_url = "https://bioconductor.org/packages/release/bioc/VIEWS"
tf = tempfile()
download.file(bioc_views_url, tf, quiet = TRUE)
bioc_views = as.data.table(read.dcf(tf))
bioc_deps = package_dependencies("data.table", db = as.matrix(bioc_views), reverse = TRUE, which = "Depends")$data.table
bioc_sub = bioc_views[Package %in% bioc_deps]

# 3. Harvest existing tracked links dynamically from GitHub issue #3076 body
issue_body = tryCatch({
  res = httr::GET("https://api.github.com/repos/Rdatatable/data.table/issues/3076", httr::user_agent("Rdatatable"))
  httr::content(res, "parsed")$body
}, error = function(e) "")

known_links = list()
fixed_links = list()
lines = strsplit(issue_body, "\r?\n")[[1]]

for (l in lines) {
  # Parse table rows: | # | [pkg](repo) | [Host](host_url) | Last Upload | [✅](issue) | [✅](pr) | [date](fix) |
  if (grepl(r"{^\|.*\|.*\|.*\|}", l)) {
    parts = strsplit(l, "\\|")[[1]]
    if (length(parts) > 0 && parts[1] == "") parts = parts[-1]
    parts = trimws(parts)
    if (length(parts) >= 4 && !any(grepl("Last Upload|:---", parts))) {
      pkg_col = if (grepl("^[0-9]+$", parts[1])) 2 else 1
      pkg = sub(r"{^\[([^]]+)\].*}", "\\1", parts[pkg_col])
      
      issue_col = pkg_col + 3
      pr_col    = pkg_col + 4
      fix_col   = pkg_col + 5
      
      if (length(parts) >= issue_col && grepl(r"{https?://}", parts[issue_col])) {
        m_iss = regmatches(parts[issue_col], regexec(r"{\((https?://[^)]+)\)}", parts[issue_col]))[[1]]
        if (length(m_iss) > 1) {
          known_links[[length(known_links) + 1]] = data.table(Package = pkg, Type = "Issue", URL = m_iss[2])
        }
      }
      if (length(parts) >= pr_col && grepl(r"{https?://}", parts[pr_col])) {
        m_pr = regmatches(parts[pr_col], regexec(r"{\((https?://[^)]+)\)}", parts[pr_col]))[[1]]
        if (length(m_pr) > 1) {
          known_links[[length(known_links) + 1]] = data.table(Package = pkg, Type = "PR", URL = m_pr[2])
        }
      }
      if (length(parts) >= fix_col && grepl(r"{https?://}", parts[fix_col])) {
        fixed_links[[length(fixed_links) + 1]] = data.table(Package = pkg, Fixed_In_Dev = parts[fix_col])
      }
    }
  }
}

links_dt = if (length(known_links) > 0) {
  dcast(unique(rbindlist(known_links)), Package ~ Type, value.var = "URL", fun.aggregate = function(x) x[1])
} else {
  data.table(Package = character(), Issue = character(), PR = character())
}

fixed_dt = if (length(fixed_links) > 0) unique(rbindlist(fixed_links)) else data.table(Package = character(), Fixed_In_Dev = character())

# Helper: extract source forge repository (GitHub/GitLab) from package fields or links
extract_repo <- function(pkg, url_fields, issue_url, pr_url) {
  ref_url = na.omit(c(issue_url, pr_url))
  if (length(ref_url) > 0) {
    m = regmatches(ref_url[1], regexec(r"{(https?://[^/]+/[^/]+/[^/#?]+)}", ref_url[1]))[[1]]
    if (length(m) > 1) {
      repo = sub(r"{/(issues|pull|tree|blob|commit)/?.*$}", "", m[2])
      repo = sub(r"{/-/issues/?.*$}", "", repo)
      return(repo)
    }
  }
  urls = unlist(strsplit(url_fields[!is.na(url_fields)], "[,\n\t ]+"))
  urls = urls[urls != ""]
  repo_urls = grep("git(hub|lab)", urls, value = TRUE)
  repo_urls = repo_urls[!grepl("\\.github\\.io", repo_urls)]
  if (length(repo_urls) > 0) {
    m = regmatches(repo_urls[1], regexec(r"{(https?://[^/]+/[^/]+/[^/#?]+)}", repo_urls[1]))[[1]]
    if (length(m) > 1) {
      repo = sub(r"{\.git$}", "", m[2])
      repo = sub(r"{/(issues|pull)/?.*$}", "", repo)
      repo = sub(r"{/-/issues/?.*$}", "", repo)
      return(repo)
    }
  }
  NA_character_
}

# CRAN data
cran_dt = data.table(
  Package_Name = cran_sub$Package,
  Host_Name = "CRAN",
  Host_URL = sprintf("https://cran.r-project.org/package=%s", cran_sub$Package),
  Last_Upload = as.character(as.Date(as.POSIXct(cran_sub[["Date/Publication"]]))),
  URL_Fields = paste(cran_sub$URL, cran_sub$BugReports, sep = ", ")
)

# Bioconductor data
bioc_dt = data.table(
  Package_Name = bioc_sub$Package,
  Host_Name = "Bioconductor",
  Host_URL = sprintf("https://bioconductor.org/packages/%s", bioc_sub$Package),
  Last_Upload = bioc_sub[["Date/Publication"]],
  URL_Fields = paste(bioc_sub$URL, bioc_sub$BugReports, bioc_sub$git_url, sep = ", ")
)

all_pkgs = rbind(cran_dt, bioc_dt)
all_pkgs = merge(all_pkgs, links_dt, by.x = "Package_Name", by.y = "Package", all.x = TRUE)
all_pkgs = merge(all_pkgs, fixed_dt, by.x = "Package_Name", by.y = "Package", all.x = TRUE)

all_pkgs[, Repo := mapply(extract_repo, Package_Name, URL_Fields, Issue, PR)]
all_pkgs[, Package := ifelse(!is.na(Repo) & Repo != "", sprintf("[%s](%s)", Package_Name, Repo), Package_Name)]
all_pkgs[, Host := sprintf("[%s](%s)", Host_Name, Host_URL)]
all_pkgs[, `Issue?` := ifelse(!is.na(Issue) & Issue != "", sprintf("[✅](%s)", Issue), "")]
all_pkgs[, `PR?` := ifelse(!is.na(PR) & PR != "", sprintf("[✅](%s)", PR), "")]
all_pkgs[, `Fixed in dev` := ifelse(!is.na(Fixed_In_Dev), Fixed_In_Dev, "")]

# Sort by Host (CRAN then Bioconductor) and descending Last_Upload
all_pkgs[, Host_Order := factor(Host_Name, levels = c("CRAN", "Bioconductor"))]
setorder(all_pkgs, Host_Order, -Last_Upload)

# Leftmost column numbered 1...n with empty header
all_pkgs[, ` ` := .I]

out_dt = all_pkgs[, .(` `, Package, Host, `Last Upload` = Last_Upload, `Issue?`, `PR?`, `Fixed in dev`)]
writeLines(knitr::kable(out_dt, format = "markdown", align = c("r", "l", "l", "l", "c", "c", "l")))

Metadata

Metadata

Assignees

No one assigned

    Labels

    breaking-changeissues whose solution would require breaking existing behavior

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions