Dean Moldovan | 1fb9df6 | 2017-08-18 19:26:49 +0200 | [diff] [blame] | 1 | Upgrade guide |
| 2 | ############# |
| 3 | |
| 4 | This is a companion guide to the :doc:`changelog`. While the changelog briefly |
| 5 | lists all of the new features, improvements and bug fixes, this upgrade guide |
| 6 | focuses only the subset which directly impacts your experience when upgrading |
| 7 | to a new version. But it goes into more detail. This includes things like |
| 8 | deprecated APIs and their replacements, build system changes, general code |
| 9 | modernization and other useful information. |
| 10 | |
Henry Schreiner | 1729aae | 2020-08-19 12:26:26 -0400 | [diff] [blame] | 11 | .. _upgrade-guide-2.6: |
| 12 | |
| 13 | v2.6 |
| 14 | ==== |
| 15 | |
Henry Schreiner | a6887b6 | 2020-08-19 14:53:59 -0400 | [diff] [blame] | 16 | An error is now thrown when ``__init__`` is forgotten on subclasses. This was |
| 17 | incorrect before, but was not checked. Add a call to ``__init__`` if it is |
| 18 | missing. |
| 19 | |
| 20 | If ``__eq__`` defined but not ``__hash__``, ``__hash__`` is now set to |
| 21 | ``None``, as in normal CPython. You should add ``__hash__`` if you intended the |
| 22 | class to be hashable, possibly using the new ``py::hash`` shortcut. |
| 23 | |
Henry Schreiner | 1729aae | 2020-08-19 12:26:26 -0400 | [diff] [blame] | 24 | CMake support: |
| 25 | -------------- |
| 26 | |
| 27 | The minimum required version of CMake is now 3.4. Several details of the CMake |
| 28 | support have been deprecated; warnings will be shown if you need to change |
| 29 | something. The changes are: |
| 30 | |
| 31 | * ``PYBIND11_CPP_STANDARD=<platform-flag>`` is deprecated, please use |
| 32 | ``CMAKE_CXX_STANDARD=<number>`` instead, or any other valid CMake CXX or CUDA |
| 33 | standard selection method, like ``target_compile_features``. |
| 34 | |
Henry Schreiner | 37f845a | 2020-09-08 09:26:50 -0400 | [diff] [blame] | 35 | * If you do not request a standard, pybind11 targets will compile with the |
Henry Schreiner | 1729aae | 2020-08-19 12:26:26 -0400 | [diff] [blame] | 36 | compiler default, but not less than C++11, instead of forcing C++14 always. |
| 37 | If you depend on the old behavior, please use ``set(CMAKE_CXX_STANDARD 14)`` |
| 38 | instead. |
| 39 | |
| 40 | * Direct ``pybind11::module`` usage should always be accompanied by at least |
| 41 | ``set(CMAKE_CXX_VISIBILITY_PRESET hidden)`` or similar - it used to try to |
| 42 | manually force this compiler flag (but not correctly on all compilers or with |
| 43 | CUDA). |
| 44 | |
| 45 | * ``pybind11_add_module``'s ``SYSTEM`` argument is deprecated and does nothing; |
| 46 | linking now behaves like other imported libraries consistently in both |
| 47 | config and submodule mode, and behaves like a ``SYSTEM`` library by |
| 48 | default. |
| 49 | |
| 50 | * If ``PYTHON_EXECUTABLE`` is not set, virtual environments (``venv``, |
| 51 | ``virtualenv``, and ``conda``) are prioritized over the standard search |
| 52 | (similar to the new FindPython mode). |
| 53 | |
| 54 | In addition, the following changes may be of interest: |
| 55 | |
| 56 | * ``CMAKE_INTERPROCEDURAL_OPTIMIZATION`` will be respected by |
| 57 | ``pybind11_add_module`` if set instead of linking to ``pybind11::lto`` or |
| 58 | ``pybind11::thin_lto``. |
| 59 | |
| 60 | * Using ``find_package(Python COMPONENTS Interpreter Development)`` before |
| 61 | pybind11 will cause pybind11 to use the new Python mechanisms instead of its |
| 62 | own custom search, based on a patched version of classic |
| 63 | FindPythonInterp/FindPythonLibs. In the future, this may become the default. |
| 64 | |
| 65 | |
Dean Moldovan | 1fb9df6 | 2017-08-18 19:26:49 +0200 | [diff] [blame] | 66 | |
| 67 | v2.2 |
| 68 | ==== |
| 69 | |
| 70 | Deprecation of the ``PYBIND11_PLUGIN`` macro |
| 71 | -------------------------------------------- |
| 72 | |
| 73 | ``PYBIND11_MODULE`` is now the preferred way to create module entry points. |
| 74 | The old macro emits a compile-time deprecation warning. |
| 75 | |
| 76 | .. code-block:: cpp |
| 77 | |
| 78 | // old |
| 79 | PYBIND11_PLUGIN(example) { |
| 80 | py::module m("example", "documentation string"); |
| 81 | |
| 82 | m.def("add", [](int a, int b) { return a + b; }); |
| 83 | |
| 84 | return m.ptr(); |
| 85 | } |
| 86 | |
| 87 | // new |
| 88 | PYBIND11_MODULE(example, m) { |
| 89 | m.doc() = "documentation string"; // optional |
| 90 | |
| 91 | m.def("add", [](int a, int b) { return a + b; }); |
| 92 | } |
| 93 | |
| 94 | |
Dean Moldovan | b8c5dbd | 2017-08-24 02:46:07 +0200 | [diff] [blame] | 95 | New API for defining custom constructors and pickling functions |
| 96 | --------------------------------------------------------------- |
| 97 | |
| 98 | The old placement-new custom constructors have been deprecated. The new approach |
| 99 | uses ``py::init()`` and factory functions to greatly improve type safety. |
| 100 | |
| 101 | Placement-new can be called accidentally with an incompatible type (without any |
| 102 | compiler errors or warnings), or it can initialize the same object multiple times |
| 103 | if not careful with the Python-side ``__init__`` calls. The new-style custom |
| 104 | constructors prevent such mistakes. See :ref:`custom_constructors` for details. |
| 105 | |
| 106 | .. code-block:: cpp |
| 107 | |
| 108 | // old -- deprecated (runtime warning shown only in debug mode) |
| 109 | py::class<Foo>(m, "Foo") |
| 110 | .def("__init__", [](Foo &self, ...) { |
| 111 | new (&self) Foo(...); // uses placement-new |
| 112 | }); |
| 113 | |
| 114 | // new |
| 115 | py::class<Foo>(m, "Foo") |
| 116 | .def(py::init([](...) { // Note: no `self` argument |
| 117 | return new Foo(...); // return by raw pointer |
| 118 | // or: return std::make_unique<Foo>(...); // return by holder |
| 119 | // or: return Foo(...); // return by value (move constructor) |
| 120 | })); |
| 121 | |
| 122 | Mirroring the custom constructor changes, ``py::pickle()`` is now the preferred |
| 123 | way to get and set object state. See :ref:`pickling` for details. |
| 124 | |
| 125 | .. code-block:: cpp |
| 126 | |
| 127 | // old -- deprecated (runtime warning shown only in debug mode) |
| 128 | py::class<Foo>(m, "Foo") |
| 129 | ... |
| 130 | .def("__getstate__", [](const Foo &self) { |
| 131 | return py::make_tuple(self.value1(), self.value2(), ...); |
| 132 | }) |
| 133 | .def("__setstate__", [](Foo &self, py::tuple t) { |
| 134 | new (&self) Foo(t[0].cast<std::string>(), ...); |
| 135 | }); |
| 136 | |
| 137 | // new |
| 138 | py::class<Foo>(m, "Foo") |
| 139 | ... |
| 140 | .def(py::pickle( |
| 141 | [](const Foo &self) { // __getstate__ |
| 142 | return py::make_tuple(f.value1(), f.value2(), ...); // unchanged |
| 143 | }, |
| 144 | [](py::tuple t) { // __setstate__, note: no `self` argument |
| 145 | return new Foo(t[0].cast<std::string>(), ...); |
| 146 | // or: return std::make_unique<Foo>(...); // return by holder |
| 147 | // or: return Foo(...); // return by value (move constructor) |
| 148 | } |
| 149 | )); |
| 150 | |
| 151 | For both the constructors and pickling, warnings are shown at module |
| 152 | initialization time (on import, not when the functions are called). |
| 153 | They're only visible when compiled in debug mode. Sample warning: |
| 154 | |
| 155 | .. code-block:: none |
| 156 | |
| 157 | pybind11-bound class 'mymodule.Foo' is using an old-style placement-new '__init__' |
| 158 | which has been deprecated. See the upgrade guide in pybind11's docs. |
| 159 | |
| 160 | |
Dean Moldovan | 1fb9df6 | 2017-08-18 19:26:49 +0200 | [diff] [blame] | 161 | Stricter enforcement of hidden symbol visibility for pybind11 modules |
| 162 | --------------------------------------------------------------------- |
| 163 | |
| 164 | pybind11 now tries to actively enforce hidden symbol visibility for modules. |
| 165 | If you're using either one of pybind11's :doc:`CMake or Python build systems |
| 166 | <compiling>` (the two example repositories) and you haven't been exporting any |
| 167 | symbols, there's nothing to be concerned about. All the changes have been done |
| 168 | transparently in the background. If you were building manually or relied on |
| 169 | specific default visibility, read on. |
| 170 | |
| 171 | Setting default symbol visibility to *hidden* has always been recommended for |
| 172 | pybind11 (see :ref:`faq:symhidden`). On Linux and macOS, hidden symbol |
| 173 | visibility (in conjunction with the ``strip`` utility) yields much smaller |
| 174 | module binaries. `CPython's extension docs`_ also recommend hiding symbols |
| 175 | by default, with the goal of avoiding symbol name clashes between modules. |
| 176 | Starting with v2.2, pybind11 enforces this more strictly: (1) by declaring |
| 177 | all symbols inside the ``pybind11`` namespace as hidden and (2) by including |
| 178 | the ``-fvisibility=hidden`` flag on Linux and macOS (only for extension |
| 179 | modules, not for embedding the interpreter). |
| 180 | |
| 181 | .. _CPython's extension docs: https://docs.python.org/3/extending/extending.html#providing-a-c-api-for-an-extension-module |
| 182 | |
| 183 | The namespace-scope hidden visibility is done automatically in pybind11's |
| 184 | headers and it's generally transparent to users. It ensures that: |
| 185 | |
| 186 | * Modules compiled with different pybind11 versions don't clash with each other. |
| 187 | |
| 188 | * Some new features, like ``py::module_local`` bindings, can work as intended. |
| 189 | |
| 190 | The ``-fvisibility=hidden`` flag applies the same visibility to user bindings |
| 191 | outside of the ``pybind11`` namespace. It's now set automatic by pybind11's |
| 192 | CMake and Python build systems, but this needs to be done manually by users |
| 193 | of other build systems. Adding this flag: |
| 194 | |
| 195 | * Minimizes the chances of symbol conflicts between modules. E.g. if two |
| 196 | unrelated modules were statically linked to different (ABI-incompatible) |
| 197 | versions of the same third-party library, a symbol clash would be likely |
| 198 | (and would end with unpredictable results). |
| 199 | |
| 200 | * Produces smaller binaries on Linux and macOS, as pointed out previously. |
| 201 | |
| 202 | Within pybind11's CMake build system, ``pybind11_add_module`` has always been |
| 203 | setting the ``-fvisibility=hidden`` flag in release mode. From now on, it's |
| 204 | being applied unconditionally, even in debug mode and it can no longer be opted |
| 205 | out of with the ``NO_EXTRAS`` option. The ``pybind11::module`` target now also |
| 206 | adds this flag to it's interface. The ``pybind11::embed`` target is unchanged. |
| 207 | |
| 208 | The most significant change here is for the ``pybind11::module`` target. If you |
| 209 | were previously relying on default visibility, i.e. if your Python module was |
| 210 | doubling as a shared library with dependents, you'll need to either export |
| 211 | symbols manually (recommended for cross-platform libraries) or factor out the |
| 212 | shared library (and have the Python module link to it like the other |
| 213 | dependents). As a temporary workaround, you can also restore default visibility |
| 214 | using the CMake code below, but this is not recommended in the long run: |
| 215 | |
| 216 | .. code-block:: cmake |
| 217 | |
| 218 | target_link_libraries(mymodule PRIVATE pybind11::module) |
| 219 | |
| 220 | add_library(restore_default_visibility INTERFACE) |
| 221 | target_compile_options(restore_default_visibility INTERFACE -fvisibility=default) |
| 222 | target_link_libraries(mymodule PRIVATE restore_default_visibility) |
| 223 | |
| 224 | |
| 225 | Local STL container bindings |
| 226 | ---------------------------- |
| 227 | |
| 228 | Previous pybind11 versions could only bind types globally -- all pybind11 |
| 229 | modules, even unrelated ones, would have access to the same exported types. |
| 230 | However, this would also result in a conflict if two modules exported the |
| 231 | same C++ type, which is especially problematic for very common types, e.g. |
| 232 | ``std::vector<int>``. :ref:`module_local` were added to resolve this (see |
| 233 | that section for a complete usage guide). |
| 234 | |
| 235 | ``py::class_`` still defaults to global bindings (because these types are |
| 236 | usually unique across modules), however in order to avoid clashes of opaque |
| 237 | types, ``py::bind_vector`` and ``py::bind_map`` will now bind STL containers |
| 238 | as ``py::module_local`` if their elements are: builtins (``int``, ``float``, |
| 239 | etc.), not bound using ``py::class_``, or bound as ``py::module_local``. For |
| 240 | example, this change allows multiple modules to bind ``std::vector<int>`` |
| 241 | without causing conflicts. See :ref:`stl_bind` for more details. |
| 242 | |
| 243 | When upgrading to this version, if you have multiple modules which depend on |
| 244 | a single global binding of an STL container, note that all modules can still |
| 245 | accept foreign ``py::module_local`` types in the direction of Python-to-C++. |
| 246 | The locality only affects the C++-to-Python direction. If this is needed in |
| 247 | multiple modules, you'll need to either: |
| 248 | |
| 249 | * Add a copy of the same STL binding to all of the modules which need it. |
| 250 | |
| 251 | * Restore the global status of that single binding by marking it |
| 252 | ``py::module_local(false)``. |
| 253 | |
| 254 | The latter is an easy workaround, but in the long run it would be best to |
| 255 | localize all common type bindings in order to avoid conflicts with |
| 256 | third-party modules. |
| 257 | |
| 258 | |
Dean Moldovan | 4c54044 | 2017-08-30 21:53:08 +0200 | [diff] [blame] | 259 | Negative strides for Python buffer objects and numpy arrays |
| 260 | ----------------------------------------------------------- |
| 261 | |
| 262 | Support for negative strides required changing the integer type from unsigned |
| 263 | to signed in the interfaces of ``py::buffer_info`` and ``py::array``. If you |
| 264 | have compiler warnings enabled, you may notice some new conversion warnings |
| 265 | after upgrading. These can be resolved using ``static_cast``. |
| 266 | |
| 267 | |
Dean Moldovan | 1fb9df6 | 2017-08-18 19:26:49 +0200 | [diff] [blame] | 268 | Deprecation of some ``py::object`` APIs |
| 269 | --------------------------------------- |
| 270 | |
| 271 | To compare ``py::object`` instances by pointer, you should now use |
| 272 | ``obj1.is(obj2)`` which is equivalent to ``obj1 is obj2`` in Python. |
| 273 | Previously, pybind11 used ``operator==`` for this (``obj1 == obj2``), but |
| 274 | that could be confusing and is now deprecated (so that it can eventually |
| 275 | be replaced with proper rich object comparison in a future release). |
| 276 | |
| 277 | For classes which inherit from ``py::object``, ``borrowed`` and ``stolen`` |
| 278 | were previously available as protected constructor tags. Now the types |
| 279 | should be used directly instead: ``borrowed_t{}`` and ``stolen_t{}`` |
| 280 | (`#771 <https://github.com/pybind/pybind11/pull/771>`_). |
| 281 | |
| 282 | |
Dean Moldovan | 91b42c8 | 2017-09-02 14:46:32 +0200 | [diff] [blame] | 283 | Stricter compile-time error checking |
| 284 | ------------------------------------ |
| 285 | |
| 286 | Some error checks have been moved from run time to compile time. Notably, |
| 287 | automatic conversion of ``std::shared_ptr<T>`` is not possible when ``T`` is |
| 288 | not directly registered with ``py::class_<T>`` (e.g. ``std::shared_ptr<int>`` |
| 289 | or ``std::shared_ptr<std::vector<T>>`` are not automatically convertible). |
| 290 | Attempting to bind a function with such arguments now results in a compile-time |
| 291 | error instead of waiting to fail at run time. |
| 292 | |
| 293 | ``py::init<...>()`` constructor definitions are also stricter and now prevent |
| 294 | bindings which could cause unexpected behavior: |
| 295 | |
| 296 | .. code-block:: cpp |
| 297 | |
| 298 | struct Example { |
| 299 | Example(int &); |
| 300 | }; |
| 301 | |
| 302 | py::class_<Example>(m, "Example") |
| 303 | .def(py::init<int &>()); // OK, exact match |
| 304 | // .def(py::init<int>()); // compile-time error, mismatch |
| 305 | |
| 306 | A non-``const`` lvalue reference is not allowed to bind to an rvalue. However, |
| 307 | note that a constructor taking ``const T &`` can still be registered using |
| 308 | ``py::init<T>()`` because a ``const`` lvalue reference can bind to an rvalue. |
| 309 | |
Dean Moldovan | 1fb9df6 | 2017-08-18 19:26:49 +0200 | [diff] [blame] | 310 | v2.1 |
| 311 | ==== |
| 312 | |
| 313 | Minimum compiler versions are enforced at compile time |
| 314 | ------------------------------------------------------ |
| 315 | |
| 316 | The minimums also apply to v2.0 but the check is now explicit and a compile-time |
| 317 | error is raised if the compiler does not meet the requirements: |
| 318 | |
| 319 | * GCC >= 4.8 |
| 320 | * clang >= 3.3 (appleclang >= 5.0) |
| 321 | * MSVC >= 2015u3 |
| 322 | * Intel C++ >= 15.0 |
| 323 | |
| 324 | |
| 325 | The ``py::metaclass`` attribute is not required for static properties |
| 326 | --------------------------------------------------------------------- |
| 327 | |
| 328 | Binding classes with static properties is now possible by default. The |
| 329 | zero-parameter version of ``py::metaclass()`` is deprecated. However, a new |
| 330 | one-parameter ``py::metaclass(python_type)`` version was added for rare |
| 331 | cases when a custom metaclass is needed to override pybind11's default. |
| 332 | |
| 333 | .. code-block:: cpp |
| 334 | |
| 335 | // old -- emits a deprecation warning |
| 336 | py::class_<Foo>(m, "Foo", py::metaclass()) |
| 337 | .def_property_readonly_static("foo", ...); |
| 338 | |
| 339 | // new -- static properties work without the attribute |
| 340 | py::class_<Foo>(m, "Foo") |
| 341 | .def_property_readonly_static("foo", ...); |
| 342 | |
| 343 | // new -- advanced feature, override pybind11's default metaclass |
| 344 | py::class_<Bar>(m, "Bar", py::metaclass(custom_python_type)) |
| 345 | ... |
| 346 | |
| 347 | |
| 348 | v2.0 |
| 349 | ==== |
| 350 | |
| 351 | Breaking changes in ``py::class_`` |
| 352 | ---------------------------------- |
| 353 | |
| 354 | These changes were necessary to make type definitions in pybind11 |
| 355 | future-proof, to support PyPy via its ``cpyext`` mechanism (`#527 |
| 356 | <https://github.com/pybind/pybind11/pull/527>`_), and to improve efficiency |
| 357 | (`rev. 86d825 <https://github.com/pybind/pybind11/commit/86d825>`_). |
| 358 | |
| 359 | 1. Declarations of types that provide access via the buffer protocol must |
| 360 | now include the ``py::buffer_protocol()`` annotation as an argument to |
| 361 | the ``py::class_`` constructor. |
| 362 | |
| 363 | .. code-block:: cpp |
| 364 | |
| 365 | py::class_<Matrix>("Matrix", py::buffer_protocol()) |
| 366 | .def(py::init<...>()) |
| 367 | .def_buffer(...); |
| 368 | |
| 369 | 2. Classes which include static properties (e.g. ``def_readwrite_static()``) |
| 370 | must now include the ``py::metaclass()`` attribute. Note: this requirement |
| 371 | has since been removed in v2.1. If you're upgrading from 1.x, it's |
| 372 | recommended to skip directly to v2.1 or newer. |
| 373 | |
| 374 | 3. This version of pybind11 uses a redesigned mechanism for instantiating |
| 375 | trampoline classes that are used to override virtual methods from within |
| 376 | Python. This led to the following user-visible syntax change: |
| 377 | |
| 378 | .. code-block:: cpp |
| 379 | |
| 380 | // old v1.x syntax |
| 381 | py::class_<TrampolineClass>("MyClass") |
| 382 | .alias<MyClass>() |
| 383 | ... |
| 384 | |
| 385 | // new v2.x syntax |
| 386 | py::class_<MyClass, TrampolineClass>("MyClass") |
| 387 | ... |
| 388 | |
| 389 | Importantly, both the original and the trampoline class are now specified |
| 390 | as arguments to the ``py::class_`` template, and the ``alias<..>()`` call |
| 391 | is gone. The new scheme has zero overhead in cases when Python doesn't |
| 392 | override any functions of the underlying C++ class. |
| 393 | `rev. 86d825 <https://github.com/pybind/pybind11/commit/86d825>`_. |
| 394 | |
| 395 | The class type must be the first template argument given to ``py::class_`` |
| 396 | while the trampoline can be mixed in arbitrary order with other arguments |
| 397 | (see the following section). |
| 398 | |
| 399 | |
| 400 | Deprecation of the ``py::base<T>()`` attribute |
| 401 | ---------------------------------------------- |
| 402 | |
| 403 | ``py::base<T>()`` was deprecated in favor of specifying ``T`` as a template |
| 404 | argument to ``py::class_``. This new syntax also supports multiple inheritance. |
| 405 | Note that, while the type being exported must be the first argument in the |
| 406 | ``py::class_<Class, ...>`` template, the order of the following types (bases, |
| 407 | holder and/or trampoline) is not important. |
| 408 | |
| 409 | .. code-block:: cpp |
| 410 | |
| 411 | // old v1.x |
| 412 | py::class_<Derived>("Derived", py::base<Base>()); |
| 413 | |
| 414 | // new v2.x |
| 415 | py::class_<Derived, Base>("Derived"); |
| 416 | |
| 417 | // new -- multiple inheritance |
| 418 | py::class_<Derived, Base1, Base2>("Derived"); |
| 419 | |
| 420 | // new -- apart from `Derived` the argument order can be arbitrary |
| 421 | py::class_<Derived, Base1, Holder, Base2, Trampoline>("Derived"); |
| 422 | |
| 423 | |
| 424 | Out-of-the-box support for ``std::shared_ptr`` |
| 425 | ---------------------------------------------- |
| 426 | |
| 427 | The relevant type caster is now built in, so it's no longer necessary to |
| 428 | include a declaration of the form: |
| 429 | |
| 430 | .. code-block:: cpp |
| 431 | |
| 432 | PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>) |
| 433 | |
| 434 | Continuing to do so won’t cause an error or even a deprecation warning, |
| 435 | but it's completely redundant. |
| 436 | |
| 437 | |
| 438 | Deprecation of a few ``py::object`` APIs |
| 439 | ---------------------------------------- |
| 440 | |
| 441 | All of the old-style calls emit deprecation warnings. |
| 442 | |
| 443 | +---------------------------------------+---------------------------------------------+ |
| 444 | | Old syntax | New syntax | |
| 445 | +=======================================+=============================================+ |
| 446 | | ``obj.call(args...)`` | ``obj(args...)`` | |
| 447 | +---------------------------------------+---------------------------------------------+ |
| 448 | | ``obj.str()`` | ``py::str(obj)`` | |
| 449 | +---------------------------------------+---------------------------------------------+ |
| 450 | | ``auto l = py::list(obj); l.check()`` | ``py::isinstance<py::list>(obj)`` | |
| 451 | +---------------------------------------+---------------------------------------------+ |
| 452 | | ``py::object(ptr, true)`` | ``py::reinterpret_borrow<py::object>(ptr)`` | |
| 453 | +---------------------------------------+---------------------------------------------+ |
| 454 | | ``py::object(ptr, false)`` | ``py::reinterpret_steal<py::object>(ptr)`` | |
| 455 | +---------------------------------------+---------------------------------------------+ |
| 456 | | ``if (obj.attr("foo"))`` | ``if (py::hasattr(obj, "foo"))`` | |
| 457 | +---------------------------------------+---------------------------------------------+ |
| 458 | | ``if (obj["bar"])`` | ``if (obj.contains("bar"))`` | |
| 459 | +---------------------------------------+---------------------------------------------+ |