Commit Graph

18 Commits

Author SHA1 Message Date
Dustin Spicuzza
ec81e8e778
Propagate py::multiple_inheritance to all children (#3650)
* Add tests demonstrating smart_holder issues with multiple inheritance

* Propagate C++ multiple inheritance markers to all children

- Makes py::multiple_inheritance only needed in base classes hidden from pybind11
2022-01-26 17:03:52 -08:00
Aaron Gokaslan
9df2f1ff13
maint(precommit): Apply isort (#3195)
* Apply isort

* Tweak isort config

* Add env.py as a known_first_party

* Add one missing known first party

* Make config compat with older isort versions

* Add another comment

* Revert pyproject setting
2021-08-13 12:37:05 -04:00
Henry Schreiner
c50f90eca6
style: use Black everywhere (#2594)
* style: use Black everywhere

* style: minor touchup from review
2020-10-16 16:38:13 -04:00
Henry Schreiner
4d9024ec71
tests: cleanup and ci hardening (#2397)
* tests: refactor and cleanup

* refactor: more consistent

* tests: vendor six

* tests: more xfails, nicer system

* tests: simplify to info

* tests: suggestions from @YannickJadoul and @bstaletic

* tests: restore some pypy tests that now pass

* tests: rename info to env

* tests: strict False/True

* tests: drop explicit strict=True again

* tests: reduce minimum PyTest to 3.1
2020-08-16 16:02:12 -04:00
Henry Schreiner
d8c7ee00a6
ci: GHA basic format & pre-commit (#2309) 2020-07-20 13:35:21 -04:00
Isuru Fernando
0d70f0e18e
PyPy3 support (#2146)
* Error out eval_file

* Enable dynamic attribute support for Pypy >= 6

* Add a test for dynamic attribute support

* Skip test for eval_file on pypy

* Workaround for __qualname__ on PyPy3

* Add a PyPy3.6 7.3.0 build

* Only disable in PyPy3

* Fix travis testing

* No numpy and scipy for pypy

* Enable test on pypy2

* Fix logic in eval_file

* Skip a few tests due to bugs in PyPy

* scipy wheels are broken. make pypy2 a failrue

Co-authored-by: Andreas Kloeckner <inform@tiker.net>
2020-07-07 15:58:16 +02:00
Jason Rhinelander
c4e180081d Reimplement py::init<...> to use common factory code
This reimplements the py::init<...> implementations using the various
functions added to support `py::init(...)`, and moves the implementing
structs into `detail/init.h` from `pybind11.h`.  It doesn't simply use a
factory directly, as this is a very common case and implementation
without an extra lambda call is a small but useful optimization.

This, combined with the previous lazy initialization, also avoids
needing placement new for `py::init<...>()` construction: such
construction now occurs via an ordinary `new Type(...)`.

A consequence of this is that it also fixes a potential bug when using
multiple inheritance from Python: it was very easy to write classes
that double-initialize an existing instance which had the potential to
leak for non-pod classes.  With the new implementation, an attempt to
call `__init__` on an already-initialized object is now ignored.  (This
was already done in the previous commit for factory constructors).

This change exposed a few warnings (fixed here) from deleting a pointer
to a base class with virtual functions but without a virtual destructor.
These look like legitimate warnings that we shouldn't suppress; this
adds virtual destructors to the appropriate classes.
2017-08-17 09:33:27 -04:00
Jason Rhinelander
464d98962d Allow binding factory functions as constructors
This allows you to use:

    cls.def(py::init(&factory_function));

where `factory_function` returns a pointer, holder, or value of the
class type (or a derived type).  Various compile-time checks
(static_asserts) are performed to ensure the function is valid, and
various run-time type checks where necessary.

Some other details of this feature:
- The `py::init` name doesn't conflict with the templated no-argument
  `py::init<...>()`, but keeps the naming consistent: the existing
  templated, no-argument one wraps constructors, the no-template,
  function-argument one wraps factory functions.
- If returning a CppClass (whether by value or pointer) when an CppAlias
  is required (i.e. python-side inheritance and a declared alias), a
  dynamic_cast to the alias is attempted (for the pointer version); if
  it fails, or if returned by value, an Alias(Class &&) constructor
  is invoked.  If this constructor doesn't exist, a runtime error occurs.
- for holder returns when an alias is required, we try a dynamic_cast of
  the wrapped pointer to the alias to see if it is already an alias
  instance; if it isn't, we raise an error.
- `py::init(class_factory, alias_factory)` is also available that takes
  two factories: the first is called when an alias is not needed, the
  second when it is.
- Reimplement factory instance clearing.  The previous implementation
  failed under python-side multiple inheritance: *each* inherited
  type's factory init would clear the instance instead of only setting
  its own type value.  The new implementation here clears just the
  relevant value pointer.
- dealloc is updated to explicitly set the leftover value pointer to
  nullptr and the `holder_constructed` flag to false so that it can be
  used to clear preallocated value without needing to rebuild the
  instance internals data.
- Added various tests to test out new allocation/deallocation code.
- With preallocation now done lazily, init factory holders can
  completely avoid the extra overhead of needing an extra
  allocation/deallocation.
- Updated documentation to make factory constructors the default
  advanced constructor style.
- If an `__init__` is called a second time, we have two choices: we can
  throw away the first instance, replacing it with the second; or we can
  ignore the second call.  The latter is slightly easier, so do that.
2017-08-17 09:33:27 -04:00
Jason Rhinelander
85d63c3bcd Superclass typo fix
This didn't actually affect anything (because all the MI3 constructor
does is invoke MI2 with the same arguments anyway).
2017-07-29 03:54:25 -04:00
Jason Rhinelander
353615f77e Make init_holder do registration, and rename to init_instance
The instance registration for offset base types fails (under macOS, with
a segfault) in the presense of virtual base types.  The issue occurs
when trying to `static_cast<Base *>(derived_ptr)` when `derived_ptr` has
been allocated (via `operator new`) but not initialized.

This commit fixes the issue by moving the addition to
`registered_instances` into `init_holder` rather than immediately after
value pointer allocation.

This also renames it to `init_instance` since it does more than holder
initialization now.  (I also further renamed `init_holder_helper` to
`init_holder` since `init_holder` isn't used anymore).

Fixes #959.
2017-07-28 20:39:33 -04:00
Jason Rhinelander
44a17e1f3d Convert test_multiple_inheritance to new style
Significant rearrangement, but no new tests added.
2017-07-28 20:39:33 -04:00
Jason Rhinelander
e45c211497 Support multiple inheritance from python
This commit allows multiple inheritance of pybind11 classes from
Python, e.g.

    class MyType(Base1, Base2):
        def __init__(self):
            Base1.__init__(self)
            Base2.__init__(self)

where Base1 and Base2 are pybind11-exported classes.

This requires collapsing the various builtin base objects
(pybind11_object_56, ...) introduced in 2.1 into a single
pybind11_object of a fixed size; this fixed size object allocates enough
space to contain either a simple object (one base class & small* holder
instance), or a pointer to a new allocation that can contain an
arbitrary number of base classes and holders, with holder size
unrestricted.

* "small" here means having a sizeof() of at most 2 pointers, which is
enough to fit unique_ptr (sizeof is 1 ptr) and shared_ptr (sizeof is 2
ptrs).

To minimize the performance impact, this repurposes
`internals::registered_types_py` to store a vector of pybind-registered
base types.  For direct-use pybind types (e.g. the `PyA` for a C++ `A`)
this is simply storing the same thing as before, but now in a vector;
for Python-side inherited types, the map lets us avoid having to do a
base class traversal as long as we've seen the class before.  The
change to vector is needed for multiple inheritance: Python types
inheriting from multiple registered bases have one entry per base.
2017-06-12 09:56:55 -03:00
Jason Rhinelander
1f8a100d38 Track base class pointers of instances
This commits adds base class pointers of offset base classes (i.e. due
to multiple inheritance) to `registered_instances` so that if such a
pointer is returned we properly recognize it as an existing instance.

Without this, returning a base class pointer will cast to the existing
instance if the pointer happens to coincide with the instance pointer,
but constructs a new instance (quite possibly with a segfault, if
ownership is applied) for unequal base class pointers due to multiple
inheritance.
2017-04-27 09:12:41 -04:00
Jason Rhinelander
14e70650fe Fix downcasting of base class pointers
When we are returned a base class pointer (either directly or via
shared_from_this()) we detect its runtime type (using `typeid`), then
end up essentially reinterpret_casting the pointer to the derived type.
This is invalid when the base class pointer was a non-first base, and we
end up with an invalid pointer.  We could dynamic_cast to the
most-derived type, but if *that* type isn't pybind11-registered, the
resulting pointer given to the base `cast` implementation isn't necessarily valid
to be reinterpret_cast'ed back to the backup type.

This commit removes the "backup" type argument from the many-argument
`cast(...)` and instead does the derived-or-pointer type decision and
type lookup in type_caster_base, where the dynamic_cast has to be to
correctly get the derived pointer, but also has to do the type lookup to
ensure that we don't pass the wrong (derived) pointer when the backup
type (i.e. the type caster intrinsic type) pointer is needed.

Since the lookup is needed before calling the base cast(), this also
changes the input type to a detail::type_info rather than doing a
(second) lookup in cast().
2017-04-27 09:12:41 -04:00
Dean Moldovan
08cbe8dfed Make all classes with the same instance size derive from a common base
In order to fully satisfy Python's inheritance type layout requirements,
all types should have a common 'solid' base. A solid base is one which
has the same instance size as the derived type (not counting the space
required for the optional `dict_ptr` and `weakrefs_ptr`). Thus, `object`
does not qualify as a solid base for pybind11 types and this can lead to
issues with multiple inheritance.

To get around this, new base types are created: one per unique instance
size. There is going to be very few of these bases. They ensure Python's
MRO checks will pass when multiple bases are involved.
2017-02-23 15:45:26 +01:00
Wenzel Jakob
1d1f81b278 WIP: PyPy support (#527)
This commit includes modifications that are needed to get pybind11 to work with PyPy. The full test suite compiles and runs except for a last few functions that are commented out (due to problems in PyPy that were reported on the PyPy bugtracker).

Two somewhat intrusive changes were needed to make it possible: two new tags ``py::buffer_protocol()`` and ``py::metaclass()`` must now be specified to the ``class_`` constructor if the class uses the buffer protocol and/or requires a metaclass (e.g. for static properties).

Note that this is only for the PyPy version based on Python 2.7 for now. When the PyPy 3.x has caught up in terms of cpyext compliance, a PyPy 3.x patch will follow.
2016-12-16 15:00:46 +01:00
Dean Moldovan
bad1740213 Add checks to maintain a consistent Python code style and prevent bugs (#515)
A flake8 configuration is included in setup.cfg and the checks are
executed automatically on Travis:

* Ensures a consistent PEP8 code style
* Does basic linting to prevent possible bugs
2016-11-20 21:21:54 +01:00
Wenzel Jakob
8e5dceb6a6 Multiple inheritance support 2016-09-19 13:45:31 +02:00