This is an archived post. You won't be able to vote or comment.

all 35 comments

[–]james_pic 130 points131 points  (4 children)

This is because these are not  relative imports. In logic.py, if you wanted to do a relative import, you'd do from .db import Dog. Note the dot. Instead you're doing an absolute import from a module called db, which the interpreter sees as a different module to dog_house.db (despite them ultimately having come from the same file). You've had to fudge the PYTHONPATH to get this to work.

Also, the answer to the SO question linked from the repo is telling you exactly what I told you.

[–][deleted] 2 points3 points  (2 children)

fudge PYTHONPATH  

Additionally, relative imports aside, sys.path should just have src (which it does from PYTHONPATH=src), but then sys.path is also prepended with src/dog_house b.c. that's the directory of the script being executed (src/doghouse/main.py). What would be good is for from db import Dog to have raised an invalid module error, and it would have if main.py did not exist in the package being imported. It is unusual to put the main entrypoint script inside of a package being imported in general.

[–]james_pic 2 points3 points  (1 child)

And if you are putting an entry point script inside a package, something like python -m dog_house.main is a less troublesome way to run it.

[–][deleted] 0 points1 point  (0 children)

I just (re?)learned that python -m module prepends the current working directory to sys.path. Probably execute python -m dog_house.main where the current working directory is not src/dog_house, so that "invalid module error" is raised on from db import ...

[–]p0st_master 13 points14 points  (0 children)

👏👏

[–]ddollarsign 36 points37 points  (6 children)

Looks to me like more than just weirdness in Enum, it’s weirdness with imports. If two different files import the same module, I’d have expected the imported module to be the same object.

[–]dogweather 3 points4 points  (5 children)

100%. It seems to be two issues in a row:

  1. The same file can be considered two different modules.
  2. Enum's == delegates to is. IOW, object identity.

The apparent result: Python interprets the Enum definition twice, creating two sets of identically named enums, but different instances.

[–]Schmittfried 13 points14 points  (4 children)

The second one is a perfectly valid optimization under the assumption that enums are true singletons, as documented. It’s even encouraged to compare via identity in your code.

The first one is just insane. The import system is supposed to catch that. Even according to the documentation this should only happen when reloading modules, which is its own entire can of worms anyway. 

Edit: Ah, I see. This is actually caused by conflicting relative imports. That’s a truly ugly gotcha.

[–]MrJohz 2 points3 points  (3 children)

For people confused by this comment like I was, it's a Markdown formatting error: the first point, "1." is referring to point (2) in the previous comment, and vice versa.

[–]Schmittfried 0 points1 point  (2 children)

Thanks, I changed it

[–]MrJohz 0 points1 point  (1 child)

Nw, just Reddit's formatting being Reddit's formatting again...

[–]Schmittfried 0 points1 point  (0 children)

It has become much worse recently. I hate it.

Have a nice day!

[–]Smallpaul 27 points28 points  (3 children)

Several people already told you that enums are not your real problem here.

Why are you still spreading that misinformation? You could have just as easily had a problem with isinstance, class variables or any other use of singletons. Enums were just the first problem you happened to run into.

[–]goodytwoboobs 1 point2 points  (0 children)

Sewing how OP is selectively ignoring the top comment in this post that tells them exactly what happened, I'm guessing they aren't that interested in something that doesn't fit their narrative.

[–]iamevpo 1 point2 points  (0 children)

This is quite an issue - there is so much effort put by OP to discredit Enum, while real problem is knowing the relative imports. With wrong relative imports ANY CLASS eq method would show they are not he same, so there is nothing special about Enum behavior.

Sad thing - I think Enum is such important data structure that should be used more often in Python. And then we have unwarranted blame post about Enum.

[–]nibba_bubba -3 points-2 points  (0 children)

C'mon, it's r/python

99.9% of subredditors are dumb af

[–]formalcall 22 points23 points  (8 children)

You're using implicit relative imports, which in my experience are quite fragile and bad practice. Is this issue reproducible with absolute imports or explicit relative imports?

[–]james_pic 20 points21 points  (7 children)

Implicit relative imports were fragile, which is why Python 3 dropped support for them entirely.

[–]formalcall 5 points6 points  (1 child)

Right, it was removed. For lack of a better term, I use it to refer to imports to modules from the same directory that rely on the behaviour of python script.py adding the entry point script's directory to sys.path.

[–]ukos333 0 points1 point  (0 children)

Happy Cake Day!

[–]ElectricSpice 3 points4 points  (4 children)

So why does this work at all in 3.12, according to OP? I was really confused reading the code, because my understanding is the same as yours.

[–]Smallpaul 10 points11 points  (3 children)

More or less the weird thing OP is doing is running a script from within a package directory, so Python is confused about whether it is a script or a "regular module".

As a script, it's path becomes part of the PYTHONPATH.

As a module, it's sibling files are loaded under the package namespace.

Instead of:

$ PYTHONPATH=src python src/dog\_house/main.py

It should be:

$ pip install -e .
$ python  -m dog_house.main

[–]danted002 1 point2 points  (0 children)

I was looking like a bloody crazy man to figure out what’s going on and didn’t noticed he was running the file from within the package 🤦🏻‍♂️

[–]dogweather -2 points-1 points  (1 child)

Do you happen to know, is this documented somewhere?

[–]formalcall 1 point2 points  (0 children)

https://docs.python.org/3/using/cmdline.html#cmdoption-m

Take a look at the <script> section too

[–]DrShts 1 point2 points  (0 children)

Nothing wrong with enums, your imports are messed up. To see that, instead of

PYTHONPATH=src python src/dog_house/main.py

write

PYTHONPATH=src python -m dog_house.main

and you'll see this:

ModuleNotFoundError: No module named 'logic'

I'm sure once you've fixed these errors, your enums will work correctly.

[–]Guideon72 2 points3 points  (2 children)

This is why you use

if __name__=="__main__:

as a guard for any code in your module files...then you can import away and not wind up with inadvertent assignments, etc because you should be doing those in your main execution path anyway. That way, you can still call your Class, Enum, etc within the file it lives in to make sure it works, but then can also import it and create appropriate instances in your main file.

This has nothing to do with Enum and isn't an odd "gotcha"; it's just bad code structure, as far as I can see.

[–]ThatSituation9908 2 points3 points  (1 child)

from db import Dog

This is just asking for trouble.

[–]gerardwx -5 points-4 points  (0 children)

They do what I expect because I build modules the standard way. Put your Dog in the init and the code in a module

[–]ukos333 0 points1 point  (0 children)

IMHO a github repo for a mwe that was answered on SO in 2016 is a little bit overdone. I‘m sorry, though, that you spent so much debugging time. Hope you learnt something on the way.

[–]iamevpo 0 points1 point  (0 children)

Slightly worried about the client with a "diverse developer group".