Re: A technique from a chatbot

2024-04-04 Thread Thomas Passin via Python-list

On 4/4/2024 3:03 PM, Mark Bourne via Python-list wrote:

Thomas Passin wrote:

On 4/2/2024 1:47 PM, Piergiorgio Sartor via Python-list wrote:

On 02/04/2024 19.18, Stefan Ram wrote:

   Some people can't believe it when I say that chatbots improve
   my programming productivity. So, here's a technique I learned
   from a chatbot!
   It is a structured "break". "Break" still is a kind of jump,
   you know?
   So, what's a function to return the first word beginning with
   an "e" in a given list, like for example
[ 'delta', 'epsilon', 'zeta', 'eta', 'theta' ]

   ? Well it's
def first_word_beginning_with_e( list_ ):
 for word in list_:
 if word[ 0 ]== 'e': return word

   . "return" still can be considered a kind of "goto" statement.
   It can lead to errors:

def first_word_beginning_with_e( list_ ):
 for word in list_:
 if word[ 0 ]== 'e': return word
 something_to_be_done_at_the_end_of_this_function()
   The call sometimes will not be executed here!
   So, "return" is similar to "break" in that regard.
   But in Python we can write:
def first_word_beginning_with_e( list_ ):
 return next( ( word for word in list_ if word[ 0 ]== 'e' ), None )


Doesn't look a smart advice.


   . No jumps anymore, yet the loop is aborted on the first hit


It's worse than "not a smart advice". This code constructs an 
unnecessary tuple, then picks out its first element and returns that.


I don't think there's a tuple being created.  If you mean:
     ( word for word in list_ if word[ 0 ]== 'e' )

...that's not creating a tuple.  It's a generator expression, which 
generates the next value each time it's called for.  If you only ever 
ask for the first item, it only generates that one.


Yes, I was careless when I wrote that. Still, the tuple machinery has to 
be created and that's not necessary here. My point was that you are 
asking the Python machinery to do extra work for no benefit in 
performance or readability.


When I first came across them, I did find it a bit odd that generator 
expressions look like the tuple equivalent of list/dictionary 
comprehensions.


FWIW, if you actually wanted a tuple from that expression, you'd need to 
pass the generator to tuple's constructor:

     tuple(word for word in list_ if word[0] == 'e')
(You don't need to include an extra set of brackets when passing a 
generator a the only argument to a function).




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


RE: A technique from a chatbot

2024-04-04 Thread AVI GROSS via Python-list
That is an excellent point, Mark. Some of the proposed variants to the 
requested problem, including mine, do indeed find all instances only to return 
the first. This can use additional time and space but when done, some of the 
overhead is also gone. What I mean is that a generator you create and invoke 
once, generally sits around indefinitely in your session unless it leaves your 
current range or something. It does only a part of the work and must remain 
suspended and ready to be called again to do more.

If you create a generator inside a function and the function returns, 
presumably it can be garbage-collected.

But if it is in the main body, I have to wonder what happen.

There seem to be several related scenarios to consider.

- You may want to find, in our example, a first instance. Right afterwards, you 
want the generator to disassemble anything in use.
- You may want the generator to stick around and later be able to return the 
next instance. The generator can only really go away when another call has been 
made after the last available instance and it cannot look for more beyond some 
end.
- Finally, you can call a generator with the goal of getting all instances such 
as by asking it to populate a list. In such a case, you may not necessarily 
want or need to use a generator expression and can use something 
straightforward and possible cheaper.

What confuses the issue, for me, is that you can make fairly complex 
calculations in python using various forms of generators that implement a sort 
of just-in-time approach as generators call other generators which call yet 
others and so on. Imagine having folders full of files that each contain a data 
structure such as a dictionary or set and writing functionality that searches 
for the first match for a key in any of the dictionaries (or sets or whatever) 
along the way? Now imagine that dictionary items can be a key value pair that 
can include the value being a deeper dictionary, perhaps down multiple levels.

You could get one generator that generates folder names or opens them and 
another that generates file names and reads in the data structure such as a 
dictionary and yet another that searches each dictionary and also any 
internally embedded dictionaries by calling another instance of the same 
generator as much as needed.

You can see how this creates and often consumes generators along the way as 
needed and in a sense does the minimum amount of work needed to find a first 
instance. But what might it leave open and taking up resources if not finished 
in a way that dismantles it?

Perhaps worse, imagine doing the search in parallel and as sone as it is found 
anywhere, ...



-Original Message-
From: Python-list  On 
Behalf Of Mark Bourne via Python-list
Sent: Thursday, April 4, 2024 3:04 PM
To: python-list@python.org
Subject: Re: A technique from a chatbot

Thomas Passin wrote:
> On 4/2/2024 1:47 PM, Piergiorgio Sartor via Python-list wrote:
>> On 02/04/2024 19.18, Stefan Ram wrote:
>>>Some people can't believe it when I say that chatbots improve
>>>my programming productivity. So, here's a technique I learned
>>>from a chatbot!
>>>It is a structured "break". "Break" still is a kind of jump,
>>>you know?
>>>So, what's a function to return the first word beginning with
>>>an "e" in a given list, like for example
>>> [ 'delta', 'epsilon', 'zeta', 'eta', 'theta' ]
>>>
>>>? Well it's
>>> def first_word_beginning_with_e( list_ ):
>>>  for word in list_:
>>>  if word[ 0 ]== 'e': return word
>>>
>>>. "return" still can be considered a kind of "goto" statement.
>>>It can lead to errors:
>>>
>>> def first_word_beginning_with_e( list_ ):
>>>  for word in list_:
>>>  if word[ 0 ]== 'e': return word
>>>  something_to_be_done_at_the_end_of_this_function()
>>>The call sometimes will not be executed here!
>>>So, "return" is similar to "break" in that regard.
>>>But in Python we can write:
>>> def first_word_beginning_with_e( list_ ):
>>>  return next( ( word for word in list_ if word[ 0 ]== 'e' ), None )
>>
>> Doesn't look a smart advice.
>>
>>>. No jumps anymore, yet the loop is aborted on the first hit
> 
> It's worse than "not a smart advice". This code constructs an 
> unnecessary tuple, then picks out its first element and returns that.

I don't think there's a tuple being created.  If you mean:
 ( word for word in list_ if word[ 0 ]== 'e' )

...that's not creating a tuple.  It's a generator expression, which 
generates the next value each time it's called for.  If you only ever 
ask for the first item, it only generates that one.

When I first came across them, I did find it a bit odd that generator 
expressions look like the tuple equivalent of list/dictionary 
comprehensions.

FWIW, if you actually wanted a tuple from that expression, you'd need to 
pass the generator to tuple's constructor:
 tuple(word for word in list_ if word[0] 

Re: A technique from a chatbot

2024-04-04 Thread Mark Bourne via Python-list

Thomas Passin wrote:

On 4/2/2024 1:47 PM, Piergiorgio Sartor via Python-list wrote:

On 02/04/2024 19.18, Stefan Ram wrote:

   Some people can't believe it when I say that chatbots improve
   my programming productivity. So, here's a technique I learned
   from a chatbot!
   It is a structured "break". "Break" still is a kind of jump,
   you know?
   So, what's a function to return the first word beginning with
   an "e" in a given list, like for example
[ 'delta', 'epsilon', 'zeta', 'eta', 'theta' ]

   ? Well it's
def first_word_beginning_with_e( list_ ):
 for word in list_:
 if word[ 0 ]== 'e': return word

   . "return" still can be considered a kind of "goto" statement.
   It can lead to errors:

def first_word_beginning_with_e( list_ ):
 for word in list_:
 if word[ 0 ]== 'e': return word
 something_to_be_done_at_the_end_of_this_function()
   The call sometimes will not be executed here!
   So, "return" is similar to "break" in that regard.
   But in Python we can write:
def first_word_beginning_with_e( list_ ):
 return next( ( word for word in list_ if word[ 0 ]== 'e' ), None )


Doesn't look a smart advice.


   . No jumps anymore, yet the loop is aborted on the first hit


It's worse than "not a smart advice". This code constructs an 
unnecessary tuple, then picks out its first element and returns that.


I don't think there's a tuple being created.  If you mean:
( word for word in list_ if word[ 0 ]== 'e' )

...that's not creating a tuple.  It's a generator expression, which 
generates the next value each time it's called for.  If you only ever 
ask for the first item, it only generates that one.


When I first came across them, I did find it a bit odd that generator 
expressions look like the tuple equivalent of list/dictionary 
comprehensions.


FWIW, if you actually wanted a tuple from that expression, you'd need to 
pass the generator to tuple's constructor:

tuple(word for word in list_ if word[0] == 'e')
(You don't need to include an extra set of brackets when passing a 
generator a the only argument to a function).


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


How to get insight in the relations between tracebacks of exceptions in an exception-chain

2024-04-04 Thread Klaas van Schelven via Python-list
Hi,

This question is best introduced example-first:

Consider the following trivial program:

```
class OriginalException(Exception):
pass


class AnotherException(Exception):
pass


def raise_another_exception():
raise AnotherException()


def show_something():
try:
raise OriginalException()
except OriginalException:
raise_another_exception()


show_something()
```

running this will dump the following on screen (minus annotations on the
Right-Hand-Side):

```
Traceback (most recent call last):
  File "./stackoverflow_single_complication.py", line 15, in
show_something t1
raise OriginalException()
__main__.OriginalException

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./stackoverflow_single_complication.py", line 20, in 
t0
show_something()
  File "./stackoverflow_single_complication.py", line 17, in
show_something t2
raise_another_exception()
  File "./stackoverflow_single_complication.py", line 10, in
raise_another_exceptiont3
raise AnotherException()
__main__.AnotherException
```

What we see here is first the `OriginalException` with the stackframes
between the moment that it was raised and the moment it was handled.
Then we see `AnotherException`, with _all_ a complete traceback from its
point-of-raising to the start of the program.

In itself this is perfectly fine, but a consequence of this way of
presenting the information is that the stackframes are _not_ laid out on
the screen in the order in which they were called (and not in the reverse
order either), as per the annotations _t1_, _t0_, _t2_, _t3_. The path
leading up to _t1_ is of course the same as the path leading up to _t2_,
and the creators of Python have chosen to present it only once, in the
latter case, presumably because that Exception is usually the most
interesting one, and because it allows one to read the bottom exception
bottom-up without loss of information. However, it does leave people that
want to analyze the `OriginalException` somewhat mystified: what led up to
it?

A programmer that wants to understand what led up to _t1_ would need to
[mentally] copy all the frames above the point _t2_ to the first stacktrace
to get a complete view. However, in reality the point _t2_ is, AFAIK, not
automatically annotated for you as a special frame, which makes the task of
mentally copying the stacktrace much harder.

Since the point _t2_ is in general "the failing line in the `except`
block", by cross-referencing the source-code this excercise can usually be
completed, but this seems unnecessarily hard.

**Is it possible to automatically pinpoint _t2_ as the "handling frame"?**

(The example above is given without some outer exception-handling context;
I'm perfectly fine with answers that introduce it and then use `traceback`
or other tools to arrive at the correct answer).

This is the most trivial case that illustrates the problem; real cases have
many more stack frames and thus less clearly illustrate the problem but
more clearly illustrate the need for (potentially automated) clarification
of what's happening that this SO question is about.


regards,
Klaas


Previously asked here:
https://stackoverflow.com/questions/78270044/how-to-get-insight-in-the-relations-between-tracebacks-of-exceptions-in-an-excep
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Trying to use pyinstaller under python 3.11, and, recently started receiving error message about specific module/distribution

2024-04-04 Thread Jacob Kruger via Python-list
Ok, had received response on pyinstaller mailing list, but, also just 
related to trying clean uninstall/reinstall of modules, but, while 
checking that out, something else occurred to me, and, it now operates 
as it should.



Anyway, what seemed to be causing issue was actually that, since, while 
am working under windows 11, quite often you might need to work with 
case-sensitivity in file names, not by choice, but, since a lot of 
target platforms, like linux, etc. are case-sensitive, and, at times, 
when working with external modules, this might cause hassles, etc.



In other words, the folder/directory where all my python source code is 
stored is set to be case-sensitive - there are a couple of ways to 
implement this under windows 10 and windows 11, via some external 
utilities, or by running the following command from a 
terminal/power-shell window, running it as administrator:


fsutil.exe file SetCaseSensitiveInfo C:\folder\path enable


If you instead use disable argument at the end, it then disables 
case-sensitivity, and, what did now was, under current project/test 
code, I created an additional sub-folder, copied code files, etc. over 
into it, disabled case-sensitivity on it, recreated the virtual 
environment, and installed all required modules, including pyinstaller 
using pip, and, when I then run pyinstaller from there, it works fine, 
and, does what I want it to.



In other words, something to do with having case-sensitivity enabled 
recursively on the folder/directory containing the code and the virtual 
environment seemed to be causing these errors/issues, specific to 
altgraph, which doesn't really make sense to me, but, it's now working, 
so, will archive this to memory, for later reference.



Jacob Kruger
+2782 413 4791
"Resistance is futile!...Acceptance is versatile..."


On 2024/04/02 17:11, Barry wrote:



On 1 Apr 2024, at 15:52, Jacob Kruger via Python-list  
wrote:

Found many, many mentions of errors, with some of the same keywords, but, no 
resolutions that match my exact issue at all.

Try asking the pyinstaller developers. I think there is a mailing list.

Barry


As in, most of them are mentioning older versions of python, and, mainly 
different platforms - mac and linux, but, various google searches have not 
mentioned much of using it on windows, and having it just stop working.


Now did even try shifting over to python 3.12, but, still no-go.


If launch pyinstaller under python 3.10 on this exact same machine, pyinstaller 
runs - just keep that older version hovering around for a couple of occasional 
tests, partly since some of my target environments are still running older 
versions of python, but anyway.


Also, not really relevant, but, cx_freeze is perfectly able to generate 
executables for this same code, but, then not combining all output into a 
single file - will stick to that for now, but, not always as convenient, and, 
still wondering what changed here.


Jacob Kruger
+2782 413 4791
"Resistance is futile!...Acceptance is versatile..."



On 2024/03/31 14:51, Barry wrote:


On 31 Mar 2024, at 13:24, Jacob Kruger via Python-list  
wrote:

pkg_resources.DistributionNotFound: The 'altgraph' distribution was not found 
and is required by the application

I think I have seen this error being discussed before…

A web search for pyinstaller and that error leads to people discussing why it 
happens it looks like.

Barry



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

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