Re: Enums are Singletons - but not always?

2020-05-23 Thread Terry Reedy

On 5/23/2020 2:21 PM, Ralf M. wrote:


# Code of mod1.py #
import enum, mod2
class En(enum.Enum):
     A = 1
     B = 2
def main():
     a = mod2.getA()
     print("a is En.A:", a is En.A)
     print("a:", repr(a), "    En.A:", repr(En.A))
     print("id(a), id(a.__class__)", id(a), id(a.__class__))
     print("id(En.A), id(En)  ", id(En.A), id(En))
if __name__ == "__main__":
     main()


> # Code of mod2.py #
> import mod1
> def getA():
> return mod1.En.A

As a couple of people mentioned, the issue is the circular import.  When 
you run mod1 from a command line, its name is __main__.  Its execution 
of mod1 code stops to execute enum code and then mod2 code.


Execution of mod2 stops to execute mod1 code in a new module named 
'mod1'.  When the second execution of mod1 code does the import of enum 
and mod2, sys.modules['enum'] and sys.modules['mod2'] exist, are found, 
and used to satify the import.  So enum and mod2 code are not executed 
again.  In the 'mod1' module, 'mod2' is linked to the *incomplete* mod2. 
 This 'works' in your code because the reference to mod2 within def 
main is not used because the name is 'mod1', not '__main__'.  But if in 
mod1, you had written


import enum
from mod2 import getA

the second execution of mod1 would fail because mod2.getA does not exist 
because execution of mod2 is paused to import mod1


When the mod1 import in mod2 finishes, the execution of mod2 code for 
'mod2' finishes without issue.  Then the execution of 'mod1' code in 
'__main__' finishes with the call to main.


When you instead interactively import mod1, it is executed just once and 
you have to explicitly call main.


In idlelib, pyshell currently has the IDLE Shell code, the main() 
function, and a couple of classes used elsewhere.  (An atrocious design 
that I inherited and have only partly fixed.)  As a *temporary* fix to 
an issue due to there being  duplicate '__main__' and 'pyshell' modules, 
I added the following to the top of pyshell.


import sys
if __name__ == "__main__":
sys.modules['idlelib.pyshell']=sys.modules['__main__']

But doing what others suggested, limiting the main or start module to 
one-time code, is better.



--
Terry Jan Reedy


--
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Roel Schroeven

Richard Damon schreef op 23/05/2020 om 20:57:

On 5/23/20 2:21 PM, Ralf M. wrote:

Hello,

recently I wrote a small library that uses an Enum. That worked as
expected. Then I added a main() and if __name__ == "__main__" to make
it runable as script. Now Enum members that should be the same aren't
identical any more, there seem to be two instances of the same Enum.

I think I know what's going on, but cannot find a good and elegant way
to avoid the problem. I hope someone here can help me there.


I don;'t think Python anywhere defines that a enum will be a singleton,
and you should be checking for equality (==) not identity (is)


So much this. Always check for equality instead of identity, unless you 
know what you're doing and you have a very good reason to use identity.


Everywhere I see questions all the time from people new to Python, 
confused about stuff that happens when comparing using identity. I would 
like to know where that comes from ... are there tutorials that 
encourage using identity checks?


--
"Honest criticism is hard to take, particularly from a relative, a
friend, an acquaintance, or a stranger."
-- Franklin P. Jones

Roel Schroeven

--
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Ethan Furman

On 05/23/2020 11:57 AM, Richard Damon wrote:


I don't think Python anywhere defines that a enum will be a singleton,
and you should be checking for equality (==) not identity (is)


If you're not sure, please do a little research first.  We have enough bad 
information on the 'nets already.

According to Kushal's link:


The most interesting thing about Enum members is that they are singletons.


The only time using `==` is necessary is for mixed-Enums, such as IntEnum, and 
you need to compare what could be an IntEnum member in code that could be using 
actual `int`s.

--
~Ethan~
--
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Chris Angelico
On Sun, May 24, 2020 at 5:58 AM Kushal Kumaran  wrote:
>
> "Ralf M."  writes:
>
> > Below are a simplified code sample, the results when I run it and my
> > thoughts.
> >
> > # Code of mod1.py #
> > import enum, mod2
> > def main():
> > a = mod2.getA()
> > # End of mod1.py #
> >
> > # Code of mod2.py #
> > import mod1
> > def getA():
> > return mod1.En.A
> > # End of mod2.py #
> >
> I think of circular dependencies as a code smell.  You can move the enum
> definition into a separate module, and have both mod1 and mod2 import
> that, or like you say, by moving the main function into its own module.
> Maybe you can explain why you don't want this.

Correct - and especially since you're using the main module also as a
module. Currently, that is something you just plain shouldn't do. You
end up with one copy of the module called __main__ and another one
called mod1, and they're completely independent. Just don't do that.

However, there HAVE been some proposals to make this sort of thing
work. In general, they'd end up doing something like having the module
inserted into sys.modules twice, once as __main__ and once as mod1, so
you end up getting the same module in both cases. That would work,
since (with only one module) there'd only be one En class. I don't
think any such proposal has gotten fully serious yet, but if this is
something you're interested in (or feel strongly about), dig through
the python-ideas archives to see some of the arguments for and
against.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Kushal Kumaran
"Ralf M."  writes:

> Hello,
>
> recently I wrote a small library that uses an Enum. That worked as
> expected. Then I added a main() and if __name__ == "__main__" to make
> it runable as script. Now Enum members that should be the same aren't
> identical any more, there seem to be two instances of the same Enum.
>
> I think I know what's going on, but cannot find a good and elegant way
> to avoid the problem. I hope someone here can help me there.
>
> Below are a simplified code sample, the results when I run it and my
> thoughts.
>
> # Code of mod1.py #
> import enum, mod2
> class En(enum.Enum):
> A = 1
> B = 2
> def main():
> a = mod2.getA()
> print("a is En.A:", a is En.A)
> print("a:", repr(a), "En.A:", repr(En.A))
> print("id(a), id(a.__class__)", id(a), id(a.__class__))
> print("id(En.A), id(En)  ", id(En.A), id(En))
> if __name__ == "__main__":
> main()
> # End of mod1.py #
>
> # Code of mod2.py #
> import mod1
> def getA():
> return mod1.En.A
> # End of mod2.py #
>
> # Results when run: #
> C:\tmp>py mod1.py
> a is En.A: False
> a:  En.A: 
> id(a), id(a.__class__) 33305864 7182808
> id(En.A), id(En)   33180552 7183752
>
> C:\tmp>py
> Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC
> v.1916 64 bit
> (AMD64)] on win32
> Type "help", "copyright", "credits" or "license" for more information.
 import mod1
 mod1.main()
> a is En.A: True
> a:  En.A: 
> id(a), id(a.__class__) 49566792 44574280
> id(En.A), id(En)   49566792 44574280

>
> So: When run as script there are two instances of En (different ids),
> but when mod1 is imported and mod1.main() is run it works as expected
> (just one instance of En, same ids).
> BTW: py -m mod1 doesn't work either.
>
> What I thing is happening:
> When the script is run, mod1.py is executed and an instance of En and
> its members is created. During the same run mod1 is also imported (via
> mod2), the file mod1.py is executed again as part of the import and
> another, different instance of En and its members is created.
>
> How do I have to change mod1.py to avoid the problem?
> Currently I have moved main() into a new file script.py. That works,
> but is not what I wanted.
>

I think of circular dependencies as a code smell.  You can move the enum
definition into a separate module, and have both mod1 and mod2 import
that, or like you say, by moving the main function into its own module.
Maybe you can explain why you don't want this.

The note in the documentation about enum members being singletons only
applies to the members.  It does not apply to the enum class itself.  In
your case, __main__.En is not the same as mod1.En.  The members being
singleton means that, for the same enum class En, En.A will refer to the
same object as En(1), or, perhaps an unpickled object that has the same
value.

> I doubt it's a bug in the enum module, but should that be the case,
> I'm willing to open an issue on the bug tracker.
>
> Or can nothing be done about it?
>

It is not specific to the Enum class.  You'll see the same behaviour
with any class that gets imported under two different module names.

> Looking forward to any ideas
> Ralf M.
>
> P.S.:
> As I was about to send this post the following modification occured to
> me (see below). It works, but it doesn't feel right to import a module
> directly from inside itself.
> # Modified code of mod1.py (one line added) #
> import enum, mod2
> class En(enum.Enum):
> A = 1
> B = 2
> from mod1 import En  # NEW LINE, overwrite just defined En
> def main():
> a = mod2.getA()
> print("a is En.A:", a is En.A)
> print("a:", repr(a), "En.A:", repr(En.A))
> print("id(a), id(a.__class__)", id(a), id(a.__class__))
> print("id(En.A), id(En)  ", id(En.A), id(En))
> if __name__ == "__main__":
> main()

I agree this is unpleasant to read.

-- 
regards,
kushal
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Peter Otten
Peter Otten wrote:

>> # Code of mod2.py #
> import __main__ as mod1
>> def getA():
>>return mod1.En.A
>> # End of mod2.py #
> 
> but that would hardcode the assumption that __main__ is always mod1.py.

I should have mentioned the cyclic dependency -- if two modules import each 
other one may not be completely set up when the other tries to access it.


-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Kushal Kumaran
Richard Damon  writes:

> On 5/23/20 2:21 PM, Ralf M. wrote:
>> Hello,
>>
>> recently I wrote a small library that uses an Enum. That worked as
>> expected. Then I added a main() and if __name__ == "__main__" to make
>> it runable as script. Now Enum members that should be the same aren't
>> identical any more, there seem to be two instances of the same Enum.
>>
>> I think I know what's going on, but cannot find a good and elegant way
>> to avoid the problem. I hope someone here can help me there.
>
> I don;'t think Python anywhere defines that a enum will be a singleton,
> and you should be checking for equality (==) not identity (is)
>

https://docs.python.org/3/library/enum.html#enum-members-aka-instances

-- 
regards,
kushal
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Peter Otten
Ralf M. wrote:

> Hello,
> 
> recently I wrote a small library that uses an Enum. That worked as
> expected. Then I added a main() and if __name__ == "__main__" to make it
> runable as script. Now Enum members that should be the same aren't
> identical any more, there seem to be two instances of the same Enum.
> 
> I think I know what's going on, but cannot find a good and elegant way
> to avoid the problem. I hope someone here can help me there.
> 
> Below are a simplified code sample, the results when I run it and my
> thoughts.
> 
> # Code of mod1.py #
> import enum, mod2
> class En(enum.Enum):
>  A = 1
>  B = 2
> def main():
>  a = mod2.getA()
>  print("a is En.A:", a is En.A)
>  print("a:", repr(a), "En.A:", repr(En.A))
>  print("id(a), id(a.__class__)", id(a), id(a.__class__))
>  print("id(En.A), id(En)  ", id(En.A), id(En))
> if __name__ == "__main__":
>  main()
> # End of mod1.py #
> 
> # Code of mod2.py #
> import mod1
> def getA():
>  return mod1.En.A
> # End of mod2.py #
> 
> # Results when run: #
> C:\tmp>py mod1.py
> a is En.A: False
> a:  En.A: 
> id(a), id(a.__class__) 33305864 7182808
> id(En.A), id(En)   33180552 7183752
> 
> C:\tmp>py
> Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916
> 64 bit
> (AMD64)] on win32
> Type "help", "copyright", "credits" or "license" for more information.
>  >>> import mod1
>  >>> mod1.main()
> a is En.A: True
> a:  En.A: 
> id(a), id(a.__class__) 49566792 44574280
> id(En.A), id(En)   49566792 44574280
>  >>>
> 
> So: When run as script there are two instances of En (different ids),
> but when mod1 is imported and mod1.main() is run it works as expected
> (just one instance of En, same ids).
> BTW: py -m mod1 doesn't work either.
> 
> What I thing is happening:
> When the script is run, mod1.py is executed and an instance of En and
> its members is created. During the same run mod1 is also imported (via
> mod2), the file mod1.py is executed again as part of the import and
> another, different instance of En and its members is created.
> 
> How do I have to change mod1.py to avoid the problem?
> Currently I have moved main() into a new file script.py. That works, but
> is not what I wanted.

But that is exactly the right approach. Avoid importing the main script 
under its name because that will run all code that is not guarded by 

if __name__ == "__main__": ...

twice. In your case it's enum, but the same goes for every class, function 
or object and the module itself. In Python they are all created rather than 
declared.

> I doubt it's a bug in the enum module, but should that be the case, I'm
> willing to open an issue on the bug tracker.
> 
> Or can nothing be done about it?

mod2 could import the __main__ module, e. g. 

> # Code of mod2.py #
  import __main__ as mod1
> def getA():
>  return mod1.En.A
> # End of mod2.py #

but that would hardcode the assumption that __main__ is always mod1.py.

The only sane option is to not put anything in the main script that needs to 
be imported by other modules.

> Looking forward to any ideas
> Ralf M.
> 
> P.S.:
> As I was about to send this post the following modification occured to
> me (see below). It works, but it doesn't feel right to import a module
> directly from inside itself.
> # Modified code of mod1.py (one line added) #
> import enum, mod2
> class En(enum.Enum):
>  A = 1
>  B = 2
> from mod1 import En  # NEW LINE, overwrite just defined En
> def main():
>  a = mod2.getA()
>  print("a is En.A:", a is En.A)
>  print("a:", repr(a), "En.A:", repr(En.A))
>  print("id(a), id(a.__class__)", id(a), id(a.__class__))
>  print("id(En.A), id(En)  ", id(En.A), id(En))
> if __name__ == "__main__":
>  main()


-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Enums are Singletons - but not always?

2020-05-23 Thread Richard Damon
On 5/23/20 2:21 PM, Ralf M. wrote:
> Hello,
>
> recently I wrote a small library that uses an Enum. That worked as
> expected. Then I added a main() and if __name__ == "__main__" to make
> it runable as script. Now Enum members that should be the same aren't
> identical any more, there seem to be two instances of the same Enum.
>
> I think I know what's going on, but cannot find a good and elegant way
> to avoid the problem. I hope someone here can help me there.

I don;'t think Python anywhere defines that a enum will be a singleton,
and you should be checking for equality (==) not identity (is)

-- 
Richard Damon

-- 
https://mail.python.org/mailman/listinfo/python-list


Enums are Singletons - but not always?

2020-05-23 Thread Ralf M.

Hello,

recently I wrote a small library that uses an Enum. That worked as 
expected. Then I added a main() and if __name__ == "__main__" to make it 
runable as script. Now Enum members that should be the same aren't 
identical any more, there seem to be two instances of the same Enum.


I think I know what's going on, but cannot find a good and elegant way 
to avoid the problem. I hope someone here can help me there.


Below are a simplified code sample, the results when I run it and my 
thoughts.


# Code of mod1.py #
import enum, mod2
class En(enum.Enum):
A = 1
B = 2
def main():
a = mod2.getA()
print("a is En.A:", a is En.A)
print("a:", repr(a), "En.A:", repr(En.A))
print("id(a), id(a.__class__)", id(a), id(a.__class__))
print("id(En.A), id(En)  ", id(En.A), id(En))
if __name__ == "__main__":
main()
# End of mod1.py #

# Code of mod2.py #
import mod1
def getA():
return mod1.En.A
# End of mod2.py #

# Results when run: #
C:\tmp>py mod1.py
a is En.A: False
a:  En.A: 
id(a), id(a.__class__) 33305864 7182808
id(En.A), id(En)   33180552 7183752

C:\tmp>py
Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916 
64 bit

(AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import mod1
>>> mod1.main()
a is En.A: True
a:  En.A: 
id(a), id(a.__class__) 49566792 44574280
id(En.A), id(En)   49566792 44574280
>>>

So: When run as script there are two instances of En (different ids), 
but when mod1 is imported and mod1.main() is run it works as expected 
(just one instance of En, same ids).

BTW: py -m mod1 doesn't work either.

What I thing is happening:
When the script is run, mod1.py is executed and an instance of En and 
its members is created. During the same run mod1 is also imported (via 
mod2), the file mod1.py is executed again as part of the import and 
another, different instance of En and its members is created.


How do I have to change mod1.py to avoid the problem?
Currently I have moved main() into a new file script.py. That works, but 
is not what I wanted.


I doubt it's a bug in the enum module, but should that be the case, I'm 
willing to open an issue on the bug tracker.


Or can nothing be done about it?

Looking forward to any ideas
Ralf M.

P.S.:
As I was about to send this post the following modification occured to 
me (see below). It works, but it doesn't feel right to import a module 
directly from inside itself.

# Modified code of mod1.py (one line added) #
import enum, mod2
class En(enum.Enum):
A = 1
B = 2
from mod1 import En  # NEW LINE, overwrite just defined En
def main():
a = mod2.getA()
print("a is En.A:", a is En.A)
print("a:", repr(a), "En.A:", repr(En.A))
print("id(a), id(a.__class__)", id(a), id(a.__class__))
print("id(En.A), id(En)  ", id(En.A), id(En))
if __name__ == "__main__":
main()
--
https://mail.python.org/mailman/listinfo/python-list