4

I'd like use if __name__ != '__main__': and then end the execution of a script at that point when it's being imported, rather than doing the usual if __name__ == '__main__': and indentation of all the rest of the code in the file.

However I have been unable to determine what would cause only this to happen and not have other undesirable side-effects. sys.exit() stops the whole interpreter and all the other things I've tried either raise some sort of exception or are illegal.

Update:

I've selected @trutheality's answer because it accomplishes what I want and is extremely easy to start using. That said, I thought several of the other answers very interesting and/or clever -- thanks to all who responded -- and plan on investigating some of them further as time permits. I had no idea doing what I want could get so involved.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • What exactly do you want to happen? Do you want the import to succeed, but silently do nothing? – JasonFruit Jun 02 '11 at 16:51
  • 6
    If it's just for the sake of not indenting the rest of the code, forget it. If the script is large enough for this to be a serious problem, most of the code should be in functions anyway ;) –  Jun 02 '11 at 17:04
  • @JasonFruit: Yes, I want the import to succeed. – martineau Jun 02 '11 at 17:26
  • @martineau: I don't see a way you can do that without at least importing the name of the module, though you can make it be empty. Really, you should just have it raise an exception if you try to import it, and let the importing script handle it. Things that look like they succeed but do nothing that could be defined as success are a problem. – JasonFruit Jun 02 '11 at 17:36
  • @JasonFruit: By succeed I mean that everything defined in the module up until that point is retained as normal -- which generally seems to rule out raising exceptions which aborts the loading of the module (unless you do something sinister looking like in @David's `partialimport` answer). – martineau Jun 02 '11 at 20:45
  • @delnan: To be honest that was my original motivation, but I think there are other more legitimate uses which would necessarily depend on the value of `__name__`. For myself, perhaps I just need to switch to tabs-of-two so the extra level of indentation doesn't bother me so much. ;-) – martineau Jun 02 '11 at 20:54
  • I see --- I thought it was all-or-nothing. In that case, I'm with @delnan. – JasonFruit Jun 02 '11 at 21:43

4 Answers4

2

Very similar to my original answer but slightly scarier

partialimport.py

class PartialImport(Exception):
    def __init__(self, locals, msg = ""):
        self.module = locals

main.py

from partialimport import PartialImport
try:
   import foo
except PartialImport, e:
    #Note e.module and therefore foo will be a Dict and not a module instance!
    foo = e.module

foo.py

from partialimport import PartialImport

class Boo:
    pass

if __name__ != "__main__":    
    raise PartialImport(locals())

class Foo:
    pass

Disclaimer

This is a fantastically terrible hack that will increase the likelihood of your peers murdering you, but this does work.

David
  • 17,673
  • 10
  • 68
  • 97
  • 1
    Not quite as transparent (or usable) as I hoped for, but +1 for creativity and low overhead, you evil genius. ;-) – martineau Jun 02 '11 at 19:15
  • @martineau Honestly I'm looking forward to seeing if anyone can find a better/more elegant solution. – David Jun 02 '11 at 19:17
  • This I'll upvote, although I wouldn't want anyone to actually use it. For even more evil, you could actually use `types.ModuleType` and replace the `__import__` function so you could get foo as a module anyway. – Thomas K Jun 02 '11 at 23:04
  • Sorry, but this actually doesn't work, although there *are* entries in the `e.module` dict that correspond to thing(s) defined in the module (`foo`, in your example), they're all mapped to `None` and unusable. I think this is the result of raising an except -- perhaps along the lines of what's described happening in [this answer](http://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name/5365733#5365733) to a different question (but the fix there doesn't apply in this scenario). – martineau Jun 04 '11 at 21:28
  • @martineau That's dissappointing, will see if I can find yet another outside of the box solution. – David Jun 06 '11 at 17:02
2

Another Hack:

# code

if __name__ == "__main__": exec("""

# main code

#""")

So... you've lost the indentation, but also the syntax highlighting and any other features of the editor you were using, unless you comment out the if line every time you edit.

trutheality
  • 23,114
  • 6
  • 54
  • 68
  • +1 for being the cleanest and least obtrusive answer I've seen so far. – martineau Jun 03 '11 at 01:51
  • @Tadeck: Because it achieves the stated goals with minimal effort, side-effects, and maintenance. I try to respond to the use of `exec()` on an objective, analytical, and case-by-case basis rather than with an automatic knee-jerk. – martineau Jun 03 '11 at 15:41
  • @martineau This was not _automatic knee-jerk_ from my side, rather experience with code containing careless usage of `exec()`-like solutions. Also OP stated that he would like solutions other than `doing the usual if __name__ == '__main__': and indentation of all the rest of the code in the file`. Why do you think he would prefer IDE treating all of his code as a string instead of just indenting the code? – Tadeck Jun 03 '11 at 16:06
  • @martineau I have just realized you are the OP and thus you may be actually correct when assuming what OP had in mind and what OP really wanted/needed ;) – Tadeck Jun 03 '11 at 16:12
  • @Tadeck: No problem. IMHO this use of `exec()` would be acceptable. FWIW, I often don't use the Python IDE I have, preferring instead the fairly-sophisticated text-editor I've used for years -- but in either case (as the edited answer now points out), simply commenting-out the `if` with a single `#` will make all the code in the `exec()` a non-string. In fact, I can probably make a macro that will toggle it on and off with one keystroke. – martineau Jun 04 '11 at 15:04
1

main.py

try:
   import foo
except:
    print "Failed to import foo"

foo.py

At top of file

if __name__ != "__main__":
   raise RunTimeError("foo must be run as main, not as a module.")

class foo(Object):
   pass

Since python processes a file sequentially, then class Foo would not be defined.

Another thought would be to overload the import logic itself via PEP 302

David
  • 17,673
  • 10
  • 68
  • 97
  • 3
    Nearly +1. Catching any exception on an import is bad practice. `except RunTimeError`, please. And if you're being picky, you'd define your own exception, so it couldn't be raised by anything else. – Thomas K Jun 02 '11 at 17:04
  • Actually, I changed my mind. The exception aborts loading the module, so you can't use any of the foo module in your main.py. – Thomas K Jun 02 '11 at 17:09
  • Perhaps I was unclear, I want the import to be successful, just stop at that point. Running the module rather than executing it as `__main__` is fine and would just continue beyond the `if`. – martineau Jun 02 '11 at 17:33
  • @Thomas K I usually don't go for best practices with SO snippets, mostly aiming for the idea and not so much the details, but for production grade code using the "Diaper" anti-pattern is definitely asking for verbal abuse ;) – David Jun 02 '11 at 17:46
  • For anyone else wondering, you can read about the Diaper anti-pattern [here](http://mike.pirnat.com/2009/05/09/the-diaper-pattern-stinks/). – martineau Jun 02 '11 at 19:03
  • @David: Of course. But here it's easy to write decent (albeit not great) code, so we will jump on a bad example. I'd not heard of the 'Diaper' anti-pattern before, but the concept is familiar. – Thomas K Jun 02 '11 at 23:10
1

Best solution IMO: have two files.

module_main.py

import actual_module.py

if __name__ != '__main__':
    raise RunTimeError("You should be importing actual_module.py, not module_main.py")

# Your "main" code

actual_module.py

# Actual module code (classes, functions, etc)

This is "clean" in the sense that an exception is thrown only when something is actually wrong -- no one should ever import module_main.py, they should import actual_module.py.

April Fools solution

If you're using python 2.3 there is a goto module from entrian, that apparently works! It was made as an April fools joke, and should never be used, (if you look at the source you'll see why: it adds a lot of overhead) but as a proof of concept it seems like the only way I can find to accomplish what you want in any sort of concise way.

from goto import goto, label

# Code that should always be imported (classes etc.)

if __name__ != "__main__":
    goto .end

# Stuff to be executed when this is main, NOT indented

label .end
trutheality
  • 23,114
  • 6
  • 54
  • 68
  • +1 for the interesting answer. The `goto` module's use of `sys.settrace()` might be clue to a real answer. – martineau Jun 02 '11 at 18:46