How is base.a2dTopLeft exposed to users?

In many of the python files using Panda3D, I see calls to base like

base.a2dTopLeft

However I don’t see an

import base

.

How is

base

exposed?

I think it has to do with python’s builtins, but I’m not sure how this is done.

The builtin or builtins if you’re on py3 are a bit like globals only more so, it’s the topmost namespace (or at least I think it is). If something is put into the builtins it’s available everywhere within one interpreter process, for example if you put some ‘foo’ in it, then ‘foo’ will be available in any module you would import later. Just like ‘base’, ‘render’ and all the other things commonly used by p3d.

It is convenient, you don’t have to pass around a reference to all the things that could want to do common p3d stuff like .reparent_to(render), but you also have these magical objects that you never imported and may have no idea what they are.

If you want to know what’s going on check the ShowBase source (github.com/panda3d/panda3d/blob … howBase.py), you can see there all the things added to builtins:

import sys
if sys.version_info >= (3, 0):
    import builtins
else:
    import __builtin__ as builtins
#...snip...
#...snip...
#...snip...
        # DO NOT ADD TO THIS LIST.  We're trying to phase out the use of
        # built-in variables by ShowBase.  Use a Global module if necessary.
        builtins.base = self
        builtins.render2d = self.render2d
        builtins.aspect2d = self.aspect2d
        builtins.pixel2d = self.pixel2d
        builtins.render = self.render
        builtins.hidden = self.hidden
        builtins.camera = self.camera
        builtins.loader = self.loader
        builtins.taskMgr = self.taskMgr
        builtins.jobMgr = self.jobMgr
        builtins.eventMgr = self.eventMgr
        builtins.messenger = self.messenger
        builtins.bboard = self.bboard
        # Config needs to be defined before ShowBase is constructed
        #builtins.config = self.config
        builtins.run = legacyRun
        builtins.ostream = Notify.out()
        builtins.directNotify = directNotify
        builtins.giveNotify = giveNotify
        builtins.globalClock = globalClock
        builtins.vfs = vfs
        builtins.cpMgr = ConfigPageManager.getGlobalPtr()
        builtins.cvMgr = ConfigVariableManager.getGlobalPtr()
        builtins.pandaSystem = PandaSystem.getGlobalPtr()
        builtins.wantUberdog = self.config.GetBool('want-uberdog', 1)

“base” is an instance of the ShowBase class. In the ShowBase constructor, it will assign itself to a builtin called “base”, which is from that moment on available from all other modules.

You may not see a ShowBase instantiation if your application imports the direct.directbase.DirectStart module. All this module does is just instantiate ShowBase, which also puts “base” into the builtin scope.

Edit: d’aw, wezu beat me to it!