-
Notifications
You must be signed in to change notification settings - Fork 276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: error in IPython while handling HTTP4xxClientError
#486
Conversation
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
fastcore/net.py
Outdated
def _init(self, url, hdrs, fp, msg=msg, code=code): super(cls, self).__init__(url, code, msg, hdrs, fp) | ||
cls.__init__ = _init |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The crux is to override the __init__
set by get_class
to call super().__init__
.
The _mk_error
function was needed so that _init
is included as-is in the closure. Without _mk_error
, each iteration of the for loop would overwrite the previously defined _init
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is getting pretty obscure! Is there any simpler way to do this, even if a bit more verbose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you like having separate classes for each error code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd prefer to use the urllib.HTTPError
class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing I do want is to be able to catch exceptions based on what kind of error occurred. Is there a way to do that without separate classes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would using type(name, bases, attributes)
directly be an easier way to create these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think like so:
try: urlopen(...)
except HTTPError as e:
if e.code != 401: raise
# ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would using
type(name, bases, attributes)
directly be an easier way to create these?
I thought about it. type(nm, (HTTP4xxClientError,), {'code': code, 'msg': msg})
works and is concise, but would be a tiny breaking change in the signature, from current:
def __init__(self, url, hdrs, fp, msg=default_msg, code=default_code):
to the parent class':
def __init__(self, url, code, msg, hdrs, fp)
That might be okay though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add __init__
to those attrs to get the init we want?
def _init(self, url, hdrs, fp, msg=msg, code=code): HTTP4xxClientError.__init__(self, url, code, msg, hdrs, fp) | ||
cls = type(nm, (HTTP4xxClientError,), {'__init__':_init}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jph00 I think this is much better with your suggestion to use type
.
Another thing that simplifies it is to directly call HTTP4xxClientError.__init__
instead of super(...)
, because:
- Using
super
dynamically like this requires explicitly passing thecls
. And in order to pass a differentcls
each time, we need to define_init
in a closure to avoid overwriting it with eachcls
. - Since
_init
needs a reference tocls
, we have to first define it withtype(...)
and then patchcls.__init__
.
This of course has the downside that HTTP4xxClientError
is hardcoded instead of having super
's behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of interest, this seems to work too, but I think it's more obscure:
for code,msg in _httperrors:
class _(HTTP4xxClientError):
def __init__(self, url, hdrs, fp, msg=msg, code=code): super().__init__(url, code, msg, hdrs, fp)
_.__name__ = f'HTTP{code}{msg.replace(" ","")}Error'
_.__qualname__ = _.__qualname__.split('.')[0] + _.__name__
globals()[nm] = ExceptionsHTTP[code] = _
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess in that case I'd have a function like:
get _httperrcls(url, code, msg, hdrs, fp):
class _c(HTTP4xxClientError):
def __init__(self, url, hdrs, fp, msg=msg, code=code): super().__init__(url, code, msg, hdrs, fp)
_c.__name__ = f'HTTP{code}{msg.replace(" ","")}Error'
_c.__qualname__ = _.__qualname__.split('.')[0] + _c.__name__
return _c
Yay! |
For example:
cc @hamelsmu @jph00