Coverage for faststream / _internal / cli / utils / imports.py: 69%
65 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
1import importlib
2from importlib.util import module_from_spec, spec_from_file_location
3from pathlib import Path
5import typer
7from faststream.exceptions import SetupError
10def import_from_string(
11 import_str: str,
12 *,
13 is_factory: bool = False,
14) -> tuple[Path, object]:
15 module_path, instance = _import_object_or_factory(import_str)
17 if is_factory: 17 ↛ 18line 17 didn't jump to line 18 because the condition on line 17 was never true
18 if callable(instance):
19 instance = instance()
20 else:
21 msg = f'"{instance}" is not a factory.'
22 raise typer.BadParameter(msg)
24 return module_path, instance
27def _is_missing_requested_module(
28 module_str: str,
29 exc: ModuleNotFoundError,
30) -> bool:
31 if exc.name is None: 31 ↛ 32line 31 didn't jump to line 32 because the condition on line 31 was never true
32 return False
34 requested_module = module_str.split(".")
35 missing_module = exc.name.split(".")
37 return missing_module == requested_module[: len(missing_module)]
40def _import_object_or_factory(import_str: str) -> tuple[Path, object]:
41 """Import FastStream application from module specified by a string."""
42 if not isinstance(import_str, str): 42 ↛ 43line 42 didn't jump to line 43 because the condition on line 42 was never true
43 msg = "Given value is not of type string"
44 raise typer.BadParameter(msg)
46 module_str, _, attrs_str = import_str.partition(":")
47 if not module_str or not attrs_str:
48 msg = f'Import string "{import_str}" must be in format "<module>:<attribute>"'
49 raise typer.BadParameter(
50 msg,
51 )
53 try:
54 module = importlib.import_module( # nosemgrep: python.lang.security.audit.non-literal-import.non-literal-import
55 module_str,
56 )
58 except ModuleNotFoundError as e:
59 if not _is_missing_requested_module(module_str, e):
60 raise
62 module_path, import_obj_name = _get_obj_path(import_str)
63 instance = _try_import_app(module_path, import_obj_name)
65 else:
66 attr = module
67 try:
68 for attr_str in attrs_str.split("."):
69 attr = getattr(attr, attr_str)
70 instance = attr
72 except AttributeError as e:
73 typer.echo(e, err=True)
74 msg = f'Attribute "{attrs_str}" not found in module "{module_str}".'
75 raise typer.BadParameter(
76 msg,
77 ) from e
79 if module.__file__: 79 ↛ 82line 79 didn't jump to line 82 because the condition on line 79 was always true
80 module_path = Path(module.__file__).resolve().parent
81 else:
82 module_path = Path.cwd()
84 return module_path, instance
87def _try_import_app(module: Path, app: str) -> object:
88 """Tries to import a FastStream app from a module."""
89 try:
90 app_object = _import_object(module, app)
92 except FileNotFoundError as e:
93 typer.echo(e, err=True)
94 msg = "Please, input module like [python_file:docs_object] or [module:attribute]"
95 raise typer.BadParameter(
96 msg,
97 ) from e
99 else:
100 return app_object
103def _import_object(module: Path, app: str) -> object:
104 """Import an object from a module."""
105 spec = spec_from_file_location(
106 "mode",
107 f"{module}.py",
108 submodule_search_locations=[str(module.parent.absolute())],
109 )
111 if spec is None: # pragma: no cover
112 raise FileNotFoundError(module)
114 mod = module_from_spec(spec)
115 loader = spec.loader
117 if loader is None: # pragma: no cover
118 msg = f"{spec} has no loader"
119 raise SetupError(msg)
121 loader.exec_module(mod)
123 try:
124 obj = getattr(mod, app)
125 except AttributeError as e:
126 raise FileNotFoundError(module) from e
128 return obj
131def _get_obj_path(obj_path: str) -> tuple[Path, str]:
132 """Get the application path."""
133 if ":" not in obj_path:
134 msg = f"`{obj_path}` is not a path to object"
135 raise SetupError(msg)
137 module, app_name = obj_path.split(":", 2)
139 mod_path = Path.cwd()
140 for i in module.split("."):
141 mod_path /= i
143 return mod_path, app_name