Re: Question regarding unexpected behavior in using __enter__ method

2023-04-20 Thread Cameron Simpson

On 21Apr2023 00:44, Lorenzo Catoni  wrote:

I am writing to seek your assistance in understanding an unexpected
behavior that I encountered while using the __enter__ method. I have
provided a code snippet below to illustrate the problem:

```

class X:

... __enter__ = int
... __exit__ = lambda *_: None
...

with X() as x:

... pass
...

x

0
```
As you can see, the __enter__ method does not throw any exceptions and
returns the output of "int()" correctly. However, one would normally expect
the input parameter "self" to be passed to the function.


My descriptor fu is weak, but I believe this is because `int` is not a 
plain function but a type.


Consider this class definition:

class X:
   x = 1
   def y(self):
   return "y"

When you define a class, the body of the class is run in a namespace, 
and on completion, the namespace is _used_ to construct the class.  
During that process, the various names are considered. Here we've got 2 
names: "x" and "y".


"x" refers to an int and is just stored as a class attribute, unchanged.

"y" refers to a function, and is promoted to a descriptor of an unbound 
method.


So later: X.x return 1 but X.y returns a unbound method. If we make an 
instance:


objx = X()

then obj.x returns 1 (by not fining an "x" on "obj", but finding one on 
"type(obj)" i.e. the class attribute.


By contrast, obj.y returns a bound method, a function already curried 
with a leading parameter "obj" (which will be "self"). There's no "y" 
attribute directly on "obj" but there's an unbound method on 
"type(obj).y", which gets bound by saying "obj.y".


The means that what happens to a name when you define the class depends 
on the typeof the value bound to the name.


A plain function gets turned into an unbound instance method, but other 
things are left alone.


When you went:

__enter__ = int

That's not a plain function and so "obj.__enter__" doesn't turn into a 
bound method - it it just `int`.


Cheers,
Cameron Simpson 
--
https://mail.python.org/mailman/listinfo/python-list


Re: Question regarding unexpected behavior in using __enter__ method

2023-04-20 Thread dn via Python-list

On 21/04/2023 10.44, Lorenzo Catoni wrote:

I am writing to seek your assistance in understanding an unexpected
behavior that I encountered while using the __enter__ method. I have
provided a code snippet below to illustrate the problem:


It is expected behavior - just not what WE might have expected!



class X:

... __enter__ = int
... __exit__ = lambda *_: None
...

with X() as x:

... pass
...

x

0


Note that what is happening is the creation of an alias for the int 
built-in function.


The docs say:
«
class int(x=0)
class int(x, base=10)

Return an integer object constructed from a number or string x, or 
return 0 if no arguments are given. If x defines __int__(), int(x) 
returns x.__int__(). If x defines __index__(), it returns x.__index__(). 
If x defines __trunc__(), it returns x.__trunc__(). For floating point 
numbers, this truncates towards zero.

...
»

(https://docs.python.org/3/library/functions.html#int)

No argument is given. int() delivers as-promised. Hence, the x == 0 
result obtained.





As you can see, the __enter__ method does not throw any exceptions and
returns the output of "int()" correctly. However, one would normally expect
the input parameter "self" to be passed to the function.

On the other hand, when I implemented a custom function in place of the
__enter__ method, I encountered the following TypeError:

```

def myint(*a, **kw):

... return int(*a, **kw)
...

class X:

... __enter__ = myint
... __exit__ = lambda *_: None
...

with X() as x:

... pass
...
Traceback (most recent call last):
   File "", line 1, in 
   File "", line 2, in myint
TypeError: int() argument must be a string, a bytes-like object or a real
number, not 'X'
```
Here, the TypeError occurred because "self" was passed as an input
parameter to "myint". Can someone explain why this unexpected behavior
occurs only in the latter case?

I tested this issue on the following Python versions, and the problem
persists on all of them:
- Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] on linux
- Python 3.10.10 (main, Feb  8 2023, 14:50:01) [GCC 9.4.0] on linux
- Python 3.10.7 (tags/v3.10.7:6cc6b13, Sep  5 2022, 14:08:36) [MSC v.1933
64 bit (AMD64)] on win32

I appreciate any input or insights that you might have on this matter.



(you know this next part!)

However, if int() is fed an X-object, which in no way represents a 
numeric or numeric-able value, then it crashes. After the class 
definition, try:


int( X )

=> int 0


Right-y-ho: the evidence.

Firstly, what happens when int() is called with no argument?

print( "int", int(), )


Note that whereas int and myint are both called with no argument(s), and 
therefore int() defaults to 0, myint is attempting to use the arguments 
as part of its return-value - "pass-through".


As identified, the first argument (the a-tuple's zero-th element) is 
going to be x's self - which is NOT numeric, stored in a tuple - which 
is NOT numeric...



Thus, if we "shadow" the built-in int() with a user-function, we can see 
what is being passed-in


def int( *a, **kw, ):
print( locals(), )
print( "a", a, type( a ), id( a ), )
print( len( a ), a[ 0 ], type( a[ 0 ], ) )
print( "kw", kw, type( kw ), id( kw ), )
return 42

class X:
__enter__ = int
__exit__ = lambda *_: None

with X() as x:
pass

print( "After first CM", x, "\n\n")

del( int )


def myint(*a, **kw):
print( locals(), )
print( "a", a, type( a ), id( a ), )
print( len( a ), a[ 0 ], type( a[ 0 ], ) )
print( "kw", kw, type( kw ), id( kw ), )
return int(*a, **kw)

class Y:
__enter__ = myint
__exit__ = lambda *_: None


print( Y, type( Y ), id( Y ), )

with Y() as y:
print( y, type( y ), id( y ), )
pass

print( y )


=>
{'a': (<__main__.X object at 0x7f9b6bf13b90>,), 'kw': {}}
a (<__main__.X object at 0x7f9b6bf13b90>,)  140305733882528
1 <__main__.X object at 0x7f9b6bf13b90> 
kw {}  140305734120576
After first CM 42


  93904023389520
{'a': (<__main__.Y object at 0x7f9b6bf2c0d0>,), 'kw': {}}
a (<__main__.Y object at 0x7f9b6bf2c0d0>,)  140305507712640
1 <__main__.Y object at 0x7f9b6bf2c0d0> 
kw {}  140305507621376
Traceback (most recent call last):
  File "/home/dn/Projects/analyzer/lorenzo.py", line 53, in 
with Y() as y:
  File "/home/dn/Projects/analyzer/lorenzo.py", line 44, in myint
return int(*a, **kw)
   ^
TypeError: int() argument must be a string, a bytes-like object or a 
real number, not 'Y'



If you remover the del() and leave my play-version of int(), Python can 
make it work...


(the second half of the output)

  94557671306576
{'a': (<__main__.Y object at 0x7f950ee2c0d0>,), 'kw': {}}
a (<__main__.Y object at 0x7f950ee2c0d0>,)  140278176579200
1 <__main__.Y object at 0x7f950ee2c0d0> 
kw {}  140278176487936
{'a': (<__main__.Y object at 0x7f950ee2c0d0>,), 'kw': {}}
a (<__main__.Y object at 0x7f950ee2c0d0>,)  140278176579152
1 <__main__.Y object at 0x7f950ee2c0d0> 

Question regarding unexpected behavior in using __enter__ method

2023-04-20 Thread Lorenzo Catoni
Dear Python Mailing List members,

I am writing to seek your assistance in understanding an unexpected
behavior that I encountered while using the __enter__ method. I have
provided a code snippet below to illustrate the problem:

```
>>> class X:
... __enter__ = int
... __exit__ = lambda *_: None
...
>>> with X() as x:
... pass
...
>>> x
0
```
As you can see, the __enter__ method does not throw any exceptions and
returns the output of "int()" correctly. However, one would normally expect
the input parameter "self" to be passed to the function.

On the other hand, when I implemented a custom function in place of the
__enter__ method, I encountered the following TypeError:

```
>>> def myint(*a, **kw):
... return int(*a, **kw)
...
>>> class X:
... __enter__ = myint
... __exit__ = lambda *_: None
...
>>> with X() as x:
... pass
...
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 2, in myint
TypeError: int() argument must be a string, a bytes-like object or a real
number, not 'X'
```
Here, the TypeError occurred because "self" was passed as an input
parameter to "myint". Can someone explain why this unexpected behavior
occurs only in the latter case?

I tested this issue on the following Python versions, and the problem
persists on all of them:
- Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] on linux
- Python 3.10.10 (main, Feb  8 2023, 14:50:01) [GCC 9.4.0] on linux
- Python 3.10.7 (tags/v3.10.7:6cc6b13, Sep  5 2022, 14:08:36) [MSC v.1933
64 bit (AMD64)] on win32

I appreciate any input or insights that you might have on this matter.

Thank you for your help in advance!

Best regards,
Lorenzo Catoni
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: PyCharm's strict PEP and not so strict?

2023-04-20 Thread Mats Wichmann

On 4/19/23 17:19, dn via Python-list wrote:

The "light bulb" has little to do with "quotes"! This is one of the 
advantages of utilising a Python-native IDE (cf a more general-purpose 
alternative, perhaps with some Python add-on). PyCharm attempts to 
understand the code it is editing, and apply various lessons or 
experiences to offer intelligent assistance.


To nitpick a little bit, PyCharm isn't *exactly* Python-native, it's a 
derivative of the IntelliJ generic IDE platform (written in, sigh, Java) 
with a *very* evolved Python add-on and packaged in such a way as to 
make it look as if it weren't an addon at all (but if you lift the 
covers up you can see the roots)



Builtin bits are very configurable, usually, but I haven't found that 
the Python code inspection (this is the stuff that by default gives you 
the PEP8-type warnings) is particularly so - be happy to stand corrected 
on that. However, you can configure an external tool - say, pylint - and 
if you do, you configure that in the normal way (a .pylintrc rcfile, or 
a toml config file). If you do this, you can turn off those style checks 
from the builtin checker.   If you want.


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


Re: PyCharm's strict PEP and not so strict?

2023-04-20 Thread Thomas Passin

On 4/19/2023 7:19 PM, dn via Python-list wrote:



*PyCharm enables a range of Python-linters. Some by add-in. Some by
"External tools"*

I was not 'up' on "linters", Thanks for the heads up! Attempted 
finding a "linter" on jetbrains... I take it that finding a 'linter' 
for Build #PC-222.4554.11, built on March 15, 2023

Is a matter of hit and miss... Any suggestions?


Try a web-search for "Python linters". Linters, I've know a few... (hum 
along with the music...) have long been a part of the Python eco-system. 
They have usually been designed to run from Python or 'the 
command-line'. Accordingly, many can be added into PyCharm from 'the 
outside'.


The original purpose was possibly to guide/nudge coders into the 
practices recommended by PEP-008 (https://peps.python.org/pep-0008/). 
This, as you will read, is not about syntax, but more about coding "style".


There are many to choose from. Some are more strict than others. 
Recommendation: (for the good of your blood-pressure) stay away from 
"highly opinionated" alternatives, such as Black. Try the ones which 
offer options to turn-on/-off particular situations (as described earlier).


Be aware that most of these tools can be configured to be more or less 
fussy, but it's not going to be so easy for the uninitiated. 
Unconfigured, you will likely see a stream of messages that aren't 
helpful but may seem overwhelming. Once you have finally gotten one 
configured to your satisfaction, you probably will not need to change 
the configuration again for a long time.


You can generally disable specific warnings at a point in the code with 
special comments.  Using pylint, for example, perhaps appropriate in 
view of the OP's original post:


# pylint: disable = consider-using-f-string

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


Re: Pycharm IDE

2023-04-20 Thread Thomas Passin

On 4/19/2023 7:48 PM, dn via Python-list wrote:

On 20/04/2023 08.59, Thomas Passin wrote:

On 4/19/2023 4:06 PM, Mark Bourne wrote:


print(f'{LIMIT})


^ I think this one should be:

print(f'{LIMIT}')

with the closing quote ;o)


Yup a typo!  Where's pylint when I need it?


but (and you designed it this way - right?) an excellent object-lesson 
for the OP


AND

great rationale for why linters are so handy!


The Leo editor, which I use, checks the file you are working on for 
syntax errors and also for undefined variables, missing imports, and the 
like, when you save it.  It gives you a clickable link to the line in 
question.  This is a good compromise because it stays out of your way 
until you take a break by saving.


I am bullish on F-strings, but they can stretch the typing fingers and 
strain the eyes. Remember the days when pythonista used to make 
deprecating remarks about the superiority of Python's syntax because we 
didn't have 'all those braces' (and other punctuation-characters) 
cluttering-up the code???


When you get right down for it, there isn't much difference between

'Here is the result: %s, %s' %(a.b, c)

and

'Here is the result: {}, {}'.format(a.b, c)

Readability and typeability aren't much different.  It's only when we 
get to f-strings that there's a real change, and the second form started 
to prepare us for it (yes, I know that the .format() form lets you use 
your own formatter - probably a real advantage though I never had 
occasion to use it)


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