Get the source of a class reliably?!?

dieter dieter at handshake.de
Tue Apr 30 02:38:55 EDT 2019


"computermaster360 ." <computermaster360 at gmail.com> writes:

> Does anyone have an idea why classes don't contain their definition
> line number as functions or methods do?
>
>>>> some_fun.__code__.co_firstlineno
> 123

Because classes do not have associated "code" objects.

As you see above, it is not the function itself ("some_fun" in
your case) that carries the line information but the associated
"code object" ("some_fun.__code__").

Functions typically are executed several times and
each execution needs to execute the associated code;
therefore, functions reference this code.
There is no specific code directly associated with a class: 
general (i.e. not class specific) code is used to collect
the operands for the class contruction (i.e. base classes, attribute/method
definitions), then the class' metaclass is used to really construct
the class object. Therefore, class objects have no associated "code object".


> ...
> This leads to some funny stuff when using `inspect`, such as this:
>
> -- weird.py -----------------------------
> """
> class C:
>   HAHAHA! YOU FOOL!
> """
>
> class C:
>   "this is a perfectly ok class"
> ...
>>>> inspect.getsource(weird.C)
> class C:
>   HAHAHA! YOU FOOL!
>
>
> Why ???

In order to answer questions of this kind yourself, you
can use the fact that Python is open source.

A look into "inspect.py" shows that Python uses the following
code to find the source lines for a class:

    if isclass(object):
        name = object.__name__
        pat = re.compile(r'^(\s*)class\s*' + name + r'\b')
        # make some effort to find the best matching class definition:
        # use the one with the least indentation, which is the one
        # that's most probably not inside a function definition.
        candidates = []
        for i in range(len(lines)):
            match = pat.match(lines[i])
            if match:
                # if it's at toplevel, it's already the best one
                if lines[i][0] == 'c':
                    return lines, i
                # else add whitespace to candidate list
                candidates.append((match.group(1), i))
        if candidates:
            # this will sort by whitespace, and by line number,
            # less whitespace first
            candidates.sort()
            return lines, candidates[0][1]

The comment "make some effort ..." indicates that the author
knew that the result will not be correct in all cases.
You have found such a case.



More information about the Python-list mailing list