pharo-contributions / clap-st Goto Github PK
View Code? Open in Web Editor NEWCommand-line argument parsing for Pharo
Command-line argument parsing for Pharo
Hi,
When I don`t use compulsory args and try this
| pos1 command context match|
pos1 := (ClapPositional withName: 'inputFile').
command := (ClapCommand withName: 'MyApp')
addPositional: pos1.
context := ClapContext on: #('MyApp' 'input.txt').
match := command matchOn: context.
(match at:pos1) value. "Returns input.txt"
But when I use compulsory args and try
| pos1 command context match|
pos1 := (ClapPositional withName: 'inputFile').
command := (ClapCommand withName: 'MyApp')
addCompulsory: (pos1) asCompulsory .
context := ClapContext on: #('MyApp' 'input.txt').
match := command matchOn: context.
(match at:pos1) value. "Returns inputFile"
Shouldn`t they both return the same value. The second one is returning the positional name rather than the value. Am I doing it correct?
Enhance ClapFlag with option to specify short form of flag name.
Describe the request
Currently, short form ClapFlag instance is always represented by first letter of long version name. This causes a problem, when having multiple flags starting with same letter, e.g. noHeader
and name
. There should be an option to specify short name programmatically (when specifying command) in order to avoid name conficts.
Expected behavior
(ClapFlag id: #name)
description: 'Name description.';
shortName: 'n';
...
(ClapFlag id: #noHeader)
description: 'No header description.';
shortName: 'h';
Expected development cost
Version information:
I'd like to create a custom-named command line handler that I can run in the shell, s.a., the Eval
command from How to use the ClapCommandLineHandler in Pharo. However, following the tutorial, am hitting a couple of "Message sent but not implemented"
errors. I suspect this is because the tutorial is simply outdated and perhaps warrants the need for a new tutorial as per #22 - In the meantime, is there a "hot-fix" to resolve these errors?
Discussion started at pillar-markup/pillar#440
@cdlm:
If flags have side-effects then their order of execution must be clear; however there is no obvious order that would be valid in all situations. Sometimes the order of declaration in the specification makes sense, sometimes it's the order of invocation on the command line, and it could also be some arbitrary order depending on the application. Some flags can also be repeated.
I didn't want to assume how and when an app should use its flags. So they are best seen as optional parameters that you use when you need their value, instead of an imperative API to a stateful builder. The meaning blocks are there to lift the string passed on the command line into an object value that's useful to the app. We tried a version where the block had a side-effect, but then in the meaning block it looked like (args at: #all) value.
with no immediate indication of what effect this has.
For commands the situation is different because each subcommand selects a narrower subset of the app's behavior. In the end you have one and only one most-specific subcommand, so that makes a convenient entry point.
If flags have side-effects then their order of execution must be clear
Yes, and Clap could define an order :). Even more, it could have a default one "definition order" for example. And then accept other strategies like "invocation order", and each command could define the order of flags with a strategy, no?
I didn't want to assume how and when an app should use its flags. So they are best seen as optional parameters that you use when you need their value, instead of an imperative API to a stateful builder. The meaning blocks are there to lift the string passed on the command line into an object value that's useful to the app.
I understand. But to me, we should take a stand and favour one usage over the other :).
And maybe give hooks to the user to override those decisions.
We will never make a choice that pleases everybody, I'd take that outside the table :P.
My heuristic here is that we should favour most common usages, and leave the freedom to do less common usages.
We are now using a full-grown framework to parse command line arguments, so we should extract the juice from it, no?
I'd like to discuss alternatives, tell me what you think:
myBuilder := MyBuilder new.
ClapCommand new
add: ((ClapFlag id: #x) meaning: [ myBuilder withX ]);
add: ((ClapFlag id: #y)
beIntegerValue; "clap does the parsing and validation for me ;)"
meaning: [ :anInteger | myBuilder withY: anInteger ]);
add: ((ClapFlag id: #z) meaning: [ :aString | myBuilder withZ: aString asUppercase ]);
meaning: [ myBuilder build ].
ClapCommand new
add: ((ClapFlag id: #x) meaning: [ SomeObject new ]);
add: ((ClapFlag id: #y) meaning: [ 17 ]);
add: ((ClapFlag id: #z) meaning: [ :value | value asInteger ]);
meaning: [ :x :y :z | MyObject withX: x withY: y withZ: z ].
ClapCommand new
add: ((ClapFlag id: #x) meaning: [ SomeObject new ]);
add: ((ClapFlag id: #y) meaning: [ 17 ]);
add: ((ClapFlag id: #z) meaning: [ :value | value asInteger ]);
meaning: [ :args | MyObject withX: (args at: #x) withY: (args at: #y) withZ: (args at: #z ifAbsent: [0]) ].
I prefer solution 1. I think it is easy to understand and that defining a clear order of execution of flags can simplify the task of the developer (I think definition order is the best one because the developer can foresee what will happen more easily).
Solution 2 is too limiting, and solution 3 too verbose.
But please note that solutions 1 and 3 are somewhat compatible ;). We could use solution 1 and, those people wanting fine grained control could use solution 3.
We tried a version where the block had a side-effect, but then in the meaning block it looked like
(args at: #all) value.
with no immediate indication of what effect this has.
I did not understand this. My point is that nobody should call (args at: #all) value.
at all :). It seems superfluous. It should be called automatically.
Otherwise, why should I do value
?
ClapCommand new
add: ((ClapFlag id: #x) meaning: [ SomeObject new ]);
meaning: [ :args | (args at: #x) value ].
If the evaluation could be done automatically when accessing the argument:
ClapCommand new
add: ((ClapFlag id: #x) meaning: [ SomeObject new ]);
meaning: [ :args | args at: #x ].
or even simpler, because people do not need to understand the semantics of meaning
:
ClapCommand new
add: (ClapFlag id: #x);
meaning: [ :args | args at: #x ifPresent: [ SomeObject new ] ].
I don't know, tell me what you think about these ideas. I am really convinced that clap could do more :)
It could be good if when you load Clap, it also load Mocketry because of the tests
The clap
command line handler tries commands out of a set, but isn't a Clap command itself, making things like root flags and default behavior difficult.
When passing a short flag, the parser don't recognize it as a flag ans it returns a ClapMismatch.
When I have that:
sub := (ClapCommand withName: 'this') addFlag: (ClapFlag withName: 'force').
pos := ClapPositional withName: 'class'.
command := (ClapCommand withName: 'foo') addPositional: pos;addSubcommand: sub.
context := ClapContext on: #('foo' 'this' 'Object').
command matchOn: context
It returns a ClapCommandMatch with a ClapPositionalMatch
Trying commands in the playground quits the image, that's unintuitive (and annoying…)
Running the following example inside image from Defining and invoking commands yields a Instance of ClapCommand did not understand #runWith:
error
Control is passed down one level of subcommands, but not down to the innermost subcommand (intended behavior… apparently recursion is hard).
I guess the clap help help
in the demo was the 1st one describing its positional instead of the 2nd one describing its parent 🤔
printVersion
— show image/vm version, overlap with option on root commandFuel
— materialize Fuel-serialized data in the imageloadHermes
— load serialized behavior (for bootstrap)get
— install catalog projectconfig
— load Metacello using Gofermetacello
— install configuration/baselineperform
— send message to globaltest
— run unit testsst
— run code from fileeval
— run code from argumentsclean
— clean image for releaseinitializePackages
— initialize packages or re-classify selectors in protocols (but… why? 🤔)save
— rename imageWhen you have a command with one or many level of subcommands, there should be a way to ask the context if some flag is present or not.
exemple:
launcher image create fromPR 9588 --no-launch
In the example, the --no-launch
flag is specified on the create
subcommand spec.
When asking #hasFlag: #'no-launch'
to the context, it will search on the last subcommand: fromPR
and by so will not find it.
It should search from the command where it has been specified
Actual arguments are ByteString
s in the platform encoding…
When passed an undeclared flag, the parser will still accept it as a positional. Instead, any parameter that matches the general format of flags (begins with a dash) should be matched as a flag or result in a mismatch.
This requires some boilerplate in the root command (or any that wants to support both foo help
and foo --help
)
CLAP activation currently fails. Therefore cannot run any unit test on PharoCommandlineLauncher project.
MessageNotUnderstood: Pragma class>> #allNamed:
Pragma class(Object)>>doesNotUnderstand: #allNamed:
ClapContext class>>pragmaCommands
ClapContext class>>withPragmaCommands
[ | tmp2 |
tmp2 := ClapContext withPragmaCommands.
tmp2
beObeyingExits;
setStdio: Stdio;
arguments: self arguments;
executeToExit: [ :arg1 | self handleExit: arg1 ] ] in ClapCommandLineHandler>>activate in Block: [ | tmp2 |...
[ self value.
Processor terminateActive ] in BlockClosure>>newProcess in Block: [ self value....
testLauncherProcessListCommandWhenNoPharoImageRunningShouldReturnEmptyList
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.