133

Given a class C in Python, how can I determine which file the class was defined in? I need something that can work from either the class C, or from an instance of C.

I am doing this because I am generally a fan of putting files that belong together in the same folder. I want to create a class that uses a Django template to render itself as HTML. The base implementation should infer the filename for the template based on the filename that the class is defined in.

Say I put a class LocationArtifact in the file "base/artifacts.py", then I want the default behaviour to be that the template name is "base/LocationArtifact.html".

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Staale
  • 27,254
  • 23
  • 66
  • 85
  • 1
    Duplicate: http://stackoverflow.com/questions/269795/how-do-i-find-the-location-of-python-module-sources, http://stackoverflow.com/questions/602846/how-can-i-access-the-current-executing-module-or-class-name-in-python – S.Lott Mar 30 '09 at 14:06
  • 3
    Those assume you know the module you are looking up the file for, I will just have the module string as I work with implementations off a class. – Staale Mar 31 '09 at 05:51
  • Does this answer your question? [How do I find the location of Python module sources?](https://stackoverflow.com/questions/269795/how-do-i-find-the-location-of-python-module-sources) – mkrieger1 Jul 30 '23 at 21:46

4 Answers4

180

You can use the inspect module, like this:

import inspect
inspect.getfile(C.__class__)
DNS
  • 37,249
  • 18
  • 95
  • 132
  • 1
    Not sure what I was doing different but instead of `getfile` I had to use: `inspect.getmodule(C.__class__)` – AJP Jan 16 '14 at 00:34
  • 6
    Note: does not work on a classes created by the user – Daniel Braun Mar 20 '19 at 14:08
  • 12
    This should probably be `inspect.getfile(C)`. If `C` is a class, then `C.__class__` refers to `object`, which will raise an exception `TypeError: is a built-in class`. I think that only for an instance `c` do you want to use `inspect.getfile(c.__class__)`. – cheshirekow Jul 29 '19 at 21:35
  • 4
    This did not for a class that extends an abstract base class (`metaclass=abc.ABCMeta`), as it returns `/usr/local/lib/python3.7/abc.py` instead of the appropriate file. The solution by @JarretHardie (below) worked better. – martian111 Sep 08 '19 at 20:48
  • The accepted answer: *guaranteed to be the wrong answer.* It's a StackOverflow tradition by now. – Cecil Curry Apr 18 '23 at 06:46
50

try:

import sys, os
os.path.abspath(sys.modules[LocationArtifact.__module__].__file__)
Jarret Hardie
  • 95,172
  • 10
  • 132
  • 126
  • 3
    To get the path from an instance of C (as desired by the OP), replace `LocationArtifact` with `obj.__class__` where obj is an instance of LocationArtifact. – martian111 Sep 08 '19 at 20:52
  • 1
    This also works for meta classes! – Vidar Sep 07 '22 at 12:55
  • 1
    inspect take alot resources, this is perfect – Micah Sep 28 '22 at 03:07
  • 1
    **This is the way.** `inspect` should *generally* be avoided in production code. It's slow. It's fragile. It fails in common edge cases. If you can't write a one-liner that does it, you probably shouldn't do it. – Cecil Curry Apr 18 '23 at 06:57
5

This is the wrong approach for Django and really forcing things.

The typical Django app pattern is:

  • /project
    • /appname
      • models.py
      • views.py
      • /templates
        • index.html
        • etc.
Soviut
  • 88,194
  • 49
  • 192
  • 260
  • 1
    +1: Do what Django does naturally and life is so much simpler. – S.Lott Mar 30 '09 at 14:18
  • 1
    Agreed. Django is one of the frameworks with the least amount of "magic", but templates, template tags and apps have some expectations as part of their pattern. If you're having to do wacky class inference you're probably going in the wrong direction. – Soviut Mar 30 '09 at 18:33
0

You can use the error to get the path of your class

import os
import traceback
class C:
  def getClassPath(self):
    #get foldet path
    try:
        raise ValueError("Example error")
    except Exception as e:
        tb = traceback.extract_tb(e.__traceback__)
       
        return os.path.dirname(tb[-1].filename)
c = C()
print('path is',c.getClassPath())