Use pybind11.get_include()?
wjakob opened this issue · 11 comments
So pybind11 1.5 now ships with a pybind11.get_include()
method. I am wondering if/how to call it in pbtest
's setup.py
file.
If the pybind
package does not yet exist, setuptools.py
will install it. However, I don't think it's safe to assume that it exists already in https://github.com/pybind/pbtest/blob/master/setup.py#L14.
Thoughts?
Thanks,
Wenzel
My undertanding is that at the time when the build_ext command will run, the dependencies should be installed, so we can include numpy in the build_extensions
method, however I am not sure how to add things to the ext_modules
...
Scipy is using numpy.get_include()
, but their setup seems really overkill.
I solved this problem by passing a string-like object to the Extension like this
from pip import locations
class get_pybind_include(object):
def __str__(self):
pybind_include = os.path.dirname(locations.distutils_scheme('pybind11')['headers'])
return pybind_include
ext_modules = [
Extension(
'pbtest',
['py/main.cpp'],
include_dirs=[
# Path to pybind11 headers
get_pybind_include()
],
language='c++',
),
]
This evaluates the location as soon as get_pybind_include()
is used as a string (i.e. after pybind11 is installed). However, I still have the problem that if I install pybind
or pbtest
with the flag --user
, calling locations.distutils_scheme('pybind11')['headers']
does not yield the user directory but the standard include directory.
Thank you for your feedback, Ben!
Does the following give the right answer on your machine?
from pip import locations
locations.distutils_scheme('pybind11', '--user')['headers']
I'm no setuptools expert and wonder if there is a way to get the command line parameters like --user
to pass them to distutils_scheme
as a second parameter?
The __str__
trick is neat, by the way!
Yes, this works, thanks! Actually, one can pass a boolean for the user. What about doing the following? it's a bit hacky though.
is_user_install = "--user" in sys.argv[1:]
class get_pybind_include(object):
def __str__(self):
pybind_include = os.path.dirname(locations.distutils_scheme('pybind11',is_user_install)['headers'])
return pybind_include
I just added this commit: pybind/pybind11@75b1b7d
So starting with the next version, this could look as follows:
class get_pybind_include(object):
def __str__(self):
import pybind11
return pybind11.get_include(*[arg for arg in sys.argv if arg == '--user'])
Does that look reasonable?
I think you should actually pass a boolean because that's what distutils_scheme
wants:
distutils_scheme(dist_name, user=False, home=None, root=None, isolated=False, prefix=None)
Return a distutils install scheme
Technically, passing "--user" still works as python treats non-empty strings as True, but say someone calls pybind11.get_include("--not-user")
they will still get the user directory. Besides that, your solution seems fine!
Sounds good -- so the proposed change is:
class get_pybind_include(object):
def __str__(self):
import pybind11
return pybind11.get_include("--user" in sys.argv[1:])
and pybind11.get_include
doesn't have to change.
cool! btw, thanks a bunch for writing the package and also for providing this pbtest example, Sylvain. All of this has saved me a tremendous amount of work so far.
I just ran into the following issue: I installed pybind with the flag --user
, but the actual package I wrote is installed as root, hence it's not found during the install. I therefore propose to simply add both possibilities to the include list as:
class get_pybind_include(object):
def __init__(self,user=False):
self.user = user
def __str__(self):
import pybind11
return pybind11.get_include(self.user)
ext_modules = [
Extension(
'pbtest',
['py/main.cpp'],
include_dirs=[
# Path to pybind11 headers
get_pybind_include(),
get_pybind_include(user=True)
],
language='c++',
),
Sounds reasonable to me.