Skip to content
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

[RPC] Synchronous, typed interface for Python client #1272

Merged
merged 14 commits into from
Sep 17, 2021

Conversation

m-yac
Copy link
Contributor

@m-yac m-yac commented Aug 27, 2021

This PR adds a synchronous and type-annotated interface for the python Cryptol RPC bindings. The advantages of such an interface are:

  • No more calls to .result(), since everything is synchronous
  • mypy can give useful feedback about the results of running RPC commands, since everything is type-annotated (for example, the return type of check is annotated to be a CheckResult)

All the tests have been updated to use the new interface, though the old interface still exists and is functionally unchanged (aside from the one exception noted below).

This PR also makes the following two changes that affect both the old and new interfaces:

  • The CheckReport class now has __bool__ method which returns the value of its success field (e.g. so self.assert(c.check(...).success) can be replaced with just self.assert(c.check(...))).
  • Instead of returning a Union[bool, List[CryptolPython], OfflineSmt], the sat, prove, and safe methods now return a Union[SmtResult, OfflineSmtQuery], where SmtResult has the subclasses SmtUnsatResult, SmtSatResult, and SmtInvalidResult. The SmtUnsatResult class has a __bool__ method which corresponds to the old bool case and the SmtSatResult and SmtInvalidResult classes are subclasses of List[CryptolPython]. Thus, this change will only break code which explicitly checks whether the result of sat/prove/safe is a bool (e.g. isinstance(c.sat(...).result(), bool)) but not code which just uses it as a truthy value (e.g. if c.sat(...).result(): ...).

Question: I called the type of the output of from_cryptol_arg "CryptolPython", but I feel like this could potentially be confusing. Thoughts on a better name?

NB: The definition of CryptolPython really should be:

CryptolPython = Union[bool, int, BV, Tuple[CryptolPython], List[CryptolPython], Dict[str, CryptolPython], OpaqueValue]

but mypy doesn't yet support recursive types.

@m-yac m-yac requested review from atomb and pnwamk August 27, 2021 20:06
Copy link
Contributor

@atomb atomb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks nice. As I mentioned in a comment, maybe CryptolValue would be a good name for what you're currently calling CryptolType? Although maybe that would make people think that it's for the Cryptol syntax of values, rather than the Python interpretation of them.

cryptol-remote-api/python/cryptol/commands.py Outdated Show resolved Hide resolved
Copy link
Contributor

@pnwamk pnwamk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the changes. Just have some minor comments here and there, possibly bikeshedding on the interface of the SMT-related commands xD (sorry - I got confused and thought maybe a slightly simpler set of return types would help).

cryptol-remote-api/python/cryptol/commands.py Outdated Show resolved Hide resolved
cryptol-remote-api/python/cryptol/commands.py Outdated Show resolved Hide resolved
SmtResult.__init__(self, qtype)
list.__init__(self, cxs)

SmtQueryResult = Union[SmtResult, OfflineSmtQuery]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tell me what you think! It seems like having to worry about whether or not we got an OfflineSmtQuery in every instance where we call one of these functions is silly, because we can only get those under certain circumstances. I.e., when the solver is specified as an offline solver. Would it be nice to have prove_offline, sat_offline, and check_offline which only returns a OfflineSmtQuery?

We could then be clearer about our SMT Solver type (I forget what it's called as I'm typing this), and have one type for the online solvers (probably called whatever it is now), and then have a specific OfflineSolver type that delineates the available offline solvers. Then the online and offline versions of the functions could be specific about what solvers they allow, and the return types could not worry about the impossible result types! (i.e., online query will never return OfflineSmtQuery, etc).

Aye? Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the spirit of having the Python interface mirror the REPL interface, I didn't make separate methods for offline queries, I instead used @overloads to give the two cases (the OnlineSolver and OfflineSolver cases, as I called them) distinct types. Let me know what you think

from . import CryptolConnection, SmtQueryType


def connect_sync(command : Optional[str]=None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these functions need the _sync prefix, given that they're in their own module? (i.e., if you wanted to import the old and new interface, couldn't you have a prefix on the module imports to tell them apart?)

It's not a big deal, but it seems like if we move towards having the synchronous interface be the default it might be funny to have _sync on the methods. If anything we'd want the default interface to be smooth/streamlined and the odd one to have warts (if it has to -- i.e., if import prefixes aren't sufficient).

(Maybe I'm not seeing a reason to have the suffixes though?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored so now the tests use cryptol.sync.connect(...) instead of cryptol.connect_sync(...). I don't know if the module trickery I did to get sync working as an abbreviation is very Pythonic though... see __init__.py

@m-yac
Copy link
Contributor Author

m-yac commented Sep 14, 2021

Looks like the CI failures have nothing to do with these RPC changes.

@pnwamk pinging you to see if you want to take another look at this to make sure I addressed your comments.

Copy link
Contributor

@pnwamk pnwamk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo adding just a tiny more to the docstrings of those SMT-related queries IMO.

@m-yac
Copy link
Contributor Author

m-yac commented Sep 15, 2021

LGTM modulo adding just a tiny more to the docstrings of those SMT-related queries IMO.

Good catch, adding docs for this is important. What do you think of my latest commit?

@pnwamk
Copy link
Contributor

pnwamk commented Sep 15, 2021 via email

@m-yac m-yac merged commit ee8cbac into master Sep 17, 2021
@RyanGlScott RyanGlScott deleted the rpc/synchronous-interface branch March 22, 2024 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants