-
Notifications
You must be signed in to change notification settings - Fork 55
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
add support for setting protocol handlers with {.raises.}
annotation
#1064
Conversation
All of the internal protocol handlers are restricted to `{.raises.}` of `[CancelledError]`. However, the `LPProtoHandler` type is part of the public interface, and example / test code raises more errors than just `[CancelledError]`. The caller is aware of that and `CatchableError` is caught. To allow annotating the internal handlers with the proper `{.raises`.} annotation, support for an extra `LPProtoHandler2` is added. This is backward compatible as the old `LPProtoHandler` is still allowed. Examples still compile fine. There is one test that needs a slight adjustment as it accesses the internal `handler` field directly. That field needs to be renamed so that the `template` is used instead. Eventually, `LPProtoHandler` may be phased out, with appropriate notices to users who define custom handlers and the migration requiring errors to be handled inside the handler instead of raising them. At this time, such a deprecation is not yet applied, especially while the internal logic is still relying on the previous handler flow.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## unstable #1064 +/- ##
=========================================
Coverage 82.56% 82.56%
=========================================
Files 91 91
Lines 15814 15816 +2
=========================================
+ Hits 13057 13059 +2
Misses 2757 2757
|
libp2p/protocols/protocol.nim
Outdated
conn: Connection, | ||
proto: string): Future[void] {.async.} | ||
|
||
LPProtoHandler2* = proc ( |
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.
hm..
not that I necessarily disagree with this change, but this becomes an odd case which should have been solved by covariance - ie technically, because we allow any exception to be raised, we should not constrain here to [CancelledError]
(raises: [ValueError]
would also be "correct")..
I wonder if there's a way to solve this with some generics, ie create a new
that takes an new[E](handler: proc(): InternalRaisesFuture[void, E]
- if that's possible, it's probably something we should formalize in chronos somehow, to allow expressing it without referencing Internal..
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.
sure, it would still involve going through the new
or handler=
instead of constructing the object manually. Agree that making it more generic would also serve the role of allowing {.async: raises.}
annotation, but for now would have to go with Internal
. I guess that's alright to get started.
libp2p/protocols/protocol.nim
Outdated
@@ -22,9 +22,9 @@ type | |||
conn: Connection, | |||
proto: string): Future[void] {.async.} | |||
|
|||
LPProtoHandler2* = proc ( | |||
LPProtoHandler2*[E] = proc ( |
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.
does lp2[E] = proc ...: Future[void].Raising(E)
work?
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 handler=
implementation worked for me.
I tested it by changing protocols/connectivity/relay/client.nim
to
proc handleStream(conn: Connection, proto: string) {.async: (raises: [CancelledError]).} =
...
cl.handler = handleStream
and NIMFLAGS="--mm:refc" nimble test
still worked fine (refc because on orc it segfaults in metrics unrelated).
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.
no, I mean you don't need to expose InternalRaisesFuture
here (we can avoid it here too probably: #1050 (comment)):
import chronos
type T[E] = Future[void].Raising(E)
proc x() {.async: (raises: [ValueError]).} = discard
proc f[E](handler: proc(): T[E]) = debugEcho type(handler)
f(x)
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 proc
takes parameters and is passed around, hose parts would have to be continuously repeated. putting the [E]
directly on the proc
doesn't work, only the result type can be done that way.
Exposing InternalRaisesFuture
in a single place seems like the lesser evil to me, it's trivial to adjust if necessary
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.
IMO adding any *2
to the codebase isn't a good software engineering practice as it isn't sufficiently descriptive. Also, why is an Ineternal*
type from another project referenced here?
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.
have never seen such naming used in mainstream libraries.
The most used and popular api on earth does this, more or less: https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfoexa (though they call it Ex
instead of 2
)
I understand your concern, but sometimes, there just isn't any good naming solution and indeed, a comment might be the best option.
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.
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.
with a cherry on top for 3 in the interface name ;) in fact, com interfaces / all of .NET often versions itself this way
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 knew I'd regret the comment, but never worked with MS tho.
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.
So .. thinking a bit more about this, the answer is actually simple: we can remove LPProtocolHandler
/LPProtoHandler2
altogether and just use Future[...].Raising(E)
explicitly in the wrapper, which ensures user code is not polluted with a name that we don't necessarily want to support.
This makes sense also because there as limitations to how you can use aliases together with `` ie var f: LPProtocolHandler[[ValueError]]
doesn't work because of macro instantiation order issues.
libp2p/protocols/protocol.nim
Outdated
LPProtoHandler2*[E] = proc ( | ||
conn: Connection, | ||
proto: string): InternalRaisesFuture[void, E] |
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.
LPProtoHandler2*[E] = proc ( | |
conn: Connection, | |
proto: string): InternalRaisesFuture[void, E] | |
LPProtoHandler2*[E] = proc ( | |
conn: Connection, | |
proto: string): Future[void].Raising(E) |
this does not work?
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.
nope, not on the proc, only on the result
|
||
LPProtocol* = ref object of RootObj | ||
codecs*: seq[string] | ||
handler*: LPProtoHandler ## this handler gets invoked by the protocol negotiator | ||
handlerImpl: LPProtoHandler ## invoked by the protocol negotiator |
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 rename breaks LPProtocol(handler: ...)
- not sure it's worth it: new code can use LPProtocol.new
to get the covariance handling.
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.
Yes, that's what I mentioned above. It has to be renamed otherwise handler=
doesn't take precedence, and handler=
is used a lot, while using LPProtocol(...)
over LPProtocol.new
was only used in 1 example and 1 test, where it seems to be not intentional.
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.
breaks nimbus-eth2 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.
1 liner to fix
Avoid potenial issue with vacp2p/nim-libp2p#1064 (comment) in a future dependency bump.
libp2p/protocols/protocol.nim
Outdated
conn: Connection, | ||
proto: string): Future[void] {.async.} | ||
|
||
# Callbacks that are annotated with `{.async: (raises).}` explicitly |
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.
looks great, thanks.
Avoid potenial issue with vacp2p/nim-libp2p#1064 (comment) in a future dependency bump.
All of the internal protocol handlers are restricted to
{.raises.}
of[CancelledError]
. However, theLPProtoHandler
type is part of the public interface, and example / test code raises more errors than just[CancelledError]
. The caller is aware of that andCatchableError
is caught.To allow annotating the internal handlers with the proper
{.raises
.} annotation, support for an extraLPProtoHandler2
is added. This is backward compatible as the oldLPProtoHandler
is still allowed. Examples still compile fine. There is one test that needs a slight adjustment as it accesses the internalhandler
field directly. That field needs to be renamed so that thetemplate
is used instead.Eventually,
LPProtoHandler
may be phased out, with appropriate notices to users who define custom handlers and the migration requiring errors to be handled inside the handler instead of raising them. At this time, such a deprecation is not yet applied, especially while the internal logic is still relying on the previous handler flow.