Is there a way to get __thismodule__?

B

benhoyt

Is there a way to get __thismodule__ in Python? That is, the current
module you're in. Or isn't that known until the end of the module?

For instance, if I'm writing a module of message types/classes, like
so:

class SetupMessage(Message):
number = 1

class ResetMessage(Message):
number = 2

class OtherMessage(Message):
number = 255

nmap = { # maps message numbers to message classes
1: SetupMessage,
2: ResetMessage,
255: OtherMessage,
}

Or something similar. But adding each message class manually to the
dict at the end feels like repeating myself, and is error-prone. It'd
be nice if I could just create the dict automatically, something like
so:

nmap = {}
for name in dir(__thismodule__):
attr = getattr(__thismodule__, name)
if isinstance(attr, Message):
nmap[attr.number] = attr

Or something similar. Any ideas?

(A friend suggested class decorators, which is a good idea, except
that they're not here until Python 2.6 or Python 3000.)

Cheers,
Ben.
 
B

benhoyt

Replying to myself here, after discovering more. :)
Is there a way to get __thismodule__ in Python?

It looks like __thismodule__ is just sys.modules[__name__]. Neat.

Hmmm ... does sys.modules always already contain the currently-being-
loaded module? Or is this a hack that only happens to work? (It does;
I've tested it now.) Just wondering, because the Python docs say that
sys.modules is "a dictionary that maps module names to modules which
have *already been loaded*."
if isinstance(attr, Message):
nmap[attr.number] = attr

Oops, this was untested code. I actually meant issubclass (or
something similar) here, not isinstance.

Cheers,
Ben.
 
J

Jeff McNeil

This is somewhere that I would personally use a metaclass. That way,
if you define more subclasses of Message, you're not limited to doing
so in that single module.

Someone correct me if this is goofy, I don't do much metaclass programming.

Perhaps something like:

#!/usr/bin/python

class MetaMessage(type):
nmap = {}
def __new__(mcl, name, bases, d):
obj = super(MetaMessage, mcl).__new__(mcl, name, bases, d)

# Check __name__ here as 'Message' isn't yet defined.
if "Message" in [b.__name__ for b in bases]:
if "number" not in d:
raise TypeError("Message protocol expects a number attribute")
MetaMessage.nmap[d['number']] = obj

# Complete Creation.
return obj

class Message(object):
__metaclass__ = MetaMessage

class MessageOne(Message):
number = 1

class MessageTwo(Message):
number = 2

class MessageFourThousandThreeHundredAndTwentyTwoPointOne(Message):
number = 4322.1

print MetaMessage.nmap

Which results in:
mac:~ jeff$ !p
python test.py
{1: <class '__main__.MessageOne'>, 2: <class '__main__.MessageTwo'>,
4322.1000000000004: <class
'__main__.MessageFourThousandThreeHundredAndTwentyTwoPointOne'>}
mac:~ jeff$

Thanks!

Jeff

Replying to myself here, after discovering more. :)

Is there a way to get __thismodule__ in Python?


It looks like __thismodule__ is just sys.modules[__name__]. Neat.

Hmmm ... does sys.modules always already contain the currently-being-
loaded module? Or is this a hack that only happens to work? (It does;
I've tested it now.) Just wondering, because the Python docs say that
sys.modules is "a dictionary that maps module names to modules which
have *already been loaded*."

if isinstance(attr, Message):
nmap[attr.number] = attr


Oops, this was untested code. I actually meant issubclass (or
something similar) here, not isinstance.


Cheers,
Ben.
 
P

Peter Otten

benhoyt said:
Is there a way to get __thismodule__ in Python? That is, the current
module you're in. Or isn't that known until the end of the module?

For instance, if I'm writing a module of message types/classes, like
so:

class SetupMessage(Message):
number = 1

class ResetMessage(Message):
number = 2

class OtherMessage(Message):
number = 255

nmap = { # maps message numbers to message classes
1: SetupMessage,
2: ResetMessage,
255: OtherMessage,
}

Or something similar. But adding each message class manually to the
dict at the end feels like repeating myself, and is error-prone. It'd
be nice if I could just create the dict automatically, something like
so:

nmap = {}
for name in dir(__thismodule__):
attr = getattr(__thismodule__, name)
if isinstance(attr, Message):
nmap[attr.number] = attr

Or something similar. Any ideas?

Use globals():

def is_true_subclass(a, b):
try:
return issubclass(a, b) and a is not b
except TypeError:
return False

nmap = dict((m.number, m) for m in globals().itervalues()
if is_true_subclass(m, Message))
print nmap

You may also rely on duck-typing, assuming that everything that has a number
attribute is a Message subclass:

nmap = dict((m.number, m) for m in globals().itervalues()
if hasattr(m, "number"))

Peter
 
D

Duncan Booth

benhoyt said:
But adding each message class manually to the
dict at the end feels like repeating myself, and is error-prone. It'd
be nice if I could just create the dict automatically, something like
so:

nmap = {}
for name in dir(__thismodule__):
attr = getattr(__thismodule__, name)
if isinstance(attr, Message):
nmap[attr.number] = attr

Or something similar. Any ideas?

(A friend suggested class decorators, which is a good idea, except
that they're not here until Python 2.6 or Python 3000.)

Why not just use your base class?

class Message(object):
@staticmethod
def getmap():
return dict((cls.number, cls)
for cls in Message.__subclasses__())

class SetupMessage(Message):
number = 1

class ResetMessage(Message):
number = 2

class OtherMessage(Message):
number = 255

nmap = Message.getmap()
 
B

benhoyt

Wow -- thanks, guys. And who said Python only gives you one way to do
things. :) Metaclasses, globals(), and __subclasses__. Thank Duncan
for the __subclassess__ tip -- I didn't know about that.

I'd totally overlooked globals(). It's exactly what I was looking for
-- thanks, Peter. And I like your is_true_subclass() helper function
too.

I must say, I found it a bit weird that the first argument to
issubclass() *has* to be a class. I would have thought issubclass(42,
MyClass) would simply return False, because 42 is definitely not a
subclass of MyClass. But I guess "explicit is better than implicit",
and being implicit here might mask bugs.

globals() feels like the "right" way to do what I want -- fairly
simple and direct. Metaclasses are cool, but probably a touch too
complicated for this. And __subclassess__ seems a bit undocumented and
perhaps implementation-defined.

Cheers,
Ben.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top