Re: A technique from a chatbot
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
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
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
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
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