From 0f5867a29d4a60d956c8c96ec1980780756df0a8 Mon Sep 17 00:00:00 2001 From: Jim Pivarski Date: Thu, 19 Jul 2018 09:56:42 -0400 Subject: [PATCH 1/5] get classes from a centralized location, and use the pyclassname (str), not the C++ name (bytes) --- uproot/rootio.py | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/uproot/rootio.py b/uproot/rootio.py index b912edeb..a9ff806e 100644 --- a/uproot/rootio.py +++ b/uproot/rootio.py @@ -251,18 +251,9 @@ def showstreamers(self, filtername=nofilter, stream=sys.stdout): if filtername(x.fName): x.show(stream=stream) - def _classof(self, classname): - if classname == b"TDirectory" or classname == b"TDirectoryFile": - cls = ROOTDirectory - else: - cls = self._context.classes.get(classname, None) - if cls is None: - cls = ROOTObject.__metaclass__("Undefined_" + str(_safename(classname)), (Undefined,), {"_classname": classname}) - return cls - def iterkeys(self, recursive=False, filtername=nofilter, filterclass=nofilter): for key in self._keys: - cls = self._classof(key.fClassName) + cls = _classof(self._context, key.fClassName) if filtername(key.fName) and filterclass(cls): yield self._withcycle(key) @@ -272,7 +263,7 @@ def iterkeys(self, recursive=False, filtername=nofilter, filterclass=nofilter): def itervalues(self, recursive=False, filtername=nofilter, filterclass=nofilter): for key in self._keys: - cls = self._classof(key.fClassName) + cls = _classof(self._context, key.fClassName) if filtername(key.fName) and filterclass(cls): yield key.get() @@ -282,7 +273,7 @@ def itervalues(self, recursive=False, filtername=nofilter, filterclass=nofilter) def iteritems(self, recursive=False, filtername=nofilter, filterclass=nofilter): for key in self._keys: - cls = self._classof(key.fClassName) + cls = _classof(self._context, key.fClassName) if filtername(key.fName) and filterclass(cls): yield self._withcycle(key), key.get() @@ -292,7 +283,7 @@ def iteritems(self, recursive=False, filtername=nofilter, filterclass=nofilter): def iterclasses(self, recursive=False, filtername=nofilter, filterclass=nofilter): for key in self._keys: - cls = self._classof(key.fClassName) + cls = _classof(self._context, key.fClassName) if filtername(key.fName) and filterclass(cls): yield self._withcycle(key), cls @@ -488,6 +479,15 @@ def _readobjany(source, cursor, context, parent, asclass=None): return obj # return object +def _classof(context, classname): + if classname == b"TDirectory" or classname == b"TDirectoryFile": + cls = ROOTDirectory + else: + cls = context.classes.get(_safename(classname), None) + if cls is None: + cls = ROOTObject.__metaclass__("Undefined_" + str(_safename(classname)), (Undefined,), {"_classname": classname}) + return cls + def _readstreamers(source, cursor, context, parent): tlist = TList.read(source, cursor, context, parent) @@ -725,7 +725,7 @@ def _defineclasses(streamerinfos, classes): fields.append(_safename(element.fName)) else: - raise AssertionError + raise AssertionError(element) code.extend([" if self.__class__.__name__ == cls.__name__:", " self.__class__ = cls._versions[classversion]", @@ -847,19 +847,8 @@ def get(self, dismiss=True): Objects are not read or decompressed until this function is explicitly called. """ - classname = self.fClassName.decode("ascii") try: - if classname == "TDirectory" or classname == "TDirectoryFile": - return ROOTDirectory.read(self._source, self._cursor.copied(), self._context, self) - - elif classname in self._context.classes: - return self._context.classes[classname].read(self._source, self._cursor.copied(), self._context, self) - - else: - out = Undefined.read(self._source, self._cursor.copied(), self._context, self) - out._classname = classname - return out - + return _classof(self._context, self.fClassName).read(self._source, self._cursor.copied(), self._context, self) finally: if dismiss: self._source.dismiss() From e9596cadf73a5795b8332be26d59533ac0c9827b Mon Sep 17 00:00:00 2001 From: Jim Pivarski Date: Thu, 19 Jul 2018 10:29:14 -0400 Subject: [PATCH 2/5] handle TStreamerObjectAnyPointer, uproot.const.kAnyp, and uproot.const.kAnyP --- uproot/rootio.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/uproot/rootio.py b/uproot/rootio.py index a9ff806e..0e048f0a 100644 --- a/uproot/rootio.py +++ b/uproot/rootio.py @@ -692,17 +692,14 @@ def _defineclasses(streamerinfos, classes): " for index in range(self.{0}):".format(_safename(element.fCountName)), " self.{0} = {1}.read(source, cursor, context, self)".format(_safename(element.fName), _safename(element.fTypeName.rstrip(b"*")))]) - elif isinstance(element, TStreamerObjectAnyPointer): - code.append(" _raise_notimplemented({0}, {1}, source, cursor)".format(repr(element.__class__.__name__), repr(repr(element.__dict__)))) - - elif isinstance(element, TStreamerObjectPointer): - if element.fType == uproot.const.kObjectp: + elif isinstance(element, (TStreamerObjectAnyPointer, TStreamerObjectPointer)): + if element.fType == uproot.const.kObjectp or element.fType == uproot.const.kAnyp: if pyclassname in skip and _safename(element.fName) in skip[pyclassname]: code.append(" Undefined.read(source, cursor, context, self)") else: code.append(" self.{0} = {1}.read(source, cursor, context, self)".format(_safename(element.fName), _safename(element.fTypeName.rstrip(b"*")))) fields.append(_safename(element.fName)) - elif element.fType == uproot.const.kObjectP: + elif element.fType == uproot.const.kObjectP or element.fType == uproot.const.kAnyP: if pyclassname in skip and _safename(element.fName) in skip[pyclassname]: code.append(" _readobjany(source, cursor, context, parent, asclass=Undefined)") else: @@ -752,7 +749,7 @@ def _defineclasses(streamerinfos, classes): code.insert(0, " _classname = {0}".format(repr(streamerinfo.fName))) else: code.insert(0, " _classname = b{0}".format(repr(streamerinfo.fName))) - code.insert(0, " _fields = [{0}]".format(", ".join(repr(x) for x in fields))) + code.insert(0, " _fields = [{0}]".format(", ".join(repr(str(x)) for x in fields))) code.insert(0, "class {0}({1}):".format(pyclassname, ", ".join(bases))) if pyclassname in classes: @@ -1080,11 +1077,18 @@ def _readinto(cls, self, source, cursor, context, parent): ################################################################ streamed classes (with some overrides) class ROOTStreamedObject(ROOTObject): - pass - -class TObject(ROOTStreamedObject): _fields = [] + @classmethod + def rootfields(cls): + out = [] + for t in cls.__bases__: + if issubclass(t, ROOTStreamedObject): + out.extend(t.rootfields()) + out.extend(cls._fields) + return out + +class TObject(ROOTStreamedObject): @classmethod def _readinto(cls, self, source, cursor, context, parent): _skiptobj(source, cursor) @@ -1099,6 +1103,8 @@ def __str__(self): return self.decode("utf-8", "replace") class TNamed(TObject): + _fields = ["fName", "fTitle"] + @classmethod def _readinto(cls, self, source, cursor, context, parent): start, cnt, self._classversion = _startcheck(source, cursor) From 3cd83fdf9c4b754ee1e31d845eede6b4cb7505e4 Mon Sep 17 00:00:00 2001 From: Jim Pivarski Date: Thu, 19 Jul 2018 10:38:14 -0400 Subject: [PATCH 3/5] maybe fixes #107; this commit renamed rootfields() to _members() --- uproot/rootio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uproot/rootio.py b/uproot/rootio.py index 0e048f0a..25ff856e 100644 --- a/uproot/rootio.py +++ b/uproot/rootio.py @@ -1080,11 +1080,11 @@ class ROOTStreamedObject(ROOTObject): _fields = [] @classmethod - def rootfields(cls): + def _members(cls): out = [] for t in cls.__bases__: if issubclass(t, ROOTStreamedObject): - out.extend(t.rootfields()) + out.extend(t._members()) out.extend(cls._fields) return out From 23d538c833bb358bc433c9ca89cb8daa32d9c498 Mon Sep 17 00:00:00 2001 From: Jim Pivarski Date: Thu, 19 Jul 2018 10:47:03 -0400 Subject: [PATCH 4/5] addresses #107; add a starter for implementing methods --- uproot/hist.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/uproot/hist.py b/uproot/hist.py index a501f94e..68c89740 100644 --- a/uproot/hist.py +++ b/uproot/hist.py @@ -620,6 +620,15 @@ def show(self, width=80, minimum=None, maximum=None, stream=sys.stdout): uproot.rootio.methods["TH2"] = TH2Methods +class THnSparseMethods(object): + # makes __doc__ attribute mutable before Python 3.3 + __metaclass__ = type.__new__(type, "type", (TH1Methods.__metaclass__,), {}) + + def hello(self): + return "world", len(dir(self)) + +uproot.rootio.methods["THnSparse"] = THnSparseMethods + class TH1(TH1Methods, list): def _type(self): if all(isinstance(x, numbers.Integral) for x in self): From a595e4294ea84cfb8e96b8fb100665362f5aca1f Mon Sep 17 00:00:00 2001 From: Jim Pivarski Date: Tue, 7 Aug 2018 14:35:14 -0500 Subject: [PATCH 5/5] consider #107 done; merge into master with a new version number --- uproot/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uproot/version.py b/uproot/version.py index 60fdde91..7727b6bc 100644 --- a/uproot/version.py +++ b/uproot/version.py @@ -30,7 +30,7 @@ import re -__version__ = "2.9.9" +__version__ = "2.9.10" version = __version__ version_info = tuple(re.split(r"[-\.]", __version__))