Background
Following examples in the Click documentation (specifically custom multi commands, multi command pipelines and managing resources) I've written a CLI application similar to the Image Pipeline example only it operates on 3D mesh scenes via the FBX SDK instead of images.
Abstracting that particular detail aside, I am struggling to understand how and when Click's parsing of the command line occurs. The TLDR version of my problem is that command line usage errors seem to only be surfaced after the context exits and importantly, after any context managed resources are closed. Ideally I would like to establish if the command line is valid before even entering the context manager, or at least be able to react to a usage error before the manager exits.
Minimal example as follows:
- a chained multicommand is setup with options for 
inputandoutputfilepaths. - a context manager is used to load/save the data (for this example I'm just using a 
dictandjsonfile as standins for 3D scene data and FBX file). - this context manager is used with Clicks 
ctx.with_resourcemethod and stored on the contexts user object. - subcommands are all passed the context and have their own arbitrary options (for this example they all just edit 
dictvalues and are in the same script, in the actual implementation they are implemented as plugins as per the custom multi command example) - all subcommands are generators and yield the context, these generators are consumed by the 
processfunction run as a result callback by Click. (similar to the multi command pipeline example) 
Example code
import click
import json
@click.command
@click.option("-t", "value", type=float)
@click.pass_context
def translate(ctx, value):
    click.echo(f"modifying: 'translate' by {value}")
    ctx.obj.data["translate"] += value
    yield ctx
@click.command
@click.option("-r", "value", type=float)
@click.pass_context
def rotate(ctx, value):
    click.echo(f"modifying: 'rotate' by {value}")
    ctx.obj.data["rotate"] += value
    yield ctx
@click.command
@click.option("-s", "value", type=float)
@click.pass_context
def scale(ctx, value):
    click.echo(f"modifying: 'scale' by {value}")
    ctx.obj.data["scale"] += value
    yield ctx
@click.command
@click.pass_context
def report(ctx):
    click.echo(f"object data: {ctx.obj.data}")
    click.echo(f"object in: {ctx.obj.infile}")
    click.echo(f"object out: {ctx.obj.outfile}")
    yield ctx
class EditModelCLI(click.MultiCommand):
    def list_commands(self, ctx):
        return ["translate", "rotate", "scale", "report"]
    def get_command(self, ctx, name):
        return globals()[name]
class FileResource:
    def __init__(self, infile, outfile=None):
        self.infile = infile
        self.outfile = outfile
        self._dict = {}
    @property
    def data(self):
        return self._dict
    def __enter__(self):
        if self.infile:
            with open(self.infile) as f:
                click.echo(f"loading {self.infile}")
                self._dict = json.load(f)
                return self
    def __exit__(self, exc_type, exc_value, tb):
        if self.outfile:
            with open(self.outfile, "w") as f:
                click.echo(f"saving {self.outfile}")
                json.dump(self._dict, f)
@click.option("-i", "--input", type=click.Path(exists=True, dir_okay=False))
@click.option("-o", "--output", type=click.Path(), default=None)
@click.group(cls=EditModelCLI, chain=True, no_args_is_help=True)
@click.pass_context
def main(ctx, input, output):
    click.echo("starting process")
    ctx.obj = ctx.with_resource(FileResource(input, output))
@main.result_callback()
def process(subcommands, **kwargs):
    for cmd in subcommands:
        result = next(cmd)
        click.echo(f"processed ... {result.info_name}")
if __name__ == "__main__":
    click.echo("called from commandline")
    main()
Testing
So this gives us a cli where we can call:
> python -m click_test.py -i model_in.json -o model_out.json translate -t 1.0 scale -s 0.5
which reads in - model_in.json
{"translate": 1.0, "rotate": 1.0, "scale": 1.0}
and writes out - model_out.json
{"translate": 2.0, "rotate": 1.0, "scale": 1.5}
Working as designed so far, model_out.json is either created or modified if it already existed.
However any command line syntax errors (at the subcommand level) will still result in an "output" file being written out, the managed resource is still opened and closed. So calling:
> python -m click_test.py -i model_in.json -o model_out.json translate -t 1.0 scale -foobar 0.5
with an error in the options to scale will still write out - model_out.json
No changes are made to the file, we've not processed the translate subcommand.
I am trying to determine how or where I can catch any subcommand usage errors, before the context exits, so that I can access the FileResource and prevent a save.
eg ctx.obj.outfile = None would achieve this, I just don't know where I can detect usage errors in order to call it.
                        
You can test the program exit code in your resource handler
__exit__()like:Full Example:
Test code:
Results: