Re: [Python-ideas] PEP 8 update on line length
On Fri, Feb 22, 2019 at 5:10 PM Greg Ewing wrote: > I would say it the other way around. Once you've reduced the complexity > of a line to something a human can handle, *most* of the time 80 chars > is enough. > +1 It has been known for a very long time. These are *old *books that talk about *refactoring* (the word wasn't used then) of complex code (assigning subexpressions to variables, or extracting code into functions, reverting conditionals, etc.), not for line length, but for understandability, yet resulting nevertheless in much shorter lines: - Software Tools (also Software Tools in Pascal), Kernighan and Plauger, 1976 https://smile.amazon.com/Software-Tools-Brian-W-Kernighan/dp/020103669X - Code Complete (also the second edition), Steve Mc Connell, 1993, https://smile.amazon.com/Software-Tools-Brian-W-Kernighan/dp/020103669X I do make an exception in that after taking 8 spaces of indentation to write the implementation of a method in Python, the sweet spot is more around 100 chars. -- Juancarlo *Añez* ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Type hints for functions with side-effects and for functions raising exceptions
On Sat, Feb 23, 2019 at 9:14 AM Kyle Lahnakoski wrote: > Let me call this pattern the Catch-It-Name-It-Chain-It-Raise-It (CNCR) > pattern > > There are a few reasons for this. > > 1. I can add runtime values to the exception so I get a better sense of > the program state without going to the debugger: `error("some > description", {"url": url}, cause=e)` > 2. I prevent exception leakage; I have no idea the diversity of > exceptions my code can raise, so I CNCR. > 3. Every exception is it's own unique type; I can switch on the message > if I want (I rarely do this, but it happens, see below) > > Can Python provide better support for the CNCR pattern? If it is > lightweight enough, maybe people will use it, and then we can say > something useful about the (restricted) range of exceptions coming from > a method: The CNCR pattern, if used repeatedly, will quickly create a long chain of exceptions, where each exception represents one function call. Python already has very good support for seeing the function call history that led to the exception - it's called a traceback. You even get the full function locals as part of the exception object... and it requires no code whatsoever! Simply allowing exceptions to bubble up will have practically the same benefit. ChrisA ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Type hints for functions with side-effects and for functions raising exceptions
On 2019-02-21 03:09, Christopher Barker wrote: > > But yes, there is no (easy) way to distinguish an Exception raised by the function you called, and one raised somewhere deeper that. > > And I have been bitten by that more than once. It makes "Easier to ask forgiveness than permission" kind of tricky. > And Exception handling is messy -- the point made by the OP, I'm not sure there's a better way to do it. It seems to me that exception *classes* are hardly ever required. If two methods raise the same exception type, then I contend that they are actually throwing two different types of errors (as evidenced by the difference in the message). Instead, I suggest every method emits its own exception type. By demanding support for a plethora of exception types, at least as many as there are methods, then we may have a different way of looking at exceptions: The first conclusion is, it is too expensive to make classes for every exception type, rather, exception type should be defined by the message. Exception handling is messy. I find most of my Python methods look like: | def my_method(): | try: | # do something | except Exception as e: | error("some description", cause=e) I am not using Python3 everywhere yet, so maybe it should read like: | def my_method(): | try: | # do something | except Exception as e: | raise Exception("some description") from e Let me call this pattern the Catch-It-Name-It-Chain-It-Raise-It (CNCR) pattern There are a few reasons for this. 1. I can add runtime values to the exception so I get a better sense of the program state without going to the debugger: `error("some description", {"url": url}, cause=e)` 2. I prevent exception leakage; I have no idea the diversity of exceptions my code can raise, so I CNCR. 3. Every exception is it's own unique type; I can switch on the message if I want (I rarely do this, but it happens, see below) Can Python provide better support for the CNCR pattern? If it is lightweight enough, maybe people will use it, and then we can say something useful about the (restricted) range of exceptions coming from a method: A context manager, a `with` block, could do this: | def my_method(): | with Explanation("some description"): | # do something I propose a simple line, which effectively defines a try block to the end of the current code block. Call it the `on-raises` syntax: | def my_method(): | on Exception raises "some description" | # do something It is better than the `with` block because: * it does not put code in the happy path * it has less indentation * we can conclude "some description" is the only exception raised by this method The `on-raises` need not start at the beginning of a code block, but then we can say less about what exceptions come out of `my_method`: | def my_method(): | # no exception checks here | on Exception raises "some description" | # do something Since `on-raises` can be used in any code block, we can save some indentation. Instead of | def my_method(): | with some_file: | try: | # do something | except Exception as e: | raise Exception("some description") from e we have | def my_method(): | with some_file: | on Exception raises "some description" | # do something of course we can have nested `on-raises`, | def my_method(): | on Exception raises "bigger problem" | with some_file: | on Exception raises "some description" | # do something Plus we know only "bigger problem" exceptions can be raised. The above is the same as: | def my_method(): | try: | with some_file: | try: | # do something | except Exception as e: | raise Exception("some description") from e | except Exception as e: | raise Exception("bigger problem") from e in the rare case we actually want to deal with an exception, we revert back to trusty old try/except: | def my_other_method(): | on Exception raises "some other description" | try: | my_method() | except "some description" as e: | # I know how to handle this case | return which is the same as: | def my_other_method(): | try: | my_method() | except Exception as e: | if "some description" in e: | # I know how to handle this case | return | error("some other description", cause=e) ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 8 update on line length
Christopher Barker wrote: (And did they ever stop to wonder why those old terminals standardized on 80 columns?) Probably because IBM decided on 80 columns for their punched cards. And that probably didn't have anything to do with a readable width for text. Nobody used computers for word processing back then. In fact punched cards predate computers altogether, originally being developed for electromechanical accounting and record keeping machines. I’m pretty sure the old tractor feed paper terminal I first learned to code on was a lot more than 80 char wide. Line printers were traditionally 120 chars wide, but again that had nothing to do with text. It was so that accountants could print reports with a ridiculously large number of columns of figures. How many of us still type on QWERTY keyboards? Even on a phone, like I am now. Once you're used to qwerty, anything else is a nuisance, even if you're not touch typing on it. A place I park at recently installed machines you enter your car's license number into. The keyboard has the letters in alphabetical order, presumably because someone thought it would be "easier". But nowadays I suspect it just makes it harder for most people! “Typesetters hundreds of years ago used less than 80 chars per line, so that’s what we should do for Python code now” is a pretty weak argument. But that's not the entire argument -- the point it is that typesetters had the goal of making lines of text readable, which is similar (if not quite the same) as the goal of making lines of program code readable. It's a lot closer than, for example, the goal of fitting in an accountant's spreadsheet. Agreed — you can have too much complexity in 80 char. Which is why I think the 80 char limit is about what fits on a screen, not complexity. I would say it the other way around. Once you've reduced the complexity of a line to something a human can handle, *most* of the time 80 chars is enough. There will be exceptions, but as long as the exceptions are rare, making your editing window any wider will mostly just waste screen space. -- Greg ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Type hints for functions with side-effects and for functions raising exceptions
On Thu, Feb 21, 2019 at 11:32 PM Chris Angelico wrote: > On Fri, Feb 22, 2019 at 2:27 PM Juancarlo Añez wrote: > > Then, if exceptions are going to be part of a type, there should be a > way to express the semantics of them (like in Eiffel), so > stack.pop();stack.push(x) doesn't have to catch StackFullException. > > > > That assumes atomicity. If you want an atomic "replace top of stack" > that can never raise StackFullException, it's probably best to express > it as stack.replacetop(x) rather than having something that might be > interrupted. > Ah! What to do with an exception in a concurrent context? Abort, or retry after a while? > People do dumb things with exceptions, yes. Why does this mean that > they are bad? I don't understand this. Exception handling (and stack > unwinding) gives an easy and clear way to refactor code without having > to daisychain error handling everywhere. How is throwing that away > going to help people write better code? > For programs that are recursive or multi-layered, exceptions are a clean and efficient way to unwind. This PEG parser generator I wrote was made possible because Python exceptions are semantically clean and very efficient: https://github.com/neogeny/TatSu/blob/master/tatsu/contexts.py > But then, Golang also decided that Unicode wasn't necessary, and we > should all deal with UTF-8 encoded byte sequences instead of text > strings, so I'm fairly sure there are no ten foot barge poles long > enough for me to touch it with. There are languages that have problems > because of history (*cough*JavaScript*cough*), but for a new language > to make multiple poor decisions just means it's one to avoid. > The quest for a programming language in which _"anyone"_ can program, _without_ making mistakes, has never ended, and probably never will. "Alexa! Please write the software for a new Internet email system that overcomes the limitations of the current one!" Sometimes staying away is not an option. -- Juancarlo *Añez* ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] Type hints for functions with side-effects and for functions raising exceptions
On Sat, Feb 23, 2019 at 5:32 AM Juancarlo Añez wrote: > On Thu, Feb 21, 2019 at 11:32 PM Chris Angelico wrote: >> >> On Fri, Feb 22, 2019 at 2:27 PM Juancarlo Añez wrote: >> > Then, if exceptions are going to be part of a type, there should be a way >> > to express the semantics of them (like in Eiffel), so >> > stack.pop();stack.push(x) doesn't have to catch StackFullException. >> > >> >> That assumes atomicity. If you want an atomic "replace top of stack" >> that can never raise StackFullException, it's probably best to express >> it as stack.replacetop(x) rather than having something that might be >> interrupted. > > Ah! What to do with an exception in a concurrent context? Abort, or retry > after a while? You mean in a situation where another thread might be pushing/popping on the same stack? That's up to you. Exception handling works exactly the same way with multiple threads as it does with a single thread; each context has a call stack. Things are a bit more complicated with other forms of concurrency, but at the point where there is an execution context again, that's where exceptions can start bubbling again. That's how generators work, for instance. ChrisA ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 8 update on line length
Raymond Hettinger posted a helpful example to this list. Here, I run his example, and a variant, through https://black.now.sh Raymond class Frabawidget: @wozzle.setter def wibble(self, woozle): if not (self.min_woozle < woozle < self.max_woozle): raise ValueError(f"Expected woozle to be between {self.min_woozle} and {self.max_woozle}") self._wozzle = normalize(woozle) black.py class Frabawidget: @wozzle.setter def wibble(self, woozle): if not (self.min_woozle < woozle < self.max_woozle): raise ValueError( f"Expected woozle to be between {self.min_woozle} and {self.max_woozle}" ) self._wozzle = normalize(woozle) Raymond variant class Frabawidget: @wozzle.setter def wibble(self, woozle): if not (self.min_woozle < woozle < self.max_woozle < self.super_max_value) and do_this_or_that_(): raise ValueError(f"Expected woozle to be between {self.min_woozle} and {self.max_woozle}") self._wozzle = normalize(woozle) black.py class Frabawidget: @wozzle.setter def wibble(self, woozle): if ( not ( self.min_woozle < woozle < self.max_woozle < self.super_max_value ) and do_this_or_that_() ): raise ValueError( f"Expected woozle to be between {self.min_woozle} and {self.max_woozle}" ) self._wozzle = normalize(woozle) (By the way, I had to add the 'wibble' to the code above, to avoid a syntax error.) -- Jonathan ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 8 update on line length
On 2/21/19 10:33 PM, Steven D'Aprano wrote: (And did they ever stop to wonder why those old terminals standardized on 80 columns?) Punch cards? https://softwareengineering.stackexchange.com/questions/148677 There's an ensuing discussion regarding the history of the size/shape of the punch card, too. Dan ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 8 update on line length
> > > Chris, the convention to limit text to somewhere around 60-80 characters > predates old terminals by *literally centuries*. Not for code, it doesn’t— and centuries ago, there were other technical issues on play for papermaking, hand writing, and typesetting. > (And did they ever stop to wonder why those old terminals > standardized on 80 columns?) CRTs were small and low resolution? A 200 char line would have been very expensive back in the day, and I’m pretty sure the old tractor feed paper terminal I first learned to code on was a lot more than 80 char wide. But now that you mention it, I suspect the 80 char punch cards had an influence. How many of us still type on QWERTY keyboards? Even on a phone, like I am now. The point is that we conform to s lot of conventions without re-adding whether the original reasons for those conventions apply. And no one on this thread has cited a study about optimum line length for readability of code. I have no idea if that’s ever been done — but that is the question at hand. “Typesetters hundreds of years ago used less than 80 chars per line, so that’s what we should do for Python code now” is a pretty weak argument. > Go back and read the thread. I have read the thread. And Raymond made a pretty good argument for why, in some cases, a shorter line limit is a problem— and I have had much the same experience. and the nuisance value of splitting a conceptual > line of code over multiple physical lines. I think one key question is whether it’s a nuisance, or actually makes some code harder to read. > > 1. 79 characters is *very generous* for most lines of code; I’ve found a common exception for this is string literals and trailing comments. And I often prefer 80+ char trailing comments to an extra comment line. 2. When a single line goes beyond 80 columns, it often wants to go a > long way beyond. Often True, but I’ve found setting my linter to 95 char makes a big difference in my code > Opinion: we really shouldn't be encouraging people to write long complex > lines of code. Agreed — but as you point out, those are often MUCH longer, so pushing the line limit to < 100 chars doesn’t really change that. 4. But one notable exception to this is the case where you have a long > format string, often passed to "raise Exception", ... > These are often indented four or five levels deep, and they really are a > pain-point. Oh yeah, that was Raymond’s point, and I concurred and emphasized it. > > Opinion: common sense should prevail here. If you have a line "raise > ValueError(...)" which would reach 80 or even 90 characters, don't let > PEP 8 alone tell you otherwise. Yup - the trick here is that it’s hard to define clearly in a style guide, and even harder in a linter. One issue with all this is that there’s been a move toward automated enforcement (or checking) of style. Which I think is an overall good thing, but then it’s hard to allow judgement as well. But if you have a more substantial code that exceeds 80 columns, that's > a code smell and you ought to think long and hard before breaking it. My reading of your points above is that the really smelly code is well over 80 chars, so having a slightly larger limit would work fine as well. Proposal: > > - keep PEP 8's current recommendation; As long as that includes the “ you can raise it to up to 100 char, sure :-) > > - but remind people that the rule can be relaxed for lines that are > conceptually simple, such as the "raise Exception(...)" pattern; It would be nice, if possible, to clearly define this relaxation. - and also remind people that long *complex* lines are an anti-pattern. > Such complex lines can be improved by splitting them over multiple > lines, and should be. Not sure that’s needed, but why not? > > That effectively says "any amount of complexity is OK in a single line, > so long as it remains below X columns". I'd rather people look at the > line and decide "this is too complex, split it" or "it's just a format > string (or whatever), let it be". Agreed — you can have too much complexity in 80 char. Which is why I think the 80 char limit is about what fits on a screen, not complexity. -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Re: [Python-ideas] PEP 8 update on line length
A general rule of thumb is, if Python feels inconvenient or awkward, you’re doing something wrong. ___ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/