-
Notifications
You must be signed in to change notification settings - Fork 2
[CPP-119] Minimal CLI with rough Backend + Frontend coexistence. #53
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
Conversation
a2513c8
to
f063a6f
Compare
f063a6f
to
f4e143f
Compare
In order to add command line flags when running |
utils/glob.ds
Outdated
@@ -1,8 +1,10 @@ | |||
fn glob_paths_excluding_target | |||
exclude_files = array "src/main/python/console_resources.py" |
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.
if you add the generated file to .gitignore, it, and target/*
, and everything else here can be replaced by the new .gitignore supported added to cargo-make in sagiegurari/cargo-make#542
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.
Done. Unfortunately this required me to use yet another hack.
The gitignore_path_array is giving me results postfixed with "./" whereas the glob_array does not have this postfix for found entries.
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.
postfixed -> prefixed?
sounds like we might want to create an issue upstream about this after we've go it working nicely, so the maintainer can work out how to make it easier for others to use this new voodoo
952f38e
to
e78a81b
Compare
console_backend/src/cli_options.rs
Outdated
#[clap(long = "refresh-rate", validator(is_refresh_rate))] | ||
pub refresh_rate: Option<u8>, | ||
|
||
#[clap(long = "tab", validator(is_tab))] |
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.
Not sure if it's possible in clap, but in structopt you can directly declare a type on your option struct such as
pub tab: Option<Tabs>
Then implement a FromStr
for your type and structopt will automatically pick that up to parse the incoming command line argument appropriately. Perhaps something similar is possible with clap?
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 a great idea. I have hit a pickle though. I hadn't realized FromStr is already impled for Tabs because it is a generated common_constant. The error message for incorrect tab variant does not provide any information about available tabs and the helper does not either. The reason I chose for this be a shared constant is because these Tab variant names are used in python. But this means I can't add my own error message without a validator like this.
[cargo-make] INFO - Running Task: run
error: Invalid value for '--tab <tab>': Matching variant not found
For more information try --help
What do you think we should do?
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.
It looks like strum has a variant names trait maybe I can throw have py2many impl it's own FromStr instead of using enumstring strum trait then spit out https://docs.rs/strum_macros/0.20.0/strum_macros/derive.EnumVariantNames.html for the exhaustive case?
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.
Maybe we could manually implement FromStr for the enum using the EnumString trait?
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.
Sketch of this idea here: https://github.com/swift-nav/console_pp/pull/54/files
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 at the minimum if we had our py2many fork add the enumvariant trait then we could just manually implement FromStr as you did for CliTabs but throw all the available variants into the error messages.
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.
Submit a PR to https://github.com/jayvdb/py2many/tree/console if you need a custom hack, however that branch is now 100% merged into upstream, so if the changes are generic enough to be useful for others, submit them to https://github.com/adsharma/py2many and they should get reviewed and merged quickly enough.
And/Or fork strum
and implement improvements there. Possibly there is a PR or issue already in
https://github.com/Peternator7/strum about what we need.
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 created a ticket to track the strum feature looks like it would be more involved:
https://swift-nav.atlassian.net/browse/CPP-148
For the short term I created a py2many fork that adds enumvariant to the derives for enums which allows us to make wrappers with a FromStr error containing variant names.
|
||
// // Frontend Options | ||
/// Don't use opengl in plots. | ||
#[clap(long = "no-opengl", parse(from_flag = ops::Not::not))] |
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.
TL;DR: we can leave this for now, but I think we need to think more about this long term
This is going to look really weird when/if we make a command line version of the app without the GUI, or if we make a version that uses a terminal UI instead. I wonder if there's a way to remove the arguments from the argv we pass to the backend?
If we leave the CLI parsing here, then we'd need features to turn off these CLI options for a "backend only" CLI parser.
Alternately, I think we could invert this problem, it seems a lot less weird for front-end to include back-end options. So the front-end would parse the command line, but also include the back-end options, and then pass those down to the backend. In that case we may not even need CLI parsing on the backend (these options would just be parameters to the server.start
method, or an initial "init" message that the front-end would send).
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'm game for either it wouldn't take too long to switch it out. The main reason for this was trying to separate as much CLI from python as possible if we were to eventually switch. As for your first suggestion the problem lies with "unhandled args" if python is only removing the expected args then passes everything else to the backend, then all that will be seen is the backend's helper and no info on potential frontend args. It comes down to who do you want to handle unknown args.
Another option would be to throw python completely out and have Rust handle all potential args, which may be easiest in the end to swap out with a non GUI cli.
I'm not opposed to your second option. Two concerns would be just thinking of the potential number of args (considering the current console) we would have to pass to the backend initially if we were to pass it via server.start. This may not be too bad but worth considering. Second is just what we've harped on before about removing as much as possible from Python for a later switch.
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.
Another suggestion @jayvdb was talking to me about earlier this morning in our Donut was framing the backend to catch connection requests as a list of CLI args. Then when saving history we would just save the full list of arguments to a yaml file. This way we could frame all connection requests to just deconstruct a list of cli args. This would be a pretty thorough redesign but could frame how we think about this problem now.
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.
"..to catch connection requests as a list of CLI args.." : "catch" -> "cache"
console_backend/src/server.rs
Outdated
|
||
#[text_signature = "($self, /)"] | ||
pub fn start(&mut self) -> PyResult<ServerEndpoint> { | ||
let filtered_args: Vec<String> = std::env::args().filter(|x| x != "python").collect(); |
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 it make sense for Python to pass down the args we need to parse? Having python
as the start of the argv array is far from certain (as far as I know)-- when the app is compiled with Nuitka or something else, this will change.
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 this is related to my other comment we can continue the discussion there. As for this approach it is not looking for python but removing it. The general idea is that all other approaches are assuming there is one binary that is run and it seems the "std::env::args" will throw out the first argv then parse everything else afterward as command line args. I confirmed this works for the binary produced by fbs but not nuitka yet. That being said it should work as long as there is a single binary called to start the app.
console_backend/src/types.rs
Outdated
} else { | ||
println!("Couldn't open file..."); | ||
} | ||
set_connected_frontend( |
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.
Should this move inside shared_state.set_running
?
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.
Done. Good suggestion
console_backend/src/utils.rs
Outdated
pub fn from_flowcontrol_str(flow_str: &str) -> FlowControl { | ||
/// - `Ok`: The associated serialport::FlowControl variant. | ||
/// - `Err`: Error describing available flow controls available. | ||
pub fn from_flowcontrol_str(flow_str: &str) -> Result<FlowControl, String> { |
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'm still not sure why we don't implement FromStr
for these types? There's a standard trait for this type of conversion, why can't we use it?
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.
FlowControl is not something I implemented so I can't impl for it. I can create my own type as a wrapper. I think we discussed this in the original PR this feature was added.
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.
Right, but we can implement a wrapper and then implement Deref and FromStr
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.
Simplified the wrapper a bit here: https://github.com/swift-nav/console_pp/pull/54/files
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.
Done per your PR. Thank you for the assist!
console_backend/src/types.rs
Outdated
/*close_when_done = */ true, | ||
); | ||
sleep(Duration::from_millis(100)); | ||
sleep(Duration::from_millis(150)); |
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.
Constant?
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.
Done.
console_backend/src/types.rs
Outdated
client_send, | ||
RealtimeDelay::Off, | ||
); | ||
match from_flowcontrol_str(&flow) { |
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.
See comment below about implementing FromStr
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.
Done per your PR.
|
||
threading.Thread(target=receive_messages, args=(app, backend_main, messages_main,), daemon=True).start() | ||
component = QQmlComponent(engine) | ||
component.setData( |
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.
If this is a hack/work-around from Qt support, can we explain what it does with a comment?
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.
Done. I added a summary of what Qt conveyed to me in the support request.
0aec5d2
to
6094ad8
Compare
6094ad8
to
aba6837
Compare
* Respond to review requests. * simpler FlowControl wrapper, wrap generated Tabs object Co-authored-by: John Michael Burke <[email protected]>
utils/glob.ds
Outdated
handle = glob_array ${1} | ||
out = array | ||
gi_handle = array | ||
gi_handle_unfiltered = gitignore_path_array ./ |
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 should ./
shouldnt be here, as it should be based on a cargo-make variable which defines the root.
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 had a bug in my code that was not working for absolute paths. Now I use the pwd
variable exposed to duckscript.
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'm fine to merge though we may want to capture any ideas we have around strum/py2many improvements for future work.
|
||
#![allow(non_snake_case)] | ||
#![allow(non_upper_case_globals)] | ||
#![allow(trivial_casts)] |
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.
@jayvdb Can you add me as a collaborator to your py2many fork? I think we will need to rely on a custom fork for these imports. I can submit my PR to your fork.
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.
Damn I totally missed this comment. Now that it is merged to main do you want to uprev your fork and I can link back to it?
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.
Keeping it pinned to your fork branch is good. Later, we can switch to v0.2.0, or my JS branch, whichever comes arrives.
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.
But my fork does not have your changes which fix the errors in the current py2many branch?
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.
oh, then use upstream master?
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.
Alright per our conversation on Slack. I created a new branch using the uprevved changes and included the missing clippy lints needed for our 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.
I also invited you to collab on my py2many fork @jayvdb
a0b07e0
to
ece64b4
Compare
Random Extra Features
Frontend CLI Options
Backend CLI Options
Subcommands:
tcp <host> --port <port>
file <filepath>
serial <serialport> --baudrate [the available baudrates] --flow-control [the available flow-controls]
The python argparser has had the help disabled. I'm not sure if the usage gets correctly suppressed though so this may need to be reworked. In order for python arguments not to cause issues for the Rust backend CLI args, the python args are also implemented in the Rust CliOptions struct.
Fortunately Qt was able to suggest an approach for setting elements in our Constants/Globals file from PySide2. That is how I am able to set refresh-rate/opengl/initialtab/etc. Probably should try to use this sparingly though.