replace the +
with chain
, replace the *_
with islice
something along the lines of:
x, y, z = islice(chain(arg, (None,)*3), 3)
replace the +
with chain
, replace the *_
with islice
something along the lines of:
x, y, z = islice(chain(arg, (None,)*3), 3)
Exactly. To clarify further, exceptions should be caught where they occur to ensure proper handling. If you choose to propagate them instead, it may be for debugging purposes.
This is how you can handle multiple nested KeyError
exceptions effectively:
def lookup(name=None):
raise KeyError
try:
try:
try:
try:
lookup()
except KeyError:
print('First KeyError!')
lookup()
except KeyError:
print('Second KeyError!')
lookup()
except KeyError:
print('Third KeyError!')
lookup()
except KeyError:
print('Done!')
If you ignore exceptions and cross your fingers, donât be surprised when important KeyError
exceptions are silently suppressed, leaving you unaware of underlying issues:
def lookup(name=None):
raise KeyError
try:
lookup(lookup(lookup(lookup())))
except KeyError:
print('Done?')
making a new topic because the old topic got derailed (sigh)
we have a custom exception:
class PropertyError(LookupError):
"""Raised to indicate improper use of a DataProperty.
"""
pass
(where âimproper useâ just means itâs not supported by the data source)
we have an abstract API method that raises this exception:
@abc.abstractmethod
def get_property_values(self, prop):
"""Returns the values associated with the given property as an iterable.
If duplicated, earlier values should override later values.
Args:
prop (DataProperty): The property.
Returns:
The values associated with the given property.
Raises:
PropertyError: If the property is not supported by this data
source.
LookupError: If the property is supported, but isn't available.
"""
raise PropertyError
then we have various implementations that processes multiple data sources together into a new data source.
as may or may not be obvious, just because one data source doesnât support a property, doesnât mean the other data sources donât support it. so when you mix data sources that support a property and those that donât support a property, you need to decide how to handle these various cases.
for example, we might have this:
# ignore sources without the property
for source in self.sources:
try:
something = source.get_property_values(prop)
# some other stuff
except PropertyError:
pass
or we might have this:
# require all sources to have property
for source in self.sources:
something = source.get_property_values(prop)
or we might accidentally write the latter when we want the former.
and, well, these data property processors are themselves data property sources which can be used in other data property processors. that is, we can put a ârequire property on allâ inside âignore sources without the propertyâ. and this is mostly fine.
unless itâs a bug.
the fact that these are exceptions means they donât get stopped where we handled them incorrectly. no, they just keep going and propagating into other code in ways we never expected. itâs simply impossible to debug.
in fact, a similar situation is what led to the creation of PEP 479. unfortunately, PEP 479 doesnât care about exceptions, it cares about generators, even tho generators use exceptions. in our opinion, PEP 479 wouldâve been better off if it just deprecated using exceptions for iteration altogether, because it honestly reads like itâs trying to. instead, we get this eternal bolt-on of workarounds for a small issue mostly inherent to exceptions that python is doing its best to avoid properly addressing and avoid taking responsibility for. (we mean, just look at how many times ppl told us weâre holding it wrong in our previous thread, and tell us how thatâs not âblaming the userâ. but we have no way of getting that addressed so weâre not even gonna bother.) anyway hopefully this thread can stay on-topic and we can have a proper discussion about the issue.
As was said in the other thread, the problem seems to be that youâre using a subclass of LookupError when you really just want a custom error. What is the benefit of subclassing LookupError?
What you are describing is exactly what exceptions are supposed to do. If you donât catch them, they keep going. Youâre asking for some way to notice when one of these exceptions leaks out. Thatâs called catching the exception. Thatâs exactly what the try statement exists for.
No, this is not the same thing. PEP 479 dealt with a very specific interaction wherein a function call turns into a next()
result. This is not that.
Why are you creating a new thread and then making all the exact same complaints? Are you surprised that youâre getting the exact same responses?
@moderators Can this just get merged back into Better way to deal with exceptions please?
Itâs really not Chris thatâs the source of confusion here. You still havenât actually explained what you think a suitable behaviour should be, nor can I really parse what you think the problem is to begin with. I still canât tell if itâs exceptions that arenât caught and bubble up the stack (giving a nice stacktrace indicating where you went wrong) or exceptions that are caught overzelously and ignored (a self inflicted issue) that youâre taking issue with.
would you say the issue PEP 479 addresses is self-inflicted? why does PEP 479 exist if itâs self-inflicted?
Youâre being offensive and then blaming the person who takes offense?
PEP 479 exists because the way the language used to work was undesirable, where a specific kind of exception meant to implement the iterator protocol was being implicitly handled by the protocol in situations where it shouldnât. So the people responsible for the language changed its behavior.
What we are trying to say to you is that, the way you described your problem, it doesnât appear to be your case, because you are explicitly catching your exceptions, and you could adjust the code on your end by simply changing the exception class from which your control flow exceptions inherit.
Maybe we are wrong, but you will have to change the way you are explaining things, or we wonât be able to help you.
we would try to explain it, but unfortunately, despite the fact that PEP 479 is related to our issue, even PEP 479 itself fails to recognize that fact.
if all our attempts to bridge that gap so far have failed, why should we keep doing the same thing and expect it to go any different?
Trying to sidestep some of the tangents here, but: If you donât want these conditions to propagate, why are you using exceptions to represent them? Would using some kind of âreturn codeâ be an alternative? Can you give an example of a piece of code that currently does one thing that you find problematic, and explain what you behavior you would hope for instead?
for starters, return codes are unpythonic.
yes theyâre a better solution (and why we consider PEP 479 an attempt to retrofit generators to act more like return codes instead of exceptions) but only because python lacks a construct to make exceptions feel like return codes.
if you think about it:
for
)dict.get
(with default))getattr
(with default), hasattr
)but thereâs no concept of, say, âeither the immediate caller catches this, or it becomes a RuntimeErrorâ. nor anything comparable. except in generators.
and like, why does this come up in generators? what makes generators different? well theyâre not that different from getattr
and this is a hill weâre willing to die on:
__getattr__
calls other getattr
, uses attributes directly, etc__next__
also calls generators, iterators, etctho generators have the convenience of opaque implementation details, so it was easy enough to silently âfixâ them (yet not silently enough - still required deprecation period and stuff). but it basically boils down to: generators regularly call code that raises the same exceptions as generators.
Okay, but can you explain what you want to do in your code example, which doesnât involve any generators?
we want our code to have the property that, when we call into other data sources (keeping in mind all data sources have to raise the same exceptions, by API contract), we get properties similar to those of generators.
that is, if they raise PropertyError, that should be converted into RuntimeError before being propagated. if we raise PropertyError, that should be propagated directly to the caller.
Simply donât allow them to raise PropertyError
. Or, use a private _PropertyError
for your own use and allow others to use PropertyError
.
that doesnât work with the API contract.
we have a class that takes a list of sources and is itself a source. we need to interoperate with the user both ways.
even if we can change the API contract⊠so we swap the exception type, now for interoperability everyone else has to do it too. because the purpose of an API contract is interoperability.
OurDataSource([TheirDataSource(...)])
needs to work, as does TheirDataSource([OurDataSource(...)])
.