Skip to content

Commit 60f5ed5

Browse files
authored
Initial infrastructure for editions: attempt 2 (#6756)
* basic utilities * tie in editions with deprecation * add 2025 edition for testing purposes * add utility for supersession * use supersede utility * move `deprecate()` and `supersede()` * fix typos * document `set_edition()` * rename file * redocument * user facing funs first * fix bug * avoid `as.character(NULL)` * add tests * fix bug * fix bug * include test for `edition_require()` * unrustle pkgdown's jimmies * roll back defunctness comparison * use mocked test * correct snapshot * Ensure example also unsets edition * rename `set_edition()` to `set_ggplot2_edition()` to avoid namespace clashes * add withr-style edition options * further avoidance of namespace clashes * Document/export also related functions
1 parent 29ecfa8 commit 60f5ed5

File tree

14 files changed

+305
-34
lines changed

14 files changed

+305
-34
lines changed

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ Collate:
280280
'utilities-break.R'
281281
'utilities-grid.R'
282282
'utilities-help.R'
283+
'utilities-lifecycle.R'
283284
'utilities-patterns.R'
284285
'utilities-performance.R'
285286
'utilities-resolution.R'

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ export(geom_vline)
419419
export(get_alt_text)
420420
export(get_element_tree)
421421
export(get_geom_defaults)
422+
export(get_ggplot2_edition)
422423
export(get_guide_data)
423424
export(get_labs)
424425
export(get_last_plot)
@@ -493,6 +494,7 @@ export(layer_grob)
493494
export(layer_scales)
494495
export(layer_sf)
495496
export(lims)
497+
export(local_ggplot2_edition)
496498
export(make_constructor)
497499
export(map_data)
498500
export(margin)
@@ -666,6 +668,7 @@ export(scale_y_reverse)
666668
export(scale_y_sqrt)
667669
export(scale_y_time)
668670
export(sec_axis)
671+
export(set_ggplot2_edition)
669672
export(set_last_plot)
670673
export(set_theme)
671674
export(sf_transform_xy)
@@ -750,6 +753,7 @@ export(update_stat_defaults)
750753
export(update_theme)
751754
export(vars)
752755
export(waiver)
756+
export(with_ggplot2_edition)
753757
export(wrap_dims)
754758
export(xlab)
755759
export(xlim)

R/annotation-logticks.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ annotation_logticks <- function(base = 10, sides = "bl", outside = FALSE, scaled
9393
if (!is.null(color))
9494
colour <- color
9595

96-
lifecycle::signal_stage("superseded", "annotation_logticks()", "guide_axis_logticks()")
96+
supersede("2026", "annotation_logticks()", "guide_axis_logticks()")
9797

9898
if (lifecycle::is_present(size)) {
9999
deprecate("3.5.0", I("Using the `size` aesthetic in this geom"), I("`linewidth`"))

R/coord-flip.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
#' geom_area() +
5858
#' coord_flip()
5959
coord_flip <- function(xlim = NULL, ylim = NULL, expand = TRUE, clip = "on") {
60-
lifecycle::signal_stage("superseded", "coord_flip()")
60+
supersede("2026", "coord_flip()", with = I("swapping x and y aesthetics"))
6161
check_coord_limits(xlim)
6262
check_coord_limits(ylim)
6363
ggproto(NULL, CoordFlip,

R/coord-map.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ coord_map <- function(projection="mercator", ..., parameters = NULL, orientation
136136
} else {
137137
params <- parameters
138138
}
139-
lifecycle::signal_stage("superseded", "coord_map()", "coord_sf()")
139+
supersede("2026", "coord_map()", "coord_sf()")
140140
check_coord_limits(xlim)
141141
check_coord_limits(ylim)
142142

R/coord-polar.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
coord_polar <- function(theta = "x", start = 0, direction = 1, clip = "on") {
44
theta <- arg_match0(theta, c("x", "y"))
55
r <- if (theta == "x") "y" else "x"
6-
lifecycle::signal_stage("superseded", "coord_polar()", "coord_radial()")
6+
supersede("2026", "coord_polar()", "coord_radial()")
77

88
ggproto(NULL, CoordPolar,
99
theta = theta,

R/ggplot-global.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,5 @@ ggplot_global$x_aes <- c("x", "xmin", "xmax", "xend", "xintercept",
5252

5353
ggplot_global$y_aes <- c("y", "ymin", "ymax", "yend", "yintercept",
5454
"ymin_final", "ymax_final", "lower", "middle", "upper", "y0")
55+
56+
ggplot_global$edition <- NULL

R/labels.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,21 +239,21 @@ labs <- function(..., title = waiver(), subtitle = waiver(),
239239
#' superseded. It is recommended to use the `labs(x, y, title, subtitle)`
240240
#' arguments instead.
241241
xlab <- function(label) {
242-
lifecycle::signal_stage("superseded", "xlab()", "labs(x)")
242+
supersede("2026", "xlab()", "labs(x)")
243243
labs(x = label)
244244
}
245245

246246
#' @rdname labs
247247
#' @export
248248
ylab <- function(label) {
249-
lifecycle::signal_stage("superseded", "ylab()", "labs(y)")
249+
supersede("2026", "ylab()", "labs(y)")
250250
labs(y = label)
251251
}
252252

253253
#' @rdname labs
254254
#' @export
255255
ggtitle <- function(label, subtitle = waiver()) {
256-
lifecycle::signal_stage("superseded", "ggtitle()", I("labs(title, subtitle)"))
256+
supersede("2026", "ggtitle()", I("labs(title, subtitle)"))
257257
labs(title = label, subtitle = subtitle)
258258
}
259259

R/limits.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ limits.POSIXlt <- function(lims, var, call = caller_env()) {
184184
expand_limits <- function(...) {
185185
data <- list2(...)
186186

187-
lifecycle::signal_stage("superseded", "expand_limits()")
187+
188+
supersede("2026", "expand_limits()")
188189

189190
# unpack data frame columns
190191
data_dfs <- vapply(data, is.data.frame, logical(1))

R/utilities-lifecycle.R

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#' Set ggplot2 edition
2+
#'
3+
#' ggplot2 uses the 'edition' concept to manage the lifecycles of functions and
4+
#' arguments. Setting a recent edition opens up the latest features but also
5+
#' closes down deprecated and superseded functionality.
6+
#'
7+
#' @param edition An edition. Possible values currently include `"2026"` only.
8+
#' Can be `NULL` (default) to unset an edition.
9+
#'
10+
#' @returns For `set_ggplot2_edition()`, the previous `edition` value and for
11+
#' `local_ggplot2_edition()`: `NULL`. Thesef unction are called for the side
12+
#' effect of setting the edition though. For `with_ggplot2_edition`, the
13+
#' result of the evaluation. For `get_ggplot2_edition()`, the currently
14+
#' active edition.
15+
#'
16+
#' @export
17+
#' @keywords internal
18+
#'
19+
#' @examples
20+
#' # Setting an edition
21+
#' set_ggplot2_edition(2026)
22+
#'
23+
#' # Getting the edition
24+
#' get_ggplot2_edition()
25+
#'
26+
#'
27+
#' # Unsetting an edition
28+
#' set_ggplot2_edition()
29+
#'
30+
#' # Using withr-like scoping
31+
#' with_ggplot2_edition(2026, get_ggplot2_edition())
32+
set_ggplot2_edition <- function(edition = NULL) {
33+
old <- ggplot_global$edition
34+
ggplot_global$edition <- validate_edition(edition)
35+
invisible(old)
36+
}
37+
38+
#' @export
39+
#' @param env An `environment` to use for scoping.
40+
#' @rdname set_ggplot2_edition
41+
local_ggplot2_edition <- function(edition = NULL, env = parent.frame()) {
42+
old <- set_ggplot2_edition(edition)
43+
withr::defer(set_ggplot2_edition(old), envir = env)
44+
}
45+
46+
#' @export
47+
#' @param code Code to execute in the temporary environment.
48+
#' @rdname set_ggplot2_edition
49+
with_ggplot2_edition <- function(edition = NULL, code) {
50+
local_ggplot2_edition(edition)
51+
code
52+
}
53+
54+
#' @export
55+
#' @rdname set_ggplot2_edition
56+
get_ggplot2_edition <- function() {
57+
ggplot_global$edition[[1]]
58+
}
59+
60+
# Any new editions should be appended here and anchored to a version
61+
edition_versions <- c(
62+
"2025" = "4.0.0",
63+
"2026" = "4.1.0"
64+
)
65+
66+
validate_edition <- function(edition, allow_null = TRUE, call = caller_env()) {
67+
if (is.null(edition) && allow_null) {
68+
return(NULL)
69+
}
70+
edition <- as.character(edition)
71+
check_string(edition, allow_empty = FALSE, call = call)
72+
arg_match0(edition, names(edition_versions), error_call = call)
73+
}
74+
75+
edition_require <- function(edition = NULL, what, call = caller_env()) {
76+
edition <- validate_edition(edition)
77+
current_edition <- get_ggplot2_edition()
78+
if (
79+
!is.null(current_edition) &&
80+
as.numeric(current_edition) >= as.numeric(edition)
81+
) {
82+
return(invisible())
83+
}
84+
cli::cli_abort(
85+
"{what} requires the {edition} edition of {.pkg ggplot2}.",
86+
call = call
87+
)
88+
}
89+
90+
deprecate <- function(when, ..., id = NULL, always = FALSE, user_env = NULL,
91+
escalate = NULL) {
92+
93+
defunct <- "3.0.0"
94+
full <- "3.4.0"
95+
soft <- utils::packageVersion("ggplot2")
96+
97+
if (identical(escalate, "delay")) {
98+
soft <- full
99+
full <- defunct
100+
defunct <- "0.0.0"
101+
}
102+
103+
edition <- get_ggplot2_edition()
104+
if (!is.null(edition) && edition %in% names(edition_versions)) {
105+
soft <- full <- defunct <- edition_versions[[edition]]
106+
}
107+
108+
version <- as.package_version(when)
109+
if (version < defunct || identical(escalate, "abort")) {
110+
lifecycle::deprecate_stop(when, ...)
111+
}
112+
user_env <- user_env %||% getOption("ggplot2_plot_env") %||% caller_env(2)
113+
if (version <= full || identical(escalate, "warn")) {
114+
lifecycle::deprecate_warn(when, ..., id = id, always = always, user_env = user_env)
115+
} else if (version <= soft) {
116+
lifecycle::deprecate_soft(when, ..., id = id, user_env = user_env)
117+
}
118+
invisible()
119+
}
120+
121+
supersede <- function(edition, what, with = NULL, ..., env = caller_env()) {
122+
current_edition <- get_ggplot2_edition()
123+
if (
124+
!is.null(current_edition) &&
125+
current_edition %in% names(edition_versions) &&
126+
as.numeric(current_edition) >= as.numeric(edition)
127+
) {
128+
lifecycle::deprecate_stop(
129+
when = paste0("edition ", edition),
130+
what = what,
131+
with = with,
132+
env = env
133+
)
134+
}
135+
lifecycle::signal_stage(
136+
stage = "superseded",
137+
what = what,
138+
with = with,
139+
env = env
140+
)
141+
}

0 commit comments

Comments
 (0)