Skip to content

Commit b5cf389

Browse files
t-kalinowskihadley
andauthored
Automatically detect package name in new_class(package=) (#459)
* add `topNamespaceName()` * fixes to prevent `S7::` class prefix in package code * fixes to prevent `S7::` class prefix in tests * add NEWS * use explicit `package=NULL` in snapshot tests --------- Co-authored-by: Hadley Wickham <[email protected]>
1 parent 6e2e582 commit b5cf389

28 files changed

+92
-65
lines changed

NEWS.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# S7 (development version)
22

3+
* `new_class()` now automatically infers the package name when called from
4+
within an R package (#459).
5+
36
* Improved error message when custom validators return invalid values (#454, #457).
47

58
* New `nameOfClass()` method exported for S7 base classes, to enable usage like
69
`inherits("foo", S7::class_character)` (#432, #458)
710

811
* Added support for more base/S3 classes (#434):
9-
`class_POSIXlt`, `class_POSIXt`, `class_matrix`, `class_array`,
10-
`class_formula`, `class_call`, `class_language`, `class_name`
12+
`class_POSIXlt`, `class_POSIXt`, `class_formula`,
13+
`class_call`, `class_language`, `class_name`
1114

1215
* Fixed S3 methods registration across packages (#422).
1316

R/aaa.R

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ new_function <- function(args = NULL,
2222
x
2323
}
2424

25+
26+
topNamespaceName <- function(env = parent.frame()) {
27+
env <- topenv(env)
28+
if (isNamespace(env))
29+
getNamespaceName(env)
30+
}
31+
2532
is_string <- function(x) {
2633
identical(class(x), "character") && length(x) == 1L && !is.na(x) && x != ""
2734
}

R/class.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
new_class <- function(
101101
name,
102102
parent = S7_object,
103-
package = NULL,
103+
package = topNamespaceName(parent.frame()),
104104
properties = list(),
105105
abstract = FALSE,
106106
constructor = NULL,

R/method-register.R

-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ register_S3_method <- function(generic, signature, method, envir = parent.frame(
107107
}
108108

109109
class <- S7_class_name(signature[[1]])
110-
# dbg(generic$name, class, method, envir)
111110
registerS3method(generic$name, class, method, envir)
112111
}
113112

R/super.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
#' For example, imagine that you have made a subclass of "integer":
2727
#'
2828
#' ```{r}
29-
#' myint <- new_class("myint", parent = class_integer)
29+
#' myint <- new_class("myint", parent = class_integer, package = NULL)
3030
#' ```
3131
#'
3232
#' Now you go to write a custom print method:

R/zzz.R

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#' S7_object
1111
S7_object <- new_class(
1212
name = "S7_object",
13+
package = NULL,
1314
parent = NULL,
1415
constructor = function() {
1516
.Call(S7_object_)
@@ -96,6 +97,7 @@ on_load_define_S7_generic <- function() {
9697
# errors if `@` is not usable.
9798
S7_generic <<- new_class(
9899
name = "S7_generic",
100+
package = NULL,
99101
properties = list(
100102
name = class_character,
101103
methods = class_environment,
@@ -114,6 +116,7 @@ S7_method <- NULL
114116
on_load_define_S7_method <- function() {
115117
S7_method <<- new_class(
116118
"S7_method",
119+
package = NULL,
117120
parent = class_function,
118121
properties = list(generic = S7_generic, signature = class_list)
119122
)

man/new_class.Rd

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/super.Rd

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/class.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@
174174
# S7 object: displays nicely
175175

176176
Code
177-
foo <- new_class("foo", properties = list(x = class_double, y = class_double))
177+
foo <- new_class("foo", properties = list(x = class_double, y = class_double),
178+
package = NULL)
178179
foo()
179180
Output
180181
<foo>
@@ -191,7 +192,7 @@
191192
# S7 object: displays objects with data nicely
192193

193194
Code
194-
text <- new_class("text", class_character)
195+
text <- new_class("text", class_character, package = NULL)
195196
text("x")
196197
Output
197198
<text> chr "x"

tests/testthat/_snaps/inherits.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
# throws informative error
1010

1111
Code
12-
foo1 <- new_class("foo1")
13-
foo2 <- new_class("foo2")
12+
foo1 <- new_class("foo1", package = NULL)
13+
foo2 <- new_class("foo2", package = NULL)
1414
check_is_S7(foo1(), foo2)
1515
Condition
1616
Error:

tests/testthat/_snaps/super.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# super(): checks to
22

33
Code
4-
foo <- new_class("foo")
4+
foo <- new_class("foo", package = NULL)
55
super(foo(), class_character)
66
Condition
77
Error in `super()`:

tests/testthat/_snaps/union.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# has useful print method
22

33
Code
4-
foo1 <- new_class("foo1")
5-
foo2 <- new_class("foo2")
4+
foo1 <- new_class("foo1", package = NULL)
5+
foo2 <- new_class("foo2", package = NULL)
66
new_union(foo1, foo2)
77
Output
88
<S7_union>: <foo1> or <foo2>

tests/testthat/helper.R

+10-3
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,15 @@ named_list <- function(...) {
8080

8181
`add<-` <- `+`
8282

83-
dbg <- function(..., .display = utils::str) {
83+
dbg <- function(..., .display = utils::str, .file = NULL) {
8484
out <- NULL
8585
exprs <- as.list(substitute(list(...)))[-1L]
8686

87+
if (!is.null(.file)) {
88+
sink(.file, append = TRUE)
89+
on.exit(sink())
90+
}
91+
8792
for (i in seq_len(...length())) {
8893
..i <- as.symbol(sprintf("..%i", i))
8994
if (eval(substitute(missing(..i)))) {
@@ -98,7 +103,7 @@ dbg <- function(..., .display = utils::str) {
98103
} else {
99104
sprintf("(%s) `%s`", name, expr)
100105
}
101-
cat(label, ": ", sep = "")
106+
cat(label, if (identical(.display, utils::str)) ": " else "\n", sep = "")
102107
.display(out <- eval(..i))
103108
}
104109

@@ -127,9 +132,11 @@ dbg <- function(..., .display = utils::str) {
127132
cat(loc, "\n")
128133
} else {
129134
cat(sprintf("(from call: %s (srcfile missing))\n", trimws(
130-
deparse1(sys.call(-2), width.cutoff = 60)
135+
deparse1(sys.call(-2) %error% sys.call(-1), width.cutoff = 60)
131136
)))
132137
}
133138

134139
invisible(out)
135140
}
141+
142+
`%error%` <- function(x, y) tryCatch(x, error = function(e) y)

tests/testthat/test-S3.R

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ test_that("new_S3_class has a print method", {
33
})
44

55
test_that("can construct objects that extend S3 classes", {
6-
ordered2 <- new_class("ordered2", parent = class_factor)
6+
ordered2 <- new_class("ordered2", parent = class_factor, package = NULL)
77
x <- ordered2(c(1L, 2L, 1L), letters[1:3])
88
expect_equal(class(x), c("ordered2", "factor", "S7_object"))
99
expect_equal(prop_names(x), character())
@@ -15,7 +15,7 @@ test_that("subclasses inherit validator", {
1515
function(.data) structure(.data, class = "foo"),
1616
function(x) if (!is.double(x)) "Underlying data must be a double"
1717
)
18-
foo2 <- new_class("foo2", foo)
18+
foo2 <- new_class("foo2", foo, package = NULL)
1919

2020
expect_snapshot(error = TRUE, foo2("a"))
2121
})

tests/testthat/test-class-spec.R

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
test_that("can work with S7 classes", {
2-
klass <- new_class("klass")
2+
klass <- new_class("klass", package = NULL)
33
expect_equal(as_class(klass), klass)
44

55
expect_equal(class_type(klass), "S7")
@@ -35,8 +35,8 @@ test_that("can work with S7 classes in packages", {
3535
})
3636

3737
test_that("can work with unions", {
38-
text <- new_class("text", class_character)
39-
number <- new_class("number", class_double)
38+
text <- new_class("text", class_character, package = NULL)
39+
number <- new_class("number", class_double, package = NULL)
4040
klass <- new_union(text, number)
4141
expect_equal(as_class(klass), klass)
4242

@@ -144,7 +144,8 @@ test_that("can work with S3 classes", {
144144

145145
test_that("can work with S7 classes that extend S3 classes", {
146146
Date <- new_S3_class("Date", constructor = function(.data = numeric()) .Date(.data))
147-
Date2 <- new_class("Date2", parent = Date, properties = list(x = class_numeric))
147+
Date2 <- new_class("Date2", parent = Date, properties = list(x = class_numeric),
148+
package = NULL)
148149

149150
expect_equal(class_type(Date2), "S7")
150151
expect_equal(class_dispatch(Date2), c("Date2", "Date", "S7_object"))

tests/testthat/test-class.R

+17-13
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ describe("S7 classes", {
1111
})
1212

1313
it("print nicely", {
14-
foo1 <- new_class("foo1", properties = list(x = class_integer, y = class_integer))
15-
foo2 <- new_class("foo2", foo1)
14+
foo1 <- new_class("foo1", properties = list(x = class_integer, y = class_integer), package = NULL)
15+
foo2 <- new_class("foo2", foo1, package = NULL)
1616

1717
expect_snapshot({
1818
foo2
@@ -85,8 +85,8 @@ describe("abstract classes", {
8585
})
8686
})
8787
it("can construct concrete subclasses", {
88-
foo1 <- new_class("foo1", abstract = TRUE)
89-
foo2 <- new_class("foo2", parent = foo1)
88+
foo1 <- new_class("foo1", abstract = TRUE, package = NULL)
89+
foo2 <- new_class("foo2", parent = foo1, package = NULL)
9090
expect_s3_class(foo2(), "foo2")
9191
})
9292
it("can use inherited validator from abstract class", {
@@ -96,9 +96,10 @@ describe("abstract classes", {
9696
abstract = TRUE,
9797
validator = function(self) {
9898
if (self@x == 2) "@x has bad value"
99-
}
99+
},
100+
package = NULL
100101
)
101-
foo2 <- new_class("foo2", parent = foo1)
102+
foo2 <- new_class("foo2", parent = foo1, package = NULL)
102103
expect_no_error(foo2(x = 1))
103104
expect_snapshot(foo2(x = 2), error = TRUE)
104105
})
@@ -112,7 +113,8 @@ describe("new_object()", {
112113
it("validates object", {
113114
foo <- new_class("foo",
114115
properties = list(x = new_property(class_double)),
115-
validator = function(self) if (self@x < 0) "x must be positive"
116+
validator = function(self) if (self@x < 0) "x must be positive",
117+
package = NULL
116118
)
117119

118120
expect_snapshot(error = TRUE, {
@@ -136,23 +138,24 @@ describe("new_object()", {
136138

137139
describe("S7 object", {
138140
it("has an S7 and S3 class", {
139-
foo <- new_class("foo")
141+
foo <- new_class("foo", package = NULL)
140142
x <- foo()
141143
expect_equal(S7_class(x), foo)
142144
expect_equal(class(x), c("foo", "S7_object"))
143145
})
144146

145147
it("displays nicely", {
146148
expect_snapshot({
147-
foo <- new_class("foo", properties = list(x = class_double, y = class_double))
149+
foo <- new_class("foo", properties = list(x = class_double, y = class_double),
150+
package = NULL)
148151
foo()
149152
str(list(foo()))
150153
})
151154
})
152155

153156
it("displays objects with data nicely", {
154157
expect_snapshot({
155-
text <- new_class("text", class_character)
158+
text <- new_class("text", class_character, package = NULL)
156159
text("x")
157160
str(list(text("x")))
158161
})
@@ -162,7 +165,8 @@ describe("S7 object", {
162165
foo1 <- new_class(
163166
"foo1",
164167
parent = class_list,
165-
properties = list(x = class_double, y = class_list)
168+
properties = list(x = class_double, y = class_list),
169+
package = NULL
166170
)
167171
expect_snapshot(
168172
foo1(
@@ -213,8 +217,8 @@ describe("default constructor", {
213217
})
214218

215219
it("initializes property with S7 object", {
216-
foo1 <- new_class("foo1")
217-
foo2 <- new_class("foo2", properties = list(x = foo1))
220+
foo1 <- new_class("foo1", package = NULL)
221+
foo2 <- new_class("foo2", properties = list(x = foo1), package = NULL)
218222
x <- foo2()
219223
expect_s3_class(x@x, "foo1")
220224
})

tests/testthat/test-constructor.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ test_that("can create constructors with missing or lazy defaults", {
164164

165165
test_that("Dynamic settable properties are included in constructor", {
166166
Foo <- new_class(
167-
name = "Foo",
167+
name = "Foo", package = NULL,
168168
properties = list(
169169
dynamic_settable = new_property(
170170
class_numeric,

tests/testthat/test-convert.R

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
test_that("can register convert methods", {
22
local_methods(convert)
3-
converttest <- new_class("converttest")
3+
converttest <- new_class("converttest", package = NULL)
44
method(convert, list(converttest, class_character)) <- function(from, to, ...) "c"
55
method(convert, list(converttest, class_integer)) <- function(from, to, ...) "i"
66

@@ -25,17 +25,17 @@ describe("fallback convert", {
2525
local_methods(convert)
2626

2727
it("can convert to own class", {
28-
foo1 <- new_class("foo1")
29-
foo2 <- new_class("foo2", foo1)
28+
foo1 <- new_class("foo1", package = NULL)
29+
foo2 <- new_class("foo2", foo1, package = NULL)
3030

3131
obj <- convert(foo2(), to = foo2)
3232
expect_equal(class(obj), c("foo2", "foo1", "S7_object"))
3333
expect_equal(S7_class(obj), foo2)
3434
})
3535

3636
it("can convert to super class", {
37-
foo1 <- new_class("foo1", properties = list(x = class_double))
38-
foo2 <- new_class("foo2", foo1, properties = list(y = class_double))
37+
foo1 <- new_class("foo1", properties = list(x = class_double), package = NULL)
38+
foo2 <- new_class("foo2", foo1, properties = list(y = class_double), package = NULL)
3939

4040
obj <- convert(foo2(1, 2), to = foo1)
4141
expect_equal(class(obj), c("foo1", "S7_object"))

tests/testthat/test-generic.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ test_that("check_dispatch_args() produces informative errors", {
3131

3232
test_that("S7_generic printing", {
3333
foo1 <- new_generic("foo1", "x")
34-
text <- new_class("text")
34+
text <- new_class("text", package = NULL)
3535

3636
method(foo1, class_character) <- function(x) 1
3737
method(foo1, text) <- function(x) 2

tests/testthat/test-inherits.R

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ test_that("checks that input is a class", {
1515

1616
test_that("throws informative error", {
1717
expect_snapshot(error = TRUE, {
18-
foo1 <- new_class("foo1")
19-
foo2 <- new_class("foo2")
18+
foo1 <- new_class("foo1", package = NULL)
19+
foo2 <- new_class("foo2", package = NULL)
2020
check_is_S7(foo1(), foo2)
2121
})
2222
expect_snapshot(check_is_S7("a"), error = TRUE)

tests/testthat/test-method-dispatch.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ test_that("can dispatch on base 'union' types", {
133133
test_that("single dispatch fails with informative messages", {
134134
fail <- new_generic("fail", "x")
135135

136-
foo <- new_class("foo")
136+
foo <- new_class("foo", package = NULL)
137137
Foo <- setClass("Foo", slots = list("x" = "numeric"))
138138
on.exit(S4_remove_classes("Foo"))
139139

0 commit comments

Comments
 (0)