-
Notifications
You must be signed in to change notification settings - Fork 7
[PR-739] [PR-740] Unify Context Management Under a Single config Command #709
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
059c82e
Unify Context Management Under a Single context Command
luv-bansal b9fca97
login func
luv-bansal 1889338
improvemensts and fix tests
luv-bansal 07a7ddb
better alias
luv-bansal e08d945
active to current
luv-bansal d8894f2
Unify config
luv-bansal 2baeb69
Update readme
luv-bansal 4d86b28
fix test
luv-bansal 2ac2d37
update readme
luv-bansal 12100c4
update readme
luv-bansal b177cbe
update readme
luv-bansal 67e5991
add set-context alias
luv-bansal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
srikanthbachala20 marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -1,6 +1,5 @@ | ||||||||||
import json | ||||||||||
import os | ||||||||||
import shutil | ||||||||||
import sys | ||||||||||
|
||||||||||
import click | ||||||||||
|
@@ -13,7 +12,6 @@ | |||||||||
from clarifai.utils.logging import logger | ||||||||||
|
||||||||||
|
||||||||||
# @click.group(cls=CustomMultiGroup) | ||||||||||
@click.group(cls=AliasedGroup) | ||||||||||
@click.version_option(version=__version__) | ||||||||||
@click.option('--config', default=DEFAULT_CONFIG) | ||||||||||
|
@@ -54,42 +52,71 @@ def shell_completion(shell): | |||||||||
os.system(f"_CLARIFAI_COMPLETE={shell}_source clarifai") | ||||||||||
|
||||||||||
|
||||||||||
@cli.group( | ||||||||||
['cfg'], | ||||||||||
cls=AliasedGroup, | ||||||||||
context_settings={'max_content_width': shutil.get_terminal_size().columns - 10}, | ||||||||||
srikanthbachala20 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
) | ||||||||||
@cli.group(cls=AliasedGroup) | ||||||||||
def config(): | ||||||||||
"""Manage CLI configuration""" | ||||||||||
""" | ||||||||||
Manage multiple configuration profiles (contexts). | ||||||||||
|
||||||||||
Authentication Precedence:\n | ||||||||||
1. Environment variables (e.g., `CLARIFAI_PAT`) are used first if set. | ||||||||||
2. The settings from the active context are used if no environment variables are provided.\n | ||||||||||
""" | ||||||||||
|
||||||||||
|
||||||||||
@config.command(['e']) | ||||||||||
@cli.command() | ||||||||||
@click.argument('api_url', default=DEFAULT_BASE) | ||||||||||
@click.option('--user_id', required=False, help='User ID') | ||||||||||
@click.pass_context | ||||||||||
def edit(ctx): | ||||||||||
"""Edit the configuration file""" | ||||||||||
os.system(f'{os.environ.get("EDITOR", "vi")} {ctx.obj.filename}') | ||||||||||
def login(ctx, api_url, user_id): | ||||||||||
"""Login command to set PAT and other configurations.""" | ||||||||||
from clarifai.utils.cli import validate_context_auth | ||||||||||
|
||||||||||
name = input('context name (default: "default"): ') | ||||||||||
user_id = user_id if user_id is not None else input('user id: ') | ||||||||||
pat = input_or_default( | ||||||||||
'personal access token value (default: "ENVVAR" to get out of env var rather than config): ', | ||||||||||
'ENVVAR', | ||||||||||
) | ||||||||||
|
||||||||||
@config.command(['current']) | ||||||||||
@click.option('-o', '--output-format', default='name', type=click.Choice(['name', 'json', 'yaml'])) | ||||||||||
@click.pass_context | ||||||||||
def current_context(ctx, output_format): | ||||||||||
"""Get the current context""" | ||||||||||
if output_format == 'name': | ||||||||||
print(ctx.obj.current_context) | ||||||||||
elif output_format == 'json': | ||||||||||
print(json.dumps(ctx.obj.contexts[ctx.obj.current_context].to_serializable_dict())) | ||||||||||
else: | ||||||||||
print(yaml.safe_dump(ctx.obj.contexts[ctx.obj.current_context].to_serializable_dict())) | ||||||||||
# Validate the Context Credentials | ||||||||||
validate_context_auth(pat, user_id, api_url) | ||||||||||
|
||||||||||
context = Context( | ||||||||||
name, | ||||||||||
CLARIFAI_API_BASE=api_url, | ||||||||||
CLARIFAI_USER_ID=user_id, | ||||||||||
CLARIFAI_PAT=pat, | ||||||||||
) | ||||||||||
|
||||||||||
if context.name == '': | ||||||||||
context.name = 'default' | ||||||||||
|
||||||||||
ctx.obj.contexts[context.name] = context | ||||||||||
ctx.obj.current_context = context.name | ||||||||||
|
||||||||||
ctx.obj.to_yaml() | ||||||||||
logger.info( | ||||||||||
f"Login successful and Configuration saved successfully for context '{context.name}'" | ||||||||||
) | ||||||||||
|
||||||||||
|
||||||||||
def pat_display(pat): | ||||||||||
return pat[:5] + "****" | ||||||||||
|
||||||||||
|
||||||||||
def input_or_default(prompt, default): | ||||||||||
value = input(prompt) | ||||||||||
return value if value else default | ||||||||||
|
||||||||||
|
||||||||||
@config.command(['list', 'ls']) | ||||||||||
# Context management commands under config group | ||||||||||
@config.command(aliases=['get-contexts', 'list-contexts']) | ||||||||||
@click.option( | ||||||||||
'-o', '--output-format', default='wide', type=click.Choice(['wide', 'name', 'json', 'yaml']) | ||||||||||
) | ||||||||||
@click.pass_context | ||||||||||
def get_contexts(ctx, output_format): | ||||||||||
"""Get all contexts""" | ||||||||||
"""List all available contexts.""" | ||||||||||
if output_format == 'wide': | ||||||||||
columns = { | ||||||||||
'': lambda c: '*' if c.name == ctx.obj.current_context else '', | ||||||||||
|
@@ -106,7 +133,6 @@ def get_contexts(ctx, output_format): | |||||||||
additional_columns.add(key) | ||||||||||
for key in sorted(additional_columns): | ||||||||||
columns[key] = lambda c, k=key: getattr(c, k) if hasattr(c, k) else "" | ||||||||||
luv-bansal marked this conversation as resolved.
Show resolved
Hide resolved
luv-bansal marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
formatter = TableFormatter( | ||||||||||
custom_columns=columns, | ||||||||||
) | ||||||||||
|
@@ -123,101 +149,45 @@ def get_contexts(ctx, output_format): | |||||||||
print(yaml.safe_dump(dicts)) | ||||||||||
|
||||||||||
|
||||||||||
@config.command(['use']) | ||||||||||
@click.argument('context-name', type=str) | ||||||||||
@config.command(aliases=['use-context']) | ||||||||||
@click.argument('name', type=str) | ||||||||||
@click.pass_context | ||||||||||
def use_context(ctx, context_name): | ||||||||||
"""Set the current context""" | ||||||||||
if context_name not in ctx.obj.contexts: | ||||||||||
def use_context(ctx, name): | ||||||||||
"""Set the current context.""" | ||||||||||
if name not in ctx.obj.contexts: | ||||||||||
raise click.UsageError('Context not found') | ||||||||||
ctx.obj.current_context = context_name | ||||||||||
ctx.obj.current_context = name | ||||||||||
ctx.obj.to_yaml() | ||||||||||
print(f'Set {context_name} as the current context') | ||||||||||
|
||||||||||
|
||||||||||
@config.command(['cat']) | ||||||||||
@click.option('-o', '--output-format', default='yaml', type=click.Choice(['yaml', 'json'])) | ||||||||||
@click.pass_obj | ||||||||||
def dump(ctx_obj, output_format): | ||||||||||
"""Dump the configuration to stdout""" | ||||||||||
if output_format == 'yaml': | ||||||||||
yaml.safe_dump(ctx_obj.to_dict(), sys.stdout) | ||||||||||
else: | ||||||||||
json.dump(ctx_obj.to_dict(), sys.stdout, indent=2) | ||||||||||
|
||||||||||
|
||||||||||
@config.command(['cat']) | ||||||||||
@click.pass_obj | ||||||||||
def env(ctx_obj): | ||||||||||
"""Print env vars. Use: eval "$(clarifai config env)" """ | ||||||||||
ctx_obj.current.print_env_vars() | ||||||||||
print(f'Set {name} as the current context') | ||||||||||
luv-bansal marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
|
||||||||||
@cli.command() | ||||||||||
@click.argument('api_url', default=DEFAULT_BASE) | ||||||||||
@click.option('--user_id', required=False, help='User ID') | ||||||||||
@config.command(aliases=['current-context']) | ||||||||||
@click.option('-o', '--output-format', default='name', type=click.Choice(['name', 'json', 'yaml'])) | ||||||||||
@click.pass_context | ||||||||||
def login(ctx, api_url, user_id): | ||||||||||
"""Login command to set PAT and other configurations.""" | ||||||||||
from clarifai.utils.cli import validate_context_auth | ||||||||||
|
||||||||||
name = input('context name (default: "default"): ') | ||||||||||
user_id = user_id if user_id is not None else input('user id: ') | ||||||||||
pat = input_or_default( | ||||||||||
'personal access token value (default: "ENVVAR" to get our of env var rather than config): ', | ||||||||||
'ENVVAR', | ||||||||||
) | ||||||||||
|
||||||||||
# Validate the Context Credentials | ||||||||||
validate_context_auth(pat, user_id, api_url) | ||||||||||
|
||||||||||
context = Context( | ||||||||||
name, | ||||||||||
CLARIFAI_API_BASE=api_url, | ||||||||||
CLARIFAI_USER_ID=user_id, | ||||||||||
CLARIFAI_PAT=pat, | ||||||||||
) | ||||||||||
|
||||||||||
if context.name == '': | ||||||||||
context.name = 'default' | ||||||||||
|
||||||||||
ctx.obj.contexts[context.name] = context | ||||||||||
ctx.obj.current_context = context.name | ||||||||||
|
||||||||||
ctx.obj.to_yaml() | ||||||||||
logger.info( | ||||||||||
f"Login successful and Configuration saved successfully for context '{context.name}'" | ||||||||||
) | ||||||||||
|
||||||||||
|
||||||||||
@cli.group(cls=AliasedGroup) | ||||||||||
def context(): | ||||||||||
"""Manage contexts""" | ||||||||||
|
||||||||||
|
||||||||||
def pat_display(pat): | ||||||||||
return pat[:5] + "****" | ||||||||||
|
||||||||||
|
||||||||||
def input_or_default(prompt, default): | ||||||||||
value = input(prompt) | ||||||||||
return value if value else default | ||||||||||
def current_context(ctx, output_format): | ||||||||||
"""Show the current context's details.""" | ||||||||||
if output_format == 'name': | ||||||||||
print(ctx.obj.current_context) | ||||||||||
elif output_format == 'json': | ||||||||||
print(json.dumps(ctx.obj.contexts[ctx.obj.current_context].to_serializable_dict())) | ||||||||||
else: | ||||||||||
print(yaml.safe_dump(ctx.obj.contexts[ctx.obj.current_context].to_serializable_dict())) | ||||||||||
|
||||||||||
|
||||||||||
@context.command() | ||||||||||
@config.command(aliases=['create-context', 'set-context']) | ||||||||||
@click.argument('name') | ||||||||||
@click.option('--user-id', required=False, help='User ID') | ||||||||||
@click.option('--base-url', required=False, help='Base URL') | ||||||||||
@click.option('--pat', required=False, help='Personal access token') | ||||||||||
@click.pass_context | ||||||||||
def create( | ||||||||||
def create_context( | ||||||||||
ctx, | ||||||||||
name, | ||||||||||
user_id=None, | ||||||||||
base_url=None, | ||||||||||
pat=None, | ||||||||||
): | ||||||||||
"""Create a new context""" | ||||||||||
"""Create a new context.""" | ||||||||||
from clarifai.utils.cli import validate_context_auth | ||||||||||
|
||||||||||
if name in ctx.obj.contexts: | ||||||||||
|
@@ -234,22 +204,28 @@ def create( | |||||||||
'personal access token value (default: "ENVVAR" to get our of env var rather than config): ', | ||||||||||
'ENVVAR', | ||||||||||
) | ||||||||||
|
||||||||||
# Validate the Context Credentials | ||||||||||
validate_context_auth(pat, user_id, base_url) | ||||||||||
|
||||||||||
context = Context(name, CLARIFAI_USER_ID=user_id, CLARIFAI_API_BASE=base_url, CLARIFAI_PAT=pat) | ||||||||||
ctx.obj.contexts[context.name] = context | ||||||||||
ctx.obj.to_yaml() | ||||||||||
logger.info(f"Context '{name}' created successfully") | ||||||||||
|
||||||||||
|
||||||||||
# write a click command to delete a context | ||||||||||
@context.command(['rm']) | ||||||||||
@config.command(aliases=['e']) | ||||||||||
@click.pass_context | ||||||||||
def edit( | ||||||||||
ctx, | ||||||||||
): | ||||||||||
"""Open the configuration file for editing.""" | ||||||||||
# For now, just open the config file (not per-context) | ||||||||||
os.system(f'{os.environ.get("EDITOR", "vi")} {ctx.obj.filename}') | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||
|
||||||||||
|
||||||||||
@config.command(aliases=['delete-context']) | ||||||||||
@click.argument('name') | ||||||||||
@click.pass_context | ||||||||||
def delete(ctx, name): | ||||||||||
"""Delete a context""" | ||||||||||
def delete_context(ctx, name): | ||||||||||
"""Delete a context.""" | ||||||||||
if name not in ctx.obj.contexts: | ||||||||||
print(f'{name} is not a valid context') | ||||||||||
sys.exit(1) | ||||||||||
|
@@ -258,16 +234,29 @@ def delete(ctx, name): | |||||||||
print(f'{name} deleted') | ||||||||||
|
||||||||||
|
||||||||||
@context.command() | ||||||||||
@click.argument('name', type=str) | ||||||||||
@config.command(aliases=['get-env']) | ||||||||||
@click.pass_context | ||||||||||
def use(ctx, name): | ||||||||||
"""Set the current context""" | ||||||||||
if name not in ctx.obj.contexts: | ||||||||||
raise click.UsageError('Context not found') | ||||||||||
ctx.obj.current_context = name | ||||||||||
ctx.obj.to_yaml() | ||||||||||
print(f'Set {name} as the current context') | ||||||||||
def env(ctx): | ||||||||||
"""Print env vars for the active context.""" | ||||||||||
ctx.obj.current.print_env_vars() | ||||||||||
|
||||||||||
|
||||||||||
@config.command(aliases=['show']) | ||||||||||
@click.option('-o', '--output-format', default='yaml', type=click.Choice(['json', 'yaml'])) | ||||||||||
@click.pass_context | ||||||||||
def view(ctx, output_format): | ||||||||||
"""Display the current configuration.""" | ||||||||||
config_dict = { | ||||||||||
'current-context': ctx.obj.current_context, | ||||||||||
'contexts': { | ||||||||||
name: context.to_serializable_dict() for name, context in ctx.obj.contexts.items() | ||||||||||
}, | ||||||||||
} | ||||||||||
|
||||||||||
if output_format == 'json': | ||||||||||
print(json.dumps(config_dict, indent=2)) | ||||||||||
else: | ||||||||||
print(yaml.safe_dump(config_dict, default_flow_style=False)) | ||||||||||
|
||||||||||
|
||||||||||
@cli.command() | ||||||||||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 be
set-context
according tokubectl
commands?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.
hm, I'm not sure because Matt also used
create-context
in the initial setup instead ofset-context
. we could add alias forset-context