gh70 - xts demo - #72
Conversation
|
I have read the CLA Document and I hereby sign the CLA zghp seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. |
There was a problem hiding this comment.
Pull request overview
Adds an xts demo built-in command intended to walk first-time users through alias setup and running a sample command, along with test coverage and README updates.
Changes:
- Adds a
demobuilt-in path inXTS._parse_first_arg()and an interactive_run_demo()flow. - Adds a pytest that exercises the demo flow via monkeypatching and a fake
YamlRunner. - Documents the new
xts democommand in the README.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
src/xts_core/xts.py |
Adds demo built-in handling, interactive demo flow, and a YamlRunner-compatibility fallback. |
test/test_xts_all_cases.py |
Adds an end-to-end-ish test for xts demo and adjusts import path handling. |
README.md |
Documents the new interactive demo command. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # * If not stated otherwise in this file or this component's LICENSE file the | ||
| # * following copyright and licenses apply: | ||
| # * | ||
| # * Copyright 2024 RDK Management | ||
| # * | ||
| # * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # * you may not use this file except in compliance with the License. | ||
|
|
||
| # * You may obtain a copy of the License at |
| alias_name = remaining_args[0] | ||
|
|
||
| if alias_name == 'demo': | ||
| self._run_demo() | ||
| raise SystemExit(0) | ||
|
|
||
| resolved_xts_path = xts_alias.resolve_alias_to_xts_path(alias_name) | ||
|
|
| package_root = Path(__file__).resolve().parents[2] | ||
| example_config = package_root / 'examples' / 'hello_world.xts' | ||
| if example_config.exists(): | ||
| return str(example_config) | ||
|
|
||
| alt_example = Path.cwd() / 'examples' / 'hello_world.xts' | ||
| if alt_example.exists(): | ||
| return str(alt_example) | ||
|
|
||
| error( | ||
| 'Could not locate demo example config. Ensure examples/hello_world.xts exists.' | ||
| ) | ||
|
|
There was a problem hiding this comment.
First part of the demo should be getting the user to run xts alias add --name example https://raw.githubusercontent.com/rdkcentral/xts_core/refs/heads/master/examples/hello_world.xts. So there should be no need for xts to find the example file.
| def _collect_command_paths(self, section: dict, prefix: list[str] | None = None) -> list[list[str]]: | ||
| """Recursively collect leaf command paths from a command section.""" | ||
| prefix = prefix or [] | ||
| paths: list[list[str]] = [] | ||
|
|
||
| for key, value in section.items(): | ||
| if not isinstance(value, dict): | ||
| continue | ||
|
|
||
| if 'command' in value: | ||
| paths.append(prefix + [key]) | ||
|
|
||
| paths.extend(self._collect_command_paths(value, prefix + [key])) | ||
|
|
||
| return paths |
| info('Welcome to the XTS interactive demo!') | ||
| info('This demo will add an alias, show the alias list, and run all example commands.') | ||
| print() |
| help_command = 'xts --alias --help' | ||
| add_command = f'xts --alias --add {example_config_path} --name {alias_name}' | ||
| list_command = 'xts --alias --list' | ||
| run_command = f'xts {alias_name} run hello_world' | ||
| remove_command = f'xts --alias --remove {alias_name}' | ||
|
|
| print() | ||
| info('Section 5: Remove the demo alias.') | ||
| info(f' Command: {remove_command}') | ||
| input('Press Enter to execute this command and continue... ') | ||
| try: | ||
| xts_alias.run_alias_builtin(['--remove', alias_name]) | ||
| except Exception as e: | ||
| error(f'Failed to remove demo alias: {e}') | ||
|
|
||
| print() | ||
| info('Demo finished. You can now add your own aliases with xts --alias --add <path> --name <alias>.') |
There was a problem hiding this comment.
Holy huge function Batman! Can we pull demo out of here and into it's own file with functions we can import and use in here.
| assert 'Command: xts demo-example run hello_world' in output | ||
| assert 'Section 5: Remove the demo alias.' in output | ||
| assert 'Command: xts --alias --remove demo-example' in output | ||
| assert 'Section 6: Refresh the demo alias.' in output | ||
| assert 'Command: xts --alias --refresh demo-example' in output | ||
| assert 'demo-example ->' in output | ||
| assert 'Removed alias: demo-example' in output | ||
| assert 'Demo finished. You can now add your own aliases' in output |
| A new interactive demo command is available to guide first-time users through alias setup and execution: | ||
|
|
||
| ```sh | ||
| xts demo | ||
| ``` | ||
|
|
||
| It will add a sample alias from `examples/hello_world.xts`, list the alias, and execute the example `hello_world` command. | ||
|
|
|
b'## WARNING: A Blackduck scan failure has been waived A prior failure has been upvoted
|
| try: | ||
| yaml_runner = YamlRunner( | ||
| self._command_sections, | ||
| program='xts', | ||
| hierarchical=True, | ||
| fail_fast=True, | ||
| parser_class=XTSArgumentParser | ||
| ) | ||
| except TypeError: | ||
| yaml_runner = YamlRunner( | ||
| self._command_sections, | ||
| program='xts', | ||
| hierarchical=True, | ||
| fail_fast=True | ||
| ) | ||
|
|
There was a problem hiding this comment.
What is this all about? Is the TypeError just because you hadn't got the XTSArgumentParser imported?
| package_root = Path(__file__).resolve().parents[2] | ||
| example_config = package_root / 'examples' / 'hello_world.xts' | ||
| if example_config.exists(): | ||
| return str(example_config) | ||
|
|
||
| alt_example = Path.cwd() / 'examples' / 'hello_world.xts' | ||
| if alt_example.exists(): | ||
| return str(alt_example) | ||
|
|
||
| error( | ||
| 'Could not locate demo example config. Ensure examples/hello_world.xts exists.' | ||
| ) | ||
|
|
There was a problem hiding this comment.
First part of the demo should be getting the user to run xts alias add --name example https://raw.githubusercontent.com/rdkcentral/xts_core/refs/heads/master/examples/hello_world.xts. So there should be no need for xts to find the example file.
| print() | ||
| info('Section 5: Remove the demo alias.') | ||
| info(f' Command: {remove_command}') | ||
| input('Press Enter to execute this command and continue... ') | ||
| try: | ||
| xts_alias.run_alias_builtin(['--remove', alias_name]) | ||
| except Exception as e: | ||
| error(f'Failed to remove demo alias: {e}') | ||
|
|
||
| print() | ||
| info('Demo finished. You can now add your own aliases with xts --alias --add <path> --name <alias>.') |
There was a problem hiding this comment.
Holy huge function Batman! Can we pull demo out of here and into it's own file with functions we can import and use in here.
6c24028 to
e07a989
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (12)
src/xts_core/xts.py:8
- The Apache 2.0 license header was partially removed and replaced by whitespace (the copyright + license grant lines are missing). This can create compliance/attribution issues and should be restored to the prior header format.
# * If not stated otherwise in this file or this component's LICENSE file the
# * following copyright and licenses apply:
# *
# * You may obtain a copy of the License at
src/xts_core/xts.py:403
_find_demo_example_config()is currently unused by the demo implementation (the demo uses a public URL), and the only reference is in the new test via monkeypatch. This leaves dead code in the CLI and makes the test patching misleading. Either wire this into the demo flow, or delete it and update the test accordingly.
def _find_demo_example_config(self) -> str:
"""Locate the example XTS config used by the interactive demo."""
package_root = Path(__file__).resolve().parents[2]
example_config = package_root / 'examples' / 'hello_world.xts'
if example_config.exists():
return str(example_config)
src/xts_core/xts.py:417
_collect_command_paths()is newly added but never called (no references outside the method itself). This adds maintenance surface without functionality; remove it until it’s needed.
def _collect_command_paths(self, section: dict, prefix: list[str] | None = None) -> list[list[str]]:
"""Recursively collect leaf command paths from a command section."""
prefix = prefix or []
paths: list[list[str]] = []
test/test_xts_all_cases.py:131
- The asserted demo output strings don’t match the current demo implementation (exclamation in welcome message, different section text/numbering,
xts alias ...commands, alias nameexample, and no refresh section). These assertions will fail even if the demo works.
assert 'Welcome to the XTS interactive demo' in output
assert 'Section 1: Alias help command.' in output
assert 'Command: xts --alias --help' in output
assert 'Section 2: Add a demo alias for the example file.' in output
assert 'Command: xts --alias --add' in output
assert 'Section 3: List available aliases.' in output
assert 'Command: xts --alias --list' in output
assert 'Section 4: Run the demo alias command.' in output
assert 'Command: xts demo-example run hello_world' in output
assert 'Section 5: Remove the demo alias.' in output
assert 'Command: xts --alias --remove demo-example' in output
assert 'Section 6: Refresh the demo alias.' in output
assert 'Command: xts --alias --refresh demo-example' in output
assert 'demo-example ->' in output
assert 'Removed alias: demo-example' in output
assert 'Demo finished. You can now add your own aliases' in output
src/xts_core/demo.py:100
- This
excepthandler intends to recover, bututils.error()raisesSystemExit(1)and will exit the demo. Use a non-exiting logger (e.g.,warning()/info()) if you want the demo to proceed after a command failure.
except SystemExit:
pass
except Exception as exc:
error(f'Failed to run demo alias command: {exc}')
src/xts_core/demo.py:109
- Same issue as earlier sections:
utils.error()raisesSystemExit(1), so this error path exits the demo immediately instead of continuing to the final message. Use a non-exiting logger if the demo should continue.
try:
run_demo_alias_builtin(['remove', alias_name])
except Exception as exc:
error(f'Failed to remove demo alias: {exc}')
src/xts_core/xts.py:38
jsonis imported but not used anywhere in this module (only referenced in a docstring path). Removing it avoids lint noise and keeps imports minimal.
This issue also appears on line 398 of the same file.
import json
from pathlib import Path
test/test_xts_all_cases.py:108
- This test patches functions/classes that the demo code path doesn’t use (the demo logic lives in
xts_core.demo, notxts_core.xts, and it doesn’t calladd_alias_from_input/refresh_alias). As written, the test is likely to hit real alias logic and won’t stub out theYamlRunnerused by the demo.
monkeypatch.setattr('xts_core.xts_alias.add_alias_from_input', lambda path, name: [(name, str(path))])
monkeypatch.setattr('xts_core.xts_alias.list_aliases', lambda: None)
monkeypatch.setattr('xts_core.xts_alias.refresh_alias', lambda name: (name, str(demo_file)))
monkeypatch.setattr('xts_core.xts_alias.remove_alias', lambda name: True)
class FakeRunner:
def __init__(self, *args, **kwargs):
pass
def run(self, args):
assert args == ['run', 'hello_world']
return (None, None, [0])
monkeypatch.setattr('xts_core.xts.YamlRunner', FakeRunner)
monkeypatch.setattr('builtins.input', lambda prompt='': '')
monkeypatch.setattr('xts_core.xts.XTS._find_demo_example_config', lambda self: str(demo_file))
README.md:147
- README says the demo adds an alias from
examples/hello_world.xts, but the implementation uses a public raw GitHub URL. Update this sentence to match actual behavior so users aren’t misled.
It will add a sample alias from `examples/hello_world.xts`, list the alias, and execute the example `hello_world` command.
src/xts_core/xts.py:375
- The
try:block after thematchinrun()is dead code: everymatchbranch raisesSystemExit(directly or via_run_yaml_runner()/_run_validate_command()), so this block will never run. If it did run, it would callyaml_runner.run(args)whereargsis a dict (fromvars(args)), not an argv list, so it would likely fail. Please remove this duplicated runner logic to avoid confusion.
try:
try:
yaml_runner = YamlRunner(
self._command_sections,
program='xts',
src/xts_core/demo.py:60
utils.error()raisesSystemExit(1), so calling it inside thisexceptblock will terminate the demo immediately (the code below will never run). If the intent is to continue the demo after a failure, use a non-exiting logger (e.g.,warning()/info()) or catchSystemExitexplicitly.
This issue also appears in the following locations of the same file:
- line 97
- line 106
try:
run_demo_alias_builtin(['add', '--name', alias_name, example_url])
except Exception as exc:
error(f'Failed to add demo alias: {exc}')
src/xts_core/demo.py:70
utils.error()raisesSystemExit(1), so thereturnimmediately after this call is unreachable. Decide whether demo resolution failure should abort (then remove thereturn) or should be non-fatal (then replaceerror(...)with a non-exiting logger and keepreturn).
resolved_xts_path = xts_alias.resolve_alias_to_xts_path(alias_name)
if resolved_xts_path is None:
error(f'Failed to resolve alias for demo execution: {alias_name}')
return
No description provided.