@@ -57,14 +57,73 @@ def project_dir_callback(ctx: typer.Context, param: typer.CallbackParam, value:
5757 if not value :
5858 return None
5959
60- # Auto-apply "project" preset if no preset was explicitly set
60+ # Auto-apply "project" preset if no preset was explicitly set.
61+ # --preset is also is_eager and declared before --project-dir, so if the user
62+ # passed --preset explicitly, ctx.default_map is already populated here.
6163 if not ctx .default_map :
6264 preset_callback (ctx , param , "project" )
6365
6466 ctx .ensure_object (dict )["project_dir" ] = value
6567 return value
6668
6769
70+ def _detect_project_layout (project_dir_value : str ) -> tuple [Path | None , str | None , str | None ]:
71+ """Detect odoo_dir, odoo_version, and addons_path from a project directory.
72+
73+ Returns:
74+ (odoo_dir_path, odoo_version, addons_path) — any value may be None if not detected.
75+ """
76+ project_dir_path = Path (project_dir_value ).expanduser ().resolve ()
77+ detected_paths = detect_codebase_layout (project_dir_path )
78+
79+ addons_path = get_addons_path (project_dir_path , detected_paths = detected_paths )
80+
81+ # Resolve odoo_dir from detected layout
82+ odoo_dir_path = None
83+ if detected_paths .get ("odoo_dir" ):
84+ odoo_dir_path = detected_paths ["odoo_dir" ][0 ].parent
85+
86+ # Infer version from release.py inside the detected odoo dir
87+ odoo_version = None
88+ if odoo_dir_path :
89+ odoo_version = get_odoo_version_from_release (odoo_dir_path )
90+
91+ return odoo_dir_path , odoo_version , addons_path
92+
93+
94+ def _resolve_odoo_dir_and_version (
95+ odoo_dir : str | None ,
96+ odoo_version : str | None ,
97+ detected_odoo_dir : Path | None ,
98+ detected_version : str | None ,
99+ ) -> tuple [Path , str ]:
100+ """Determine odoo_dir and odoo_version from explicit args or detected values.
101+
102+ Priority: explicit CLI flags > auto-detected from --project-dir > default path.
103+ Exits with an error if neither can be resolved.
104+ """
105+ # Resolve odoo_dir: explicit flag > detected > default path from version
106+ if odoo_dir :
107+ odoo_dir_path = Path (odoo_dir ).expanduser ().resolve ()
108+ elif detected_odoo_dir :
109+ odoo_dir_path = detected_odoo_dir
110+ elif odoo_version :
111+ odoo_dir_path = Path (f"~/code/odoo/odoo/{ odoo_version } " ).expanduser ()
112+ else :
113+ typer .secho ("error: ODOO_VERSION is required when --project-dir is not used." , fg = typer .colors .RED )
114+ raise typer .Exit (1 )
115+
116+ # Resolve odoo_version: explicit arg > detected from release.py
117+ resolved_version = odoo_version or detected_version
118+ if not resolved_version :
119+ typer .secho (
120+ "error: Could not detect Odoo version from source. Provide ODOO_VERSION explicitly." , fg = typer .colors .RED
121+ )
122+ raise typer .Exit (1 )
123+
124+ return odoo_dir_path , resolved_version
125+
126+
68127def version_callback (value : bool ):
69128 if value :
70129 typer .echo (f"odoo-venv { version ('odoo-venv' )} " )
@@ -88,7 +147,7 @@ def main_callback(
88147
89148
90149@app .command ()
91- def create ( # noqa: C901
150+ def create (
92151 ctx : typer .Context ,
93152 odoo_version : Annotated [
94153 str | None , typer .Argument (help = "Odoo version, e.g: 18.0. Inferred from --project-dir if omitted." )
@@ -185,40 +244,15 @@ def create( # noqa: C901
185244 ] = None ,
186245):
187246 """Create virtual environment to run Odoo"""
188- # Handle --project-dir: auto-detect addons_path and odoo_dir
247+ # Auto-detect layout from --project-dir if provided
189248 project_dir_value = ctx .obj .get ("project_dir" ) if ctx .obj else None
190- detected_addons_path = None
191- if project_dir_value :
192- project_dir_path = Path (project_dir_value ).expanduser ().resolve ()
193- detected_paths = detect_codebase_layout (project_dir_path )
194- detected_addons_path = get_addons_path (
195- project_dir_path ,
196- detected_paths = detected_paths ,
197- )
198-
199- # Resolve odoo_dir (before odoo_version, which may be inferred from it)
200- if odoo_dir :
201- odoo_dir_path = Path (odoo_dir ).expanduser ().resolve ()
202- elif project_dir_value and detected_paths .get ("odoo_dir" ):
203- odoo_dir_path = detected_paths ["odoo_dir" ][0 ].parent
204- elif odoo_version :
205- odoo_dir_path = Path (f"~/code/odoo/odoo/{ odoo_version } " ).expanduser ()
206- else :
207- typer .secho (
208- "error: ODOO_VERSION is required when --project-dir is not used." ,
209- fg = typer .colors .RED ,
210- )
211- raise typer .Exit (1 )
249+ detected_odoo_dir , detected_version , detected_addons_path = (
250+ _detect_project_layout (project_dir_value ) if project_dir_value else (None , None , None )
251+ )
212252
213- # Infer odoo_version from release.py if not provided
214- if not odoo_version :
215- odoo_version = get_odoo_version_from_release (odoo_dir_path )
216- if not odoo_version :
217- typer .secho (
218- "error: Could not detect Odoo version from source. Provide ODOO_VERSION explicitly." ,
219- fg = typer .colors .RED ,
220- )
221- raise typer .Exit (1 )
253+ odoo_dir_path , odoo_version = _resolve_odoo_dir_and_version (
254+ odoo_dir , odoo_version , detected_odoo_dir , detected_version
255+ )
222256
223257 if not python_version :
224258 python_version = ODOO_PYTHON_VERSIONS .get (odoo_version )
0 commit comments