What is the "base" object and how do I get rid of

I’m working on my first project with panda3d.

My app class inherits from ShowBase.

I create a single instance of the app class at the top level.

I do NOT import DirectStart.

My code is presently working (based somewhat on the code from the tutorials), but I don’t understand what this “base” object is.

Will it work if I just replace “base” with my application instance (subclass of ShowBase)?

Also, is there a way to disable creation of the “base” object entirely? It seems like the “base” object is just for toy projects using procedural code and global variables. I want this to be a full-fledged game when I’m finished, so I want to use large-project software engineering techniques (modules and classes). In particular, if a “base” object is created for every module in my project that imports panda, that may impact performance.

At least one other forum post I’ve read (sorry, I lost the link) implies that “base” is created by DirectStart. Again, I am NOT importing DirectStart, but there is still a “base” object.

I think it’s extremely poor practice to dump things in the global namespace in Python. I don’t have any statements of the form “from module import base”, “from module import *”, or any objects called “base” in my code. But panda3d somehow puts a “base” object in the global namespace nonetheless. This is fine for tutorials and class projects, but there should be a way to disable the behavior for larger applications.

Is there a convenient list somewhere of all the things Panda creates in the global namespace without your consent?

How does Panda do that, anyway? I thought the Python programming model required every object to exist inside a module, and only be visible in another module’s namespace with a “from module import…” statement, or directly creating it in my module by assigning to it.

Python does provide a mechanism for sticking things in the global space. It’s called “builtins”, and the ShowBase.init() function stuffs itself (along with a bunch of other things) into the builtins. Thus, the global “base” object is your own ShowBase object.

You can look at ShowBase.py to see the full set of things that get shoved into builtins.

I agree it’s not a very modular thing for ShowBase to do, and it doesn’t lead to well-structured programming. But we can’t change this behavior (at least not in Panda3D 1.x) because we are constrained by backward compatibility: too many existing applications depend on this behavior.

Still, it’s not that horrible, after all. You can always ignore it. Any locally-defined “base” variable will always shadow the value in builtins, so the worst possible side-effect of having the global value assigned is that you may fail to notice an unintended undefined reference to some other “base” variable.

David

Wow, fast reply :slight_smile:

Now I understand what’s going on. Thanks for the info.

You could change the builtin behavior in a backwards compatible way. Just change the ShowBase constructor to take another parameter with a default value.

#old version

class ShowBase:
    def __init__(self, fStartDirect = True, windowType = None):
      #some code...
      __builtin__.base = self
      __builtin__.render2d = self.render2d
      __builtin__.aspect2d = self.aspect2d
      #more code...
      return

Just add a parameter to the constructor with a default value which enables the old behavior, like this:

#new version

class ShowBase:
    def __init__(self, fStartDirect = True, windowType = None, setBuiltins=True):
      #some code...
      if setBuiltins:
         __builtin__.base = self
         __builtin__.render2d = self.render2d
         __builtin__.aspect2d = self.aspect2d
      #more code...
      return

Someone who knows the internals of panda3d better than I do should probably think about whether this will break other parts of panda3d. We need to find and fix any code in panda3d itself that assumes the builtins are there before we can allow applications to take them away :slight_smile:

If I have time I might download the source, make a patch, and see if I can figure out how to run the tests. (I -HOPE- there are tests with the source.)

Yeah, we’ve talked about that kind of incremental change. Another temporary stopgap would be to provide a Python module that old code can import to define all of these symbols.

It’s not quite that straightforward, though, because there is in fact a fair amount of code within the direct tree that also relies on these builtins, but this could be tracked down and corrected. There are developers who contribute to the tree that in turn rely on it, and who might not notice if they inadvertently committed something that relied on it, but this too can be dealt with.

Mostly, it hasn’t been important enough for anyone to take the effort. I’m happy to hear you’re interested in doing the work. :slight_smile:

There are, sadly, no unit tests in the Panda source code. We have a set of tests within the Disney group, but these are not part of the public distribution, and they mainly test our own code, not necessarily all of the Panda features. This is another weakness that an enthusiastic volunteer could help us fix. :slight_smile:

David

I’ve once attempted to make a change like that - but it’s a fool’s errand. There is so many code in the direct tree that depends on the existence of the ‘base’ variable, many classes would need to be redesigned because they would need to get various variables from elsewhere. And it is not feasible or possible to do that, because it would break backward compatibility.

Panda3D 2 will most likely fix this, though - but we’re talking about the distant future there.

Oh, I don’t know. Couldn’t we just insert “from direct.pandabuiltins import *” at the top of every .py file in direct, and call it done?

David

What would that fix? All we’d be doing is moving what we had in builtins to sys.modules[“direct.pandabuiltins”].

Right, but at least it wouldn’t be polluting the global namespace any longer. Direct would still be a mess, but user applications would have to explicitly import direct.pandabuiltins if they wanted to get these symbols into the namespace, which would be closer to user expectations.

Edit: actually, on further reflection, this wouldn’t work. “from direct.pandabuiltins import *” would copy the symbols into the module’s namespace at import time, which is likely to be before the ShowBase object has been created–too soon. It means that when the ShowBase object later initializes itself and defines all these symbols, and stuffs them into pandabuiltins, it’s too late for all these other modules that have already imported it. This could only work if the modules used “from direct import pandabuiltins” and then referenced “pandabuiltins.base” instead of “base”, which is possible (and cleaner), but a difficult change to make. It also may be counterintuitive for naive Python developers who may be surprised to discover that “from direct.pandabuiltins import *” doesn’t work.

David