av-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
av_cli/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
av_cli/main.py ADDED
@@ -0,0 +1,147 @@
1
+ import inspect
2
+ import json
3
+ import os
4
+ import sys
5
+ from typing import Union, get_type_hints
6
+
7
+ import click
8
+
9
+ from av_cli import __version__
10
+
11
+
12
+ def _python_type_to_click(param_type):
13
+ """Map a Python type hint to a Click type."""
14
+ if param_type == bool or param_type == 'bool':
15
+ return bool
16
+ if param_type == int or param_type == 'int':
17
+ return click.INT
18
+ if param_type == float or param_type == 'float':
19
+ return click.FLOAT
20
+ # Handle Optional[X]
21
+ if hasattr(param_type, '__origin__') and param_type.__origin__ is Union:
22
+ args = param_type.__args__
23
+ if len(args) == 2 and type(None) in args:
24
+ inner = args[0] if args[1] is type(None) else args[1]
25
+ return _python_type_to_click(inner)
26
+ return click.STRING
27
+
28
+
29
+ def _make_tool_command(func, tool_name):
30
+ """Build a Click command for a single tool function."""
31
+ from av_api.registry import extract_description
32
+
33
+ sig = inspect.signature(func)
34
+ try:
35
+ hints = get_type_hints(func)
36
+ except Exception:
37
+ hints = {}
38
+
39
+ # Build short option flags, skipping conflicts with -h (help)
40
+ used_shorts = {'h'}
41
+ short_map = {}
42
+ for pname in sig.parameters:
43
+ for ch in pname.lower():
44
+ if ch.isalpha() and ch not in used_shorts:
45
+ used_shorts.add(ch)
46
+ short_map[pname] = f'-{ch}'
47
+ break
48
+
49
+ params = []
50
+ for pname, param in sig.parameters.items():
51
+ ptype = hints.get(pname, str)
52
+ click_type = _python_type_to_click(ptype)
53
+ short = short_map.get(pname)
54
+
55
+ if click_type is bool:
56
+ params.append(
57
+ click.Option(
58
+ [f'--{pname}/--no-{pname}'],
59
+ default=param.default if param.default is not inspect.Parameter.empty else False,
60
+ help=f'{pname} flag',
61
+ )
62
+ )
63
+ else:
64
+ required = param.default is inspect.Parameter.empty
65
+ decls = [f'--{pname}']
66
+ if short:
67
+ decls.append(short)
68
+ params.append(
69
+ click.Option(
70
+ decls,
71
+ type=click_type,
72
+ required=required,
73
+ default=None if required else param.default,
74
+ help=pname,
75
+ )
76
+ )
77
+
78
+ def callback(**kwargs):
79
+ from av_api.context import set_api_key
80
+
81
+ ctx = click.get_current_context()
82
+ api_key = ctx.obj.get('api_key') or os.getenv('ALPHA_VANTAGE_API_KEY')
83
+ if not api_key:
84
+ click.echo('Error: API key required. Use --api-key or set ALPHA_VANTAGE_API_KEY.', err=True)
85
+ sys.exit(1)
86
+
87
+ set_api_key(api_key)
88
+ result = func(**kwargs)
89
+
90
+ if isinstance(result, str):
91
+ click.echo(result)
92
+ else:
93
+ click.echo(json.dumps(result, indent=2, default=str))
94
+
95
+ cmd = click.Command(
96
+ name=tool_name,
97
+ callback=callback,
98
+ params=params,
99
+ help=extract_description(func),
100
+ )
101
+ return cmd
102
+
103
+
104
+ class ToolGroup(click.Group):
105
+ """Click group that lazily loads tool commands from the registry."""
106
+
107
+ def __init__(self, *args, **kwargs):
108
+ super().__init__(*args, **kwargs)
109
+ self._tools_loaded = False
110
+
111
+ def _load_tools(self):
112
+ if self._tools_loaded:
113
+ return
114
+ self._tools_loaded = True
115
+
116
+ from av_api.registry import _all_tools_registry, ensure_tools_loaded
117
+
118
+ ensure_tools_loaded()
119
+
120
+ for func in _all_tools_registry:
121
+ name = func.__name__
122
+ cmd = _make_tool_command(func, name)
123
+ self.add_command(cmd)
124
+
125
+ def list_commands(self, ctx):
126
+ self._load_tools()
127
+ return super().list_commands(ctx)
128
+
129
+ def get_command(self, ctx, cmd_name):
130
+ self._load_tools()
131
+ return super().get_command(ctx, cmd_name.lower())
132
+
133
+
134
+ @click.group(cls=ToolGroup, context_settings=dict(help_option_names=['-h', '--help']))
135
+ @click.version_option(version=__version__, prog_name="av-cli")
136
+ @click.option('--api-key', '-k', envvar='ALPHA_VANTAGE_API_KEY', help='Alpha Vantage API key')
137
+ @click.option('--verbose', '-v', is_flag=True, help='Enable verbose logging')
138
+ @click.pass_context
139
+ def cli(ctx, api_key, verbose):
140
+ """Alpha Vantage CLI - direct access to all Alpha Vantage API tools."""
141
+ ctx.ensure_object(dict)
142
+ ctx.obj['api_key'] = api_key
143
+ ctx.obj['verbose'] = verbose
144
+
145
+
146
+ if __name__ == "__main__":
147
+ cli()
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: av-cli
3
+ Version: 0.1.0
4
+ Summary: CLI wrapper for Alpha Vantage APIs
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: av-api
7
+ Requires-Dist: click
8
+ Requires-Dist: httpx
9
+ Requires-Dist: loguru
@@ -0,0 +1,6 @@
1
+ av_cli/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
2
+ av_cli/main.py,sha256=wptn8ltf31hmQbG8INdWlr_svXPi0W_BOTo46wFui0w,4493
3
+ av_cli-0.1.0.dist-info/METADATA,sha256=pmbhbPGAFq2OVowclAf3_7-4CFqFuQuGgC1rzeSMFPs,204
4
+ av_cli-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
5
+ av_cli-0.1.0.dist-info/entry_points.txt,sha256=8WVlaMVHQJB6WPTF9mjsUSWaFYzraCR23y6nEI7zvYw,43
6
+ av_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ av-cli = av_cli.main:cli