11"""CLI commands for setup and configuration."""
22
33import argparse
4- import os
54import sys
5+ from pathlib import Path
66from typing import Optional , Tuple
77
88
99def detect_framework_and_app () -> Optional [Tuple [str , str , str ]]:
1010 """Detect framework and app location.
11+
1112 Returns (framework, file_path, app_variable) or None.
1213 """
13- import glob
14-
1514 # Search for app files at any depth in current directory
1615 app_patterns = ["app.py" , "main.py" , "server.py" , "wsgi.py" , "asgi.py" ]
1716 common_vars = ["app" , "application" , "main" , "server" ]
1817
1918 # Find all matching files recursively
20- found_files = []
21- for pattern in app_patterns :
22- found_files .extend (glob .glob (f"**/{ pattern } " , recursive = True ))
19+ found_files = [file_path for pattern in app_patterns for file_path in Path ().rglob (pattern )]
2320
2421 # Sort by depth (shallowest first) and then by filename priority
25- found_files .sort (key = lambda x : (x . count ( os . sep ), app_patterns .index (os . path . basename ( x ) )))
22+ found_files .sort (key = lambda p : (len ( p . parts ), app_patterns .index (p . name )))
2623
2724 for file_path in found_files :
2825 try :
29- with open (file_path , "r" ) as f :
30- content = f .read ()
26+ content = file_path .read_text ()
3127
3228 if "from fastapi import" in content or "import fastapi" in content :
3329 framework = "FastAPI"
3430 elif "from flask import" in content or "import flask" in content :
3531 framework = "Flask"
3632 else :
37- continue
33+ continue # Not a framework file we care about
3834
3935 for var_name in common_vars :
4036 if f"{ var_name } = " in content :
41- return framework , file_path , var_name
37+ return framework , file_path . as_posix () , var_name
4238
43- except Exception :
39+ except ( IOError , UnicodeDecodeError ) :
4440 continue
4541
4642 return None
@@ -88,7 +84,7 @@ def app():
8884'''
8985
9086
91- def generate_pyproject_config (framework : str ) -> str :
87+ def generate_pyproject_config () -> str :
9288 """Generate pyproject.toml configuration section."""
9389 return """
9490# pytest-api-cov configuration
@@ -133,7 +129,7 @@ def cmd_init() -> int:
133129 framework , file_path , app_variable = detection_result
134130 print (f"✅ Detected { framework } app in { file_path } (variable: { app_variable } )" )
135131
136- conftest_exists = os . path . exists ("conftest.py" )
132+ conftest_exists = Path ("conftest.py" ). exists ( )
137133 if conftest_exists :
138134 print ("⚠️ conftest.py already exists" )
139135 create_conftest = input ("Do you want to overwrite it? (y/N): " ).lower ().startswith ("y" )
@@ -142,28 +138,28 @@ def cmd_init() -> int:
142138
143139 if create_conftest :
144140 conftest_content = generate_conftest_content (framework , file_path , app_variable )
145- with open ("conftest.py" , "w" ) as f :
141+ with Path ("conftest.py" ). open ( "w" ) as f :
146142 f .write (conftest_content )
147143 print ("✅ Created conftest.py" )
148144
149- pyproject_exists = os . path . exists ("pyproject.toml" )
145+ pyproject_exists = Path ("pyproject.toml" ). exists ( )
150146 if pyproject_exists :
151- print ("ℹ️ pyproject.toml already exists" )
147+ print ("ℹ️ pyproject.toml already exists" ) # noqa: RUF001
152148 print ("Add this configuration to your pyproject.toml:" )
153- print (generate_pyproject_config (framework ))
149+ print (generate_pyproject_config ())
154150 else :
155151 create_pyproject = input ("Create pyproject.toml with pytest-api-cov config? (Y/n): " ).lower ()
156152 if not create_pyproject .startswith ("n" ):
157153 pyproject_content = f"""[project]
158154name = "your-project"
159155version = "0.1.0"
160156
161- { generate_pyproject_config (framework )}
157+ { generate_pyproject_config ()}
162158
163159[tool.pytest.ini_options]
164160testpaths = ["tests"]
165161"""
166- with open ("pyproject.toml" , "w" ) as f :
162+ with Path ("pyproject.toml" ). open ( "w" ) as f :
167163 f .write (pyproject_content )
168164 print ("✅ Created pyproject.toml" )
169165
@@ -205,7 +201,7 @@ def read_root():
205201
206202
207203def main () -> int :
208- """Main CLI entry point."""
204+ """Run the main CLI entry point."""
209205 parser = argparse .ArgumentParser (prog = "pytest-api-cov" , description = "pytest API coverage plugin CLI tools" )
210206
211207 subparsers = parser .add_subparsers (dest = "command" , help = "Available commands" )
0 commit comments