diff --git a/setup.py b/setup.py index 5ca1394..2c6e3fe 100644 --- a/setup.py +++ b/setup.py @@ -75,7 +75,7 @@ setup( tests_require=extra_dependencies['tests'], extras_require=extra_dependencies, entry_points={ - 'console_scripts': ['quilla = quilla:run'], + 'console_scripts': ['quilla = quilla:main'], 'pytest11': [ 'quilla = pytest_quilla' ] diff --git a/src/pytest_quilla/pytest_classes.py b/src/pytest_quilla/pytest_classes.py index 93fce7d..34a0b0a 100644 --- a/src/pytest_quilla/pytest_classes.py +++ b/src/pytest_quilla/pytest_classes.py @@ -67,7 +67,7 @@ class QuillaItem(pytest.Item): data retrieved from the JSON file. ''' ctx = setup_context( - [*self.config.getoption('--quilla-opts').split(), ''], + [*self.config.getoption('--quilla-opts').split()], str(self.config.rootpath), recreate_context=True ) diff --git a/src/quilla/__init__.py b/src/quilla/__init__.py index 230665e..9e932bc 100644 --- a/src/quilla/__init__.py +++ b/src/quilla/__init__.py @@ -30,7 +30,7 @@ def make_parser() -> argparse.ArgumentParser: # pragma: no cover ''' parser = argparse.ArgumentParser( prog='quilla', - usage='%(prog)s [options] [-f] JSON', + # usage='%(prog)s [options] [-f] JSON', description=''' Program to provide a report of UI validations given a json representation of the validations or given the filename containing a json document describing @@ -44,16 +44,22 @@ def make_parser() -> argparse.ArgumentParser: # pragma: no cover help='Prints the version of the software and quits' ) - parser.add_argument( + data_group = parser.add_mutually_exclusive_group() + + data_group.add_argument( '-f', '--file', - dest='is_file', - action='store_true', - help='Whether to treat the argument as raw json or as a file', + dest='file_name', + action='store', + help='A file containing a Quilla test', + default=None, ) - parser.add_argument( - 'json', - help='The json file name or raw json string', + data_group.add_argument( + '-r', + '--raw', + action='store', + help='A Quilla test passed in as a raw string', + default=None, ) config_group = parser.add_argument_group(title='Configuration options') @@ -129,6 +135,9 @@ def make_parser() -> argparse.ArgumentParser: # pragma: no cover default=0 ) + # Sets the application handler, i.e. the function that runs when ctx.run() is called + parser.set_defaults(handler=run) + return parser @@ -277,10 +286,10 @@ def setup_context( if not parsed_args.definitions: parsed_args.definitions = [] - if not parsed_args.is_file: - json_data = parsed_args.json + if parsed_args.file_name is None: + json_data = parsed_args.raw else: - with open(parsed_args.json) as f: + with open(parsed_args.file_name) as f: json_data = f.read() logger.debug('Initializing context object') @@ -291,13 +300,14 @@ def setup_context( parsed_args.drivers_path, parsed_args.pretty, json_data, - parsed_args.is_file, + parsed_args.file_name is not None, parsed_args.no_sandbox, parsed_args.definitions, logger=logger, run_id=parsed_args.run_id, indent=parsed_args.indent, update_baseline=parsed_args.update_baseline, + args=parsed_args, recreate_context=recreate_context, ) @@ -307,15 +317,14 @@ def setup_context( return ctx -def run(): +def run(ctx: Context): ''' - Creates the parser object, parses the command-line arguments, and runs them, finishing with the - appropriate exit code. + Runs all reports and prints to stdout while providing the proper + exit code + + Args: + ctx: The application context ''' - ctx = setup_context(sys.argv[1:]) - - ctx.logger.debug('Context setup complete, running the "execute" function') - reports = execute(ctx) ctx.logger.debug('Finished generating reports') @@ -339,5 +348,15 @@ def run(): sys.exit(exit_code) +def main(): + ''' + Creates the context and parses all arguments, then runs the default handler function + ''' + ctx = setup_context(sys.argv[1:]) + ctx.logger.debug('Context setup complete, running the Quilla handler') + + ctx.run() + + if __name__ == '__main__': - run() + main() diff --git a/src/quilla/__main__.py b/src/quilla/__main__.py index e8611f6..de8e148 100644 --- a/src/quilla/__main__.py +++ b/src/quilla/__main__.py @@ -2,4 +2,4 @@ import quilla # pragma: no cover if __name__ == '__main__': # pragma: no cover - quilla.run() + quilla.main() diff --git a/src/quilla/ctx.py b/src/quilla/ctx.py index 56f60d0..26b8acd 100644 --- a/src/quilla/ctx.py +++ b/src/quilla/ctx.py @@ -15,6 +15,7 @@ from logging import ( ) import json import uuid +from argparse import Namespace from pluggy import PluginManager import pydeepmerge as pdm @@ -41,6 +42,7 @@ class Context(DriverHolder): no_sandbox: Whether to pass the '--no-sandbox' arg to Chrome and Edge logger: An optional configured logger instance. run_id: A string that uniquely identifies the run of Quilla. + args: The Namespace object that parsed related arguments, if applicable update_baseline: Whether the VisualParity baselines should be updated or not @@ -59,6 +61,7 @@ class Context(DriverHolder): one with the default logger. run_id: A string that uniquely identifies the run of Quilla. pretty_print_indent: How many spaces to use for indentation when pretty-printing the output + args: The Namespace object that parsed related arguments, if applicable update_baseline: Whether the VisualParity baselines should be updated or not ''' default_context: Optional['Context'] = None @@ -85,6 +88,7 @@ class Context(DriverHolder): logger: Optional[Logger] = None, run_id: Optional[str] = None, indent: int = 4, + args: Optional[Namespace] = None, update_baseline: bool = False, ): super().__init__() @@ -96,6 +100,7 @@ class Context(DriverHolder): self.is_file = is_file self.no_sandbox = no_sandbox self.pretty_print_indent = indent + self.args = args path = Path(drivers_path) if logger is None: @@ -115,6 +120,17 @@ class Context(DriverHolder): self._context_data: Dict[str, dict] = {'Validation': {}, 'Outputs': {}, 'Definitions': {}} self._load_definition_files(definitions) + def run(self): + ''' + Runs Quilla, assuming a proper Namespace object with a handler has been passed in. + + This function is not guaranteed to do anything, and is just a passthrough to allow + the proper parser handlers to execute, as described in the documentation for ArgParse + https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_subparsers + ''' + if self.args is not None: + self.args.handler(self) + @property def outputs(self) -> dict: ''' @@ -355,6 +371,7 @@ def get_default_context( logger: Optional[Logger] = None, run_id: Optional[str] = None, indent: int = 4, + args: Optional[Namespace] = None, update_baseline: bool = False, ) -> Context: ''' @@ -398,6 +415,7 @@ def get_default_context( logger, run_id, indent, - update_baseline + args, + update_baseline, ) return Context.default_context