Skip to content

Commit 0eb7287

Browse files
committed
minimal updates to cppyy frontend to make new cppyy work
1 parent 0d5a63c commit 0eb7287

File tree

2 files changed

+128
-114
lines changed

2 files changed

+128
-114
lines changed

bindings/pyroot/cppyy/cppyy/python/cppyy/__init__.py

Lines changed: 95 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,21 @@
5050
'set_debug', # enable/disable debug output
5151
]
5252

53-
import ctypes
54-
import os
55-
import sys
56-
import sysconfig
57-
import warnings
53+
from ._version import __version__
54+
55+
import ctypes, os, sys, sysconfig, warnings
56+
57+
if not 'CLING_STANDARD_PCH' in os.environ:
58+
def _set_pch():
59+
try:
60+
import cppyy_backend as cpb
61+
local_pch = os.path.join(os.path.dirname(__file__), 'allDict.cxx.pch.'+str(cpb.__version__))
62+
if os.path.exists(local_pch):
63+
os.putenv('CLING_STANDARD_PCH', local_pch)
64+
os.environ['CLING_STANDARD_PCH'] = local_pch
65+
except (ImportError, AttributeError):
66+
pass
67+
_set_pch(); del _set_pch
5868

5969
try:
6070
import __pypy__
@@ -63,9 +73,6 @@
6373
except ImportError:
6474
ispypy = False
6575

66-
from . import _typemap
67-
from ._version import __version__
68-
6976
# import separately instead of in the above try/except block for easier to
7077
# understand tracebacks
7178
if ispypy:
@@ -79,7 +86,17 @@
7986
sys.modules['cppyy.gbl.std'] = gbl.std
8087

8188

89+
#- force creation of std.exception -------------------------------------------------------
90+
_e = gbl.std.exception
91+
92+
93+
#- enable auto-loading -------------------------------------------------------
94+
try: gbl.cling.runtime.gCling.EnableAutoLoading()
95+
except: pass
96+
97+
8298
#- external typemap ----------------------------------------------------------
99+
from . import _typemap
83100
_typemap.initialize(_backend) # also creates (u)int8_t mapper
84101

85102
try:
@@ -113,15 +130,18 @@ def tuple_getitem(self, idx, get=cppyy.gbl.std.get):
113130
raise IndexError(idx)
114131
pyclass.__getitem__ = tuple_getitem
115132

116-
# pythonization of std::string; placed here because it's simpler to write the
133+
# pythonization of std::basic_string<char>; placed here because it's simpler to write the
117134
# custom "npos" object (to allow easy result checking of find/rfind) in Python
118-
elif pyclass.__cpp_name__ == "std::string":
119-
class NPOS(0x3000000 <= sys.hexversion and int or long):
135+
elif pyclass.__cpp_name__ == "std::basic_string<char>":
136+
class NPOS(int):
137+
def __init__(self, npos):
138+
self.__cpp_npos = npos
120139
def __eq__(self, other):
121-
return other == -1 or int(self) == other
140+
return other == -1 or other == self.__cpp_npos
122141
def __ne__(self, other):
123-
return other != -1 and int(self) != other
124-
del pyclass.__class__.npos # drop b/c is const data
142+
return other != -1 and other != self.__cpp_npos
143+
if hasattr(pyclass.__class__, 'npos'):
144+
del pyclass.__class__.npos # drop b/c is const data
125145
pyclass.npos = NPOS(pyclass.npos)
126146

127147
return True
@@ -158,24 +178,24 @@ def __getitem__(self, cls):
158178
return py_make_smartptr(cls, self.ptrcls)
159179
except AttributeError:
160180
pass
161-
if isinstance(cls, str) and not cls in ('int', 'float'):
181+
if type(cls) == str and not cls in ('int', 'float'):
162182
return py_make_smartptr(getattr(gbl, cls), self.ptrcls)
163183
return self.maker[cls]
164184

165-
gbl.std.make_shared = make_smartptr(gbl.std.shared_ptr, gbl.std.make_shared)
166-
gbl.std.make_unique = make_smartptr(gbl.std.unique_ptr, gbl.std.make_unique)
185+
# gbl.std.make_shared = make_smartptr(gbl.std.shared_ptr, gbl.std.make_shared)
186+
# gbl.std.make_unique = make_smartptr(gbl.std.unique_ptr, gbl.std.make_unique)
167187
del make_smartptr
168188

169189

170190
#--- interface to Cling ------------------------------------------------------
171191
class _stderr_capture(object):
172192
def __init__(self):
173-
self._capture = not gbl.gDebug and True or False
174-
self.err = ""
193+
self._capture = not gbl.Cpp.IsDebugOutputEnabled()
194+
self.err = ""
175195

176196
def __enter__(self):
177197
if self._capture:
178-
_begin_capture_stderr()
198+
_begin_capture_stderr()
179199
return self
180200

181201
def __exit__(self, tp, val, trace):
@@ -200,32 +220,39 @@ def _cling_report(msg, errcode, msg_is_error=False):
200220
def cppdef(src):
201221
"""Declare C++ source <src> to Cling."""
202222
with _stderr_capture() as err:
203-
errcode = gbl.gInterpreter.Declare(src)
204-
_cling_report(err.err, int(not errcode), msg_is_error=True)
223+
errcode = gbl.Cpp.Declare(src, False)
224+
if not errcode == 0 or err.err:
225+
if 'warning' in err.err.lower() and not 'error' in err.err.lower():
226+
warnings.warn(err.err, SyntaxWarning)
227+
return True
228+
raise SyntaxError('Failed to parse the given C++ code%s' % err.err)
205229
return True
206230

207231
def cppexec(stmt):
208232
"""Execute C++ statement <stmt> in Cling's global scope."""
209233
if stmt and stmt[-1] != ';':
210234
stmt += ';'
211235

212-
# capture stderr, but note that ProcessLine could legitimately be writing to
236+
# capture stderr, but note that Process could legitimately be writing to
213237
# std::cerr, in which case the captured output needs to be printed as normal
214238
with _stderr_capture() as err:
215239
errcode = ctypes.c_int(0)
216240
try:
217-
gbl.gInterpreter.ProcessLine(stmt, ctypes.pointer(errcode))
241+
errcode = gbl.Cpp.Process(stmt)
218242
except Exception as e:
219243
sys.stderr.write("%s\n\n" % str(e))
220-
if not errcode.value:
221-
errcode.value = 1
244+
if not errcode.value: errcode.value = 1
222245

223-
_cling_report(err.err, errcode.value)
224-
if err.err and err.err[1:] != '\n':
246+
if not errcode == 0:
247+
raise SyntaxError('Failed to parse the given C++ code%s' % err.err)
248+
elif err.err and err.err[1:] != '\n':
225249
sys.stderr.write(err.err[1:])
226250

227251
return True
228252

253+
def evaluate(input, HadError = _backend.nullptr):
254+
return gbl.Cpp.Evaluate(input, HadError)
255+
229256
def macro(cppm):
230257
"""Attempt to evalute a C/C++ pre-processor macro as a constant"""
231258

@@ -243,43 +270,35 @@ def macro(cppm):
243270
def load_library(name):
244271
"""Explicitly load a shared library."""
245272
with _stderr_capture() as err:
246-
gSystem = gbl.gSystem
247-
if name[:3] != 'lib':
248-
if not gSystem.FindDynamicLibrary(gbl.TString(name), True) and\
249-
gSystem.FindDynamicLibrary(gbl.TString('lib'+name), True):
250-
name = 'lib'+name
251-
sc = gSystem.Load(name)
252-
if sc == -1:
253-
# special case for Windows as of python3.8: use winmode=0, otherwise the default
254-
# will not consider regular search paths (such as $PATH)
255-
if 0x3080000 <= sys.hexversion and 'win32' in sys.platform and os.path.isabs(name):
256-
return ctypes.CDLL(name, ctypes.RTLD_GLOBAL, winmode=0) # raises on error
257-
raise RuntimeError('Unable to load library "%s"%s' % (name, err.err))
273+
result = gbl.Cpp.LoadLibrary(name, True)
274+
if result == False:
275+
raise RuntimeError('Could not load library "%s": %s' % (name, err.err))
276+
258277
return True
259278

260279
def include(header):
261280
"""Load (and JIT) header file <header> into Cling."""
262281
with _stderr_capture() as err:
263-
errcode = gbl.gInterpreter.Declare('#include "%s"' % header)
264-
if not errcode:
282+
errcode = gbl.Cpp.Declare('#include "%s"' % header, False)
283+
if not errcode == 0:
265284
raise ImportError('Failed to load header file "%s"%s' % (header, err.err))
266285
return True
267286

268287
def c_include(header):
269288
"""Load (and JIT) header file <header> into Cling."""
270289
with _stderr_capture() as err:
271-
errcode = gbl.gInterpreter.Declare("""extern "C" {
272-
#include "%s"
273-
}""" % header)
274-
if not errcode:
290+
errcode = gbl.Cpp.Declare("""extern "C" {
291+
#include "%s"
292+
}""" % header, False)
293+
if not errcode == 0:
275294
raise ImportError('Failed to load header file "%s"%s' % (header, err.err))
276295
return True
277296

278297
def add_include_path(path):
279298
"""Add a path to the include paths available to Cling."""
280299
if not os.path.isdir(path):
281300
raise OSError('No such directory: %s' % path)
282-
gbl.gInterpreter.AddIncludePath(path)
301+
gbl.Cpp.AddIncludePath(path)
283302

284303
def add_library_path(path):
285304
"""Add a path to the library search paths available to Cling."""
@@ -319,23 +338,22 @@ def add_library_path(path):
319338
else:
320339
import pkg_resources as pr
321340

322-
d = pr.get_distribution('CPyCppyy')
323-
for line in d.get_metadata_lines('RECORD'):
324-
if 'API.h' in line:
325-
ape = os.path.join(d.location, line[0:line.find(',')])
326-
break
327-
del line, d, pr
341+
d = pr.get_distribution('CPyCppyy')
342+
for line in d.get_metadata_lines('RECORD'):
343+
if 'API.h' in line:
344+
part = line[0:line.find(',')]
328345

346+
ape = os.path.join(d.location, part)
329347
if os.path.exists(ape):
330348
apipath_extra = os.path.dirname(os.path.dirname(ape))
331-
del ape
349+
350+
del part, d, pr
332351
except Exception:
333352
pass
334353

335354
if apipath_extra is None:
336355
ldversion = sysconfig.get_config_var('LDVERSION')
337-
if not ldversion:
338-
ldversion = sys.version[:3]
356+
if not ldversion: ldversion = sys.version[:3]
339357

340358
apipath_extra = os.path.join(os.path.dirname(apipath), 'site', 'python'+ldversion)
341359
if not os.path.exists(os.path.join(apipath_extra, 'CPyCppyy')):
@@ -361,9 +379,7 @@ def add_library_path(path):
361379

362380
if apipath_extra.lower() != 'none':
363381
if not os.path.exists(os.path.join(apipath_extra, 'CPyCppyy')):
364-
warnings.warn("CPyCppyy API not found (tried: %s); "
365-
"set CPPYY_API_PATH envar to the 'CPyCppyy' API directory to fix"
366-
% apipath_extra)
382+
warnings.warn("CPyCppyy API not found (tried: %s); set CPPYY_API_PATH envar to the 'CPyCppyy' API directory to fix" % apipath_extra)
367383
else:
368384
add_include_path(apipath_extra)
369385

@@ -372,38 +388,30 @@ def add_library_path(path):
372388
if os.getenv('CONDA_PREFIX'):
373389
# MacOS, Linux
374390
include_path = os.path.join(os.getenv('CONDA_PREFIX'), 'include')
375-
if os.path.exists(include_path):
376-
add_include_path(include_path)
391+
if os.path.exists(include_path): add_include_path(include_path)
377392

378393
# Windows
379394
include_path = os.path.join(os.getenv('CONDA_PREFIX'), 'Library', 'include')
380-
if os.path.exists(include_path):
381-
add_include_path(include_path)
395+
if os.path.exists(include_path): add_include_path(include_path)
382396

383-
# assuming that we are in PREFIX/lib/python/site-packages/cppyy,
384-
# add PREFIX/include to the search path
385-
include_path = os.path.abspath(
386-
os.path.join(os.path.dirname(__file__), *(4*[os.path.pardir]+['include'])))
387-
if os.path.exists(include_path):
388-
add_include_path(include_path)
397+
# assuming that we are in PREFIX/lib/python/site-packages/cppyy, add PREFIX/include to the search path
398+
include_path = os.path.abspath(os.path.join(os.path.dirname(__file__), *(4*[os.path.pardir]+['include'])))
399+
if os.path.exists(include_path): add_include_path(include_path)
389400

390401
del include_path, apipath, ispypy
391402

392403
def add_autoload_map(fname):
393404
"""Add the entries from a autoload (.rootmap) file to Cling."""
394405
if not os.path.isfile(fname):
395406
raise OSError("no such file: %s" % fname)
396-
gbl.gInterpreter.LoadLibraryMap(fname)
407+
gbl.cling.runtime.gCling.LoadLibraryMap(fname)
397408

398409
def set_debug(enable=True):
399410
"""Enable/disable debug output."""
400-
if enable:
401-
gbl.gDebug = 10
402-
else:
403-
gbl.gDebug = 0
411+
gbl.Cpp.EnableDebugOutput(enable)
404412

405413
def _get_name(tt):
406-
if isinstance(tt, str):
414+
if type(tt) == str:
407415
return tt
408416
try:
409417
ttname = tt.__cpp_name__
@@ -414,15 +422,15 @@ def _get_name(tt):
414422
_sizes = {}
415423
def sizeof(tt):
416424
"""Returns the storage size (in chars) of C++ type <tt>."""
417-
if not isinstance(tt, type) and not isinstance(tt, str):
425+
if not isinstance(tt, type) and not type(tt) == str:
418426
tt = type(tt)
419427
try:
420428
return _sizes[tt]
421429
except KeyError:
422430
try:
423431
sz = ctypes.sizeof(tt)
424432
except TypeError:
425-
sz = gbl.gInterpreter.ProcessLine("sizeof(%s);" % (_get_name(tt),))
433+
sz = gbl.Cpp.Evaluate("sizeof(%s)" % (_get_name(tt),), nullptr)
426434
_sizes[tt] = sz
427435
return sz
428436

@@ -435,7 +443,7 @@ def typeid(tt):
435443
return _typeids[tt]
436444
except KeyError:
437445
tidname = 'typeid_'+str(len(_typeids))
438-
gbl.gInterpreter.ProcessLine(
446+
cppexec(
439447
"namespace _cppyy_internal { auto* %s = &typeid(%s); }" %\
440448
(tidname, _get_name(tt),))
441449
tid = getattr(gbl._cppyy_internal, tidname)
@@ -445,9 +453,15 @@ def typeid(tt):
445453
def multi(*bases): # after six, see also _typemap.py
446454
"""Resolve metaclasses for multiple inheritance."""
447455
# contruct a "no conflict" meta class; the '_meta' is needed by convention
448-
nc_meta = type.__new__(
449-
type, 'cppyy_nc_meta', tuple(type(b) for b in bases if type(b) is not type), {})
456+
nc_meta = type.__new__(type, 'cppyy_nc_meta', tuple(type(b) for b in bases if type(b) is not type), {})
450457
class faux_meta(type):
451-
def __new__(mcs, name, this_bases, d):
458+
def __new__(cls, name, this_bases, d):
452459
return nc_meta(name, bases, d)
453460
return type.__new__(faux_meta, 'faux_meta', (), {})
461+
462+
463+
#- workaround (TODO: may not be needed with Clang9) --------------------------
464+
if 'win32' in sys.platform:
465+
cppdef("""template<>
466+
std::basic_ostream<char, std::char_traits<char>>& __cdecl std::endl<char, std::char_traits<char>>(
467+
std::basic_ostream<char, std::char_traits<char>>&);""")

0 commit comments

Comments
 (0)