tl;dr: I show how the goal of Soni L's exception spaces can be addressed
today, via less intrusive means. (Assuming I understand their proposal,
that is.)
On 4/11/2020 7:49 AM, Soni L. wrote:
On 2020-04-11 8:38 a.m., Chris Angelico wrote:
On Sat, Apr 11, 2020 at 9:30 PM Soni L. <fakedme...@gmail.com> wrote:
>
> alternatively, they can let their caller
> handle it by having the espace come from the caller.
>
> def bar(espace=None):
> x = foo(espace=espace)
>
> and then the caller is expected to handle it, *if they so choose*.
If a function just replicates down the espace parameter, how is that
even slightly different from the current situation? You STILL won't be
able to know where the exception came from. I am at a complete loss to
understand how all of this overhead (and there is a LOT of overhead)
even solves the problem.
And if a function doesn't replicate it down, what exception space
should it be using? How would it know?
That just means you completely misunderstood the proposal. None would
still work (and be the default). raising things in None would be the
same as using python today.
the special things happen when you don't raise in None. yeah sure your
function has an espace but if you don't use it... the caller won't
catch those errors.
def bar(espace=None):
x = foo(espace=espace)
y = x['bug']
if called with an espace other than None, the explicitly declared
errors from foo will be raised in the given espace. but the indexing
in x['bug']? that'll just give you a KeyError in None, indicating
there's a bug in bar.
In your proposal, you're requiring all library code that wants to use
exception spaces to be modified to accept your exception space
parameter, and then to change where they raise exception space
exceptions. Is that correct? I'm assuming so for this discussion.
Further, client code that wants to participate in these exception spaces
also need to change how they call the library code and how they catch
exceptions.
As others have suggested, this can be achieved today with less
disruption just by subclassing exceptions in the library. Consider a
library that exports a single function that might raise a KeyError, and
that's defined as part of its interface. In my example below, it will
always raise that error. Rather than change the function to take a new
parameter and change how it raises exceptions, it could work as follows:
#####################
# library code
class LibExceptionBase(Exception): pass
class LibKeyError(LibExceptionBase, KeyError): pass
# Other library API exceptions.
class LibValueError(LibExceptionBase, ValueError): pass
def lib_fn(a):
raise LibKeyError(f'{a!r} not found')
#####################
# User code
mymap = {'x': 4}
# Case 0: I have a "local" problem in calling the library, which raises
# KeyError. This would be similar to your code which passes in an espace.
try:
lib_fn(mymap['bad key'])
except LibExceptionBase as ex:
print('0 library exception', ex)
except KeyError as ex:
print('0 normal exception', ex)
# Case 1: I call the library, which raises LibKeyError. This would be
similar
# to your code which passes in an espace.
try:
lib_fn(mymap['x'])
except LibExceptionBase as ex:
print('1 library exception', ex)
except KeyError as ex:
print('1 normal exception', ex)
# Case 2: Existing code that doesn't know about exception spaces, and has a
# "local" problem that raises KeyError. This would be code that passes in
# espace=None (via the default) in your examples.
try:
lib_fn(mymap['bad key'])
except KeyError as ex:
print('2 Some KeyError', ex)
# Case 3: Existing code that doesn't know about exception spaces, and the
# library raises its a KeyErorr. This would be code that passes in
espace=None
# (via the default) in your examples.
try:
lib_fn(4)
except KeyError as ex:
print('3 Some KeyError', ex)
#####################
This produces:
0 normal exception 'bad key'
1 library exception '4 not found'
2 Some KeyError 'bad key'
3 Some KeyError '4 not found'
In cases 2 and 3 there likely wouldn't be an exception handler, and the
error would just propagate. I'm catching it here just to show that it's
a KeyError that's propagating, although in case 3 it's really a
LibKeyError (a subclass of KeyError).
In my example, the library code that wants to use exception spaces needs
to change (same as your proposal), the client code that wants to use
exception spaces needs to change (same as your proposal), and the client
code that doesn't want to exception spaces doesn't need to change (same
as your proposal). In my case, the library changes involve defining
exception classes and require the library to catch and re-throw various
exceptions. In your proposal, the library changes involve new function
parameters and require the library to catch and re-throw exceptions in a
new way.
In my example, notice that the user code that cares about exceptions
spaces just needs to catch the library-specific exception base class. Or
it could go further by catching a specific library exception. And the
user code that doesn't care about exception spaces requires no changes.
In your proposal, the user code that does care would need to change how
the functions are called, and would need to change how they catch
exceptions. The code that doesn't care about exception spaces requires
no changes.
I think my example and your proposal achieve the same effect, with a
similar amount of required changes on the library side and on client
code that wants to participate. Both my example and your proposal
require no changes to client code that doesn't want to participate.
The difference is that my example requires no changes to Python and so
works today. It's also probably more efficient because it doesn't
require the extra espace parameter.
you can do this. you can do all sorts of weird things. you don't have
to immediately handle them. you can let them bubble up. but
espaces/channels (I've been calling them both) let you be explicit
about which raisers and which handlers link up to eachother. you
aren't supposed to mindlessly pass espaces forward. if your function
isn't supposed to raise, *don't use espaces* and don't use the new
operators. that's perfectly valid, and is in fact intended. if you
decide that a future version needs to be able to raise, you can add
espaces then. but you wouldn't litter your codebase with espaces:
you'd have an espace=None argument, and perhaps one or two statements
where you pass in an espace (either with espace=espace or espace-aware
operators). or you might not even accept an espace and instead just
want to handle errors in the function itself but don't wanna mask
bugs. all this is valid, and intended.
The problem with "if you decide that a future version needs to be able
to raise" is that you now need to pass an espace parameter all through
your code, and into the library. That might be many layers of functions
that now need to be modified. If A calls B calls C calls D which calls
the library, and A decides it wants use an exception space, then in your
proposal B, C, and D also need modifying to pass through the espace
argument. With normal exceptions (my example) that's not the case: only
A needs modifying. And imagine that C and D aren't in your code, but are
in second library. With your proposal, you'd need to modify that library
too.
> we'll also need operator variants. I was thinking of the following:
>
> x.in foo bar # for attribute access
> x[in foo bar] # for item access
> x +in foo bar # for math
> # etc
>
> (yes, these are very ugly. this is one of the unfortunate things of
> trying to retrofit this into an existing language. but they're more
> likely to catch bugs so I'm not worried.)
There have been various proposals like this in the past, revolving
around "just raise exceptions in my code, not in any code I call". I
can't say if this is better than any of the prior ones. Usually the
advice is to just limit the scope of your exception handlers. Although
that can't catch everything, I think your operator proposal would have
the same issues. But that would of course need to be worked out.
Eric
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/MIDAE6V2JUPDLZJRQZXRUTTDE5YJQ5BR/
Code of Conduct: http://python.org/psf/codeofconduct/