mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 04:31:13 +00:00
[BugFix] openbb-cli: Make Codegen Names That Start With a Digit or Are Keywords Valid (#7800)
* [BugFix] `openbb-cli`: Prefix Codegen Names That Start With a Digit or Are Keywords _safe_segment returned names such as "24hr" or "class" unchanged, so the router generator emitted `async def 24hr(` and unimportable module names. Prefix them with an underscore (Fixes #7799). * Suffix keyword segments with an underscore instead of prefixing them
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import keyword
|
||||||
import re
|
import re
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
@@ -56,9 +57,18 @@ class GeneratedRouters:
|
|||||||
|
|
||||||
|
|
||||||
def _safe_segment(name: str) -> str:
|
def _safe_segment(name: str) -> str:
|
||||||
"""Lower-case + replace non-alphanumerics with underscores."""
|
"""Lower-case + replace non-alphanumerics with underscores.
|
||||||
cleaned = re.sub(r"[^0-9a-zA-Z]+", "_", name).strip("_")
|
|
||||||
return cleaned.lower() or "ns"
|
A result that starts with a digit gets a leading underscore and a Python
|
||||||
|
keyword gets a trailing one (as pydantic_gen does for field names), so it
|
||||||
|
stays a valid function and module identifier.
|
||||||
|
"""
|
||||||
|
cleaned = (re.sub(r"[^0-9a-zA-Z]+", "_", name).strip("_") or "ns").lower()
|
||||||
|
if cleaned[0].isdigit():
|
||||||
|
cleaned = f"_{cleaned}"
|
||||||
|
elif keyword.iskeyword(cleaned):
|
||||||
|
cleaned = f"{cleaned}_"
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
|
||||||
def _module_name_for(path: str) -> str:
|
def _module_name_for(path: str) -> str:
|
||||||
|
|||||||
@@ -407,3 +407,42 @@ def test_generate_routers_command_that_is_also_a_namespace_lives_in_its_own_rout
|
|||||||
assert "async def count(" in by_module["alerts_active"].source
|
assert "async def count(" in by_module["alerts_active"].source
|
||||||
for r in out.routers:
|
for r in out.routers:
|
||||||
ast.parse(r.source)
|
ast.parse(r.source)
|
||||||
|
|
||||||
|
|
||||||
|
def test_safe_segment_prefixes_digits_and_suffixes_keywords():
|
||||||
|
assert rg._safe_segment("24hr") == "_24hr"
|
||||||
|
assert rg._safe_segment("class") == "class_"
|
||||||
|
assert rg._safe_segment("Return") == "return_"
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_routers_digit_and_keyword_commands_are_valid_python():
|
||||||
|
tree = nt.build_namespace_tree(
|
||||||
|
{
|
||||||
|
"ticker.24hr": {"providers": ["fmp"], "description": "24h stats"},
|
||||||
|
"ticker.class": {"providers": ["fmp"], "description": "Class"},
|
||||||
|
"24hr": {"providers": ["fmp"], "description": "Root 24h"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
fetchers = {
|
||||||
|
key: _Fetcher(
|
||||||
|
module_name=key.replace(".", "_"),
|
||||||
|
fetcher_class="F",
|
||||||
|
model_name="M",
|
||||||
|
)
|
||||||
|
for key in ("ticker.24hr", "ticker.class", "24hr")
|
||||||
|
}
|
||||||
|
out = rg.generate_routers(
|
||||||
|
tree,
|
||||||
|
package_name="openbb_codegen",
|
||||||
|
provider_name="fmp",
|
||||||
|
fetchers_by_command=fetchers,
|
||||||
|
post_commands_by_command={},
|
||||||
|
)
|
||||||
|
assert out.routers
|
||||||
|
for router in out.routers:
|
||||||
|
ast.parse(router.source)
|
||||||
|
for block in out.root_commands:
|
||||||
|
ast.parse(block)
|
||||||
|
generated = "".join(r.source for r in out.routers) + "".join(out.root_commands)
|
||||||
|
assert "async def _24hr(" in generated
|
||||||
|
assert "async def class_(" in generated
|
||||||
|
|||||||
Reference in New Issue
Block a user