Skip to content

Commit bd05e72

Browse files
committed
Add precautions to FD leaks
1 parent 553a192 commit bd05e72

File tree

3 files changed

+293
-45
lines changed

3 files changed

+293
-45
lines changed

Makefile

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# CodeFlash Development Makefile
2+
# This Makefile provides convenient commands for common development tasks
3+
4+
.PHONY: help install install-dev setup test test-verbose test-file lint format typecheck clean build publish dev-setup docs-serve docs-build verify-setup all-checks pre-commit
5+
6+
# Default target
7+
.DEFAULT_GOAL := help
8+
9+
# Colors for output
10+
CYAN := \033[36m
11+
GREEN := \033[32m
12+
YELLOW := \033[33m
13+
RED := \033[31m
14+
RESET := \033[0m
15+
16+
help: ## Show this help message
17+
@echo "$(CYAN)CodeFlash Development Commands$(RESET)"
18+
@echo "=============================="
19+
@echo ""
20+
@echo "$(GREEN)Installation & Setup:$(RESET)"
21+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '^(install|setup|dev-setup)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-15s$(RESET) %s\n", $$1, $$2}'
22+
@echo ""
23+
@echo "$(GREEN)Development:$(RESET)"
24+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '^(test|lint|format|typecheck|verify-setup)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-15s$(RESET) %s\n", $$1, $$2}'
25+
@echo ""
26+
@echo "$(GREEN)Build & Release:$(RESET)"
27+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '^(build|publish|clean)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-15s$(RESET) %s\n", $$1, $$2}'
28+
@echo ""
29+
@echo "$(GREEN)Documentation:$(RESET)"
30+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '^(docs-)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-15s$(RESET) %s\n", $$1, $$2}'
31+
@echo ""
32+
@echo "$(GREEN)Quality Assurance:$(RESET)"
33+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | grep -E '^(all-checks|pre-commit)' | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-15s$(RESET) %s\n", $$1, $$2}'
34+
35+
install: ## Install CodeFlash for end users
36+
@echo "$(GREEN)Installing CodeFlash...$(RESET)"
37+
uv pip install .
38+
39+
install-dev: ## Install CodeFlash with development dependencies
40+
@echo "$(GREEN)Installing CodeFlash with development dependencies...$(RESET)"
41+
uv sync --all-extras
42+
43+
setup: install-dev ## Complete development setup (install + configure)
44+
@echo "$(GREEN)Setting up CodeFlash development environment...$(RESET)"
45+
@echo "$(YELLOW)Running initial setup...$(RESET)"
46+
uv run python -c "import codeflash; print('✅ CodeFlash installed successfully')"
47+
@echo "$(GREEN)✅ Development setup complete!$(RESET)"
48+
49+
dev-setup: setup ## Alias for setup (complete development setup)
50+
51+
test: ## Run all tests
52+
@echo "$(GREEN)Running tests...$(RESET)"
53+
uv run pytest tests/ -v
54+
55+
test-verbose: ## Run tests with verbose output
56+
@echo "$(GREEN)Running tests with verbose output...$(RESET)"
57+
uv run pytest tests/ -v -s
58+
59+
test-file: ## Run tests for a specific file (usage: make test-file FILE=path/to/test.py)
60+
@echo "$(GREEN)Running tests for $(FILE)...$(RESET)"
61+
uv run pytest $(FILE) -v
62+
63+
test-unit: ## Run unit tests only (exclude benchmarks)
64+
@echo "$(GREEN)Running unit tests only...$(RESET)"
65+
uv run pytest tests/ -v -k "not benchmark"
66+
67+
test-benchmark: ## Run benchmark tests only
68+
@echo "$(GREEN)Running benchmark tests...$(RESET)"
69+
uv run pytest tests/benchmarks/ -v
70+
71+
lint: ## Run linting checks
72+
@echo "$(GREEN)Running linting checks...$(RESET)"
73+
uv run ruff check codeflash/
74+
75+
lint-fix: ## Run linting and fix issues automatically
76+
@echo "$(GREEN)Running linting with auto-fix...$(RESET)"
77+
uv run ruff check --fix codeflash/
78+
79+
format: ## Format code
80+
@echo "$(GREEN)Formatting code...$(RESET)"
81+
uv run ruff format codeflash/
82+
83+
format-check: ## Check if code is formatted correctly
84+
@echo "$(GREEN)Checking code formatting...$(RESET)"
85+
uv run ruff format --check codeflash/
86+
87+
typecheck: ## Run type checking
88+
@echo "$(GREEN)Running type checking...$(RESET)"
89+
uv run mypy codeflash/
90+
91+
verify-setup: ## Verify CodeFlash setup by running bubble sort optimization
92+
@echo "$(GREEN)Verifying CodeFlash setup...$(RESET)"
93+
cd code_to_optimize && uv run codeflash --verify-setup
94+
95+
clean: ## Clean build artifacts and cache
96+
@echo "$(GREEN)Cleaning build artifacts...$(RESET)"
97+
rm -rf build/
98+
rm -rf dist/
99+
rm -rf *.egg-info/
100+
rm -rf .pytest_cache/
101+
rm -rf .mypy_cache/
102+
rm -rf .ruff_cache/
103+
find . -type d -name __pycache__ -exec rm -rf {} +
104+
find . -type f -name "*.pyc" -delete
105+
find . -type f -name "*.pyo" -delete
106+
find . -type f -name "*.pyd" -delete
107+
find . -type f -name ".coverage" -delete
108+
find . -type d -name "*.egg-info" -exec rm -rf {} +
109+
110+
build: clean ## Build the package
111+
@echo "$(GREEN)Building CodeFlash package...$(RESET)"
112+
uv build
113+
114+
publish: build ## Publish to PyPI (requires authentication)
115+
@echo "$(YELLOW)Publishing to PyPI...$(RESET)"
116+
@echo "$(RED)⚠️ Make sure you have proper authentication configured!$(RESET)"
117+
uv publish
118+
119+
publish-test: build ## Publish to TestPyPI
120+
@echo "$(YELLOW)Publishing to TestPyPI...$(RESET)"
121+
uv publish --index-url https://test.pypi.org/simple/
122+
123+
docs-serve: ## Serve documentation locally
124+
@echo "$(GREEN)Starting documentation server...$(RESET)"
125+
cd docs && npm start
126+
127+
docs-build: ## Build documentation
128+
@echo "$(GREEN)Building documentation...$(RESET)"
129+
cd docs && npm run build
130+
131+
docs-install: ## Install documentation dependencies
132+
@echo "$(GREEN)Installing documentation dependencies...$(RESET)"
133+
cd docs && npm install
134+
135+
# Quality assurance targets
136+
all-checks: lint typecheck test ## Run all quality checks (linting, type checking, tests)
137+
@echo "$(GREEN)✅ All quality checks passed!$(RESET)"
138+
139+
pre-commit: format-check lint typecheck test-unit ## Run checks suitable for pre-commit hook
140+
@echo "$(GREEN)✅ Pre-commit checks passed!$(RESET)"
141+
142+
# Advanced development targets
143+
init-example: ## Initialize CodeFlash in an example project
144+
@echo "$(GREEN)Initializing CodeFlash in example project...$(RESET)"
145+
cd code_to_optimize && uv run codeflash init
146+
147+
optimize-example: ## Run optimization on example bubble sort
148+
@echo "$(GREEN)Running optimization on example...$(RESET)"
149+
cd code_to_optimize && uv run codeflash --file bubble_sort.py --function sorter
150+
151+
trace-example: ## Trace example workload
152+
@echo "$(GREEN)Tracing example workload...$(RESET)"
153+
cd code_to_optimize && uv run codeflash optimize workload.py
154+
155+
# File handle leak test
156+
test-file-handles: ## Test file handle leak fixes
157+
@echo "$(GREEN)Testing file handle leak fixes...$(RESET)"
158+
uv run python test_file_handle_fixes.py
159+
160+
# Development utilities
161+
shell: ## Open development shell with CodeFlash environment
162+
@echo "$(GREEN)Opening development shell...$(RESET)"
163+
uv shell
164+
165+
update-deps: ## Update dependencies
166+
@echo "$(GREEN)Updating dependencies...$(RESET)"
167+
uv sync --upgrade
168+
169+
check-deps: ## Check for dependency issues
170+
@echo "$(GREEN)Checking dependencies...$(RESET)"
171+
uv tree
172+
173+
# Debug targets
174+
debug-install: ## Debug installation issues
175+
@echo "$(GREEN)Debugging installation...$(RESET)"
176+
uv run python -c "import sys; print('Python:', sys.version)"
177+
uv run python -c "import codeflash; print('CodeFlash version:', codeflash.__version__)"
178+
uv run python -c "from codeflash.main import main; print('✅ Main function importable')"
179+
180+
# Quick development workflow
181+
quick-check: format lint typecheck test-unit ## Quick development check (format, lint, typecheck, unit tests)
182+
@echo "$(GREEN)✅ Quick development check passed!$(RESET)"
183+
184+
# Installation verification
185+
verify-install: ## Verify installation works correctly
186+
@echo "$(GREEN)Verifying CodeFlash installation...$(RESET)"
187+
uv run codeflash --version
188+
uv run python -c "import codeflash; print('✅ CodeFlash can be imported')"
189+
@echo "$(GREEN)✅ Installation verification complete!$(RESET)"
190+
191+
# Performance testing
192+
benchmark-discovery: ## Benchmark test discovery performance
193+
@echo "$(GREEN)Benchmarking test discovery...$(RESET)"
194+
uv run pytest tests/benchmarks/test_benchmark_discover_unit_tests.py -v
195+
196+
# Help for common use cases
197+
help-dev: ## Show help for common development workflows
198+
@echo "$(CYAN)Common Development Workflows$(RESET)"
199+
@echo "============================"
200+
@echo ""
201+
@echo "$(GREEN)First time setup:$(RESET)"
202+
@echo " make setup"
203+
@echo ""
204+
@echo "$(GREEN)Before committing:$(RESET)"
205+
@echo " make pre-commit"
206+
@echo ""
207+
@echo "$(GREEN)Quick development cycle:$(RESET)"
208+
@echo " make quick-check"
209+
@echo ""
210+
@echo "$(GREEN)Full quality assurance:$(RESET)"
211+
@echo " make all-checks"
212+
@echo ""
213+
@echo "$(GREEN)Test specific functionality:$(RESET)"
214+
@echo " make test-file FILE=tests/test_something.py"
215+
@echo " make verify-setup"
216+
@echo " make test-file-handles"
217+
@echo ""
218+
@echo "$(GREEN)Build and publish:$(RESET)"
219+
@echo " make build"
220+
@echo " make publish-test # Test on TestPyPI first"
221+
@echo " make publish # Publish to PyPI"

codeflash/verification/codeflash_capture.py

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -116,52 +116,55 @@ def wrapper(*args, **kwargs) -> None: # noqa: ANN002, ANN003
116116
print(f"!$######{test_stdout_tag}######$!")
117117
# Connect to sqlite
118118
codeflash_con = sqlite3.connect(f"{tmp_dir_path}_{codeflash_iteration}.sqlite")
119-
codeflash_cur = codeflash_con.cursor()
120-
121-
# Record timing information
122-
exception = None
123-
gc.disable()
124119
try:
125-
counter = time.perf_counter_ns()
126-
wrapped(*args, **kwargs)
127-
codeflash_duration = time.perf_counter_ns() - counter
128-
except Exception as e:
129-
codeflash_duration = time.perf_counter_ns() - counter
130-
exception = e
120+
codeflash_cur = codeflash_con.cursor()
121+
122+
# Record timing information
123+
exception = None
124+
gc.disable()
125+
try:
126+
counter = time.perf_counter_ns()
127+
wrapped(*args, **kwargs)
128+
codeflash_duration = time.perf_counter_ns() - counter
129+
except Exception as e:
130+
codeflash_duration = time.perf_counter_ns() - counter
131+
exception = e
132+
finally:
133+
gc.enable()
134+
print(f"!######{test_stdout_tag}######!")
135+
136+
# Capture instance state after initialization
137+
if hasattr(args[0], "__dict__"):
138+
instance_state = args[
139+
0
140+
].__dict__ # self is always the first argument, this is ensured during instrumentation
141+
else:
142+
raise ValueError("Instance state could not be captured.")
143+
codeflash_cur.execute(
144+
"CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)"
145+
)
146+
147+
# Write to sqlite
148+
pickled_return_value = pickle.dumps(exception) if exception else pickle.dumps(instance_state)
149+
codeflash_cur.execute(
150+
"INSERT INTO test_results VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
151+
(
152+
test_module_name,
153+
test_class_name,
154+
test_name,
155+
function_name,
156+
loop_index,
157+
invocation_id,
158+
codeflash_duration,
159+
pickled_return_value,
160+
VerificationType.INIT_STATE_FTO if is_fto else VerificationType.INIT_STATE_HELPER,
161+
),
162+
)
163+
codeflash_con.commit()
164+
if exception:
165+
raise exception
131166
finally:
132-
gc.enable()
133-
print(f"!######{test_stdout_tag}######!")
134-
135-
# Capture instance state after initialization
136-
if hasattr(args[0], "__dict__"):
137-
instance_state = args[
138-
0
139-
].__dict__ # self is always the first argument, this is ensured during instrumentation
140-
else:
141-
raise ValueError("Instance state could not be captured.")
142-
codeflash_cur.execute(
143-
"CREATE TABLE IF NOT EXISTS test_results (test_module_path TEXT, test_class_name TEXT, test_function_name TEXT, function_getting_tested TEXT, loop_index INTEGER, iteration_id TEXT, runtime INTEGER, return_value BLOB, verification_type TEXT)"
144-
)
145-
146-
# Write to sqlite
147-
pickled_return_value = pickle.dumps(exception) if exception else pickle.dumps(instance_state)
148-
codeflash_cur.execute(
149-
"INSERT INTO test_results VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
150-
(
151-
test_module_name,
152-
test_class_name,
153-
test_name,
154-
function_name,
155-
loop_index,
156-
invocation_id,
157-
codeflash_duration,
158-
pickled_return_value,
159-
VerificationType.INIT_STATE_FTO if is_fto else VerificationType.INIT_STATE_HELPER,
160-
),
161-
)
162-
codeflash_con.commit()
163-
if exception:
164-
raise exception
167+
codeflash_con.close()
165168

166169
return wrapper
167170

codeflash/verification/test_runner.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,31 @@ def execute_test_subprocess(
2525
"""Execute a subprocess with the given command list, working directory, environment variables, and timeout."""
2626
with custom_addopts():
2727
logger.debug(f"executing test run with command: {' '.join(cmd_list)}")
28-
return subprocess.run(cmd_list, capture_output=True, cwd=cwd, env=env, text=True, timeout=timeout, check=False)
28+
# Use explicit pipe management to prevent FD leaks
29+
proc = subprocess.Popen(
30+
cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, cwd=cwd, env=env, text=True
31+
)
32+
try:
33+
stdout, stderr = proc.communicate(timeout=timeout)
34+
returncode = proc.returncode
35+
except subprocess.TimeoutExpired:
36+
proc.kill()
37+
try:
38+
stdout, stderr = proc.communicate(timeout=5)
39+
except subprocess.TimeoutExpired:
40+
proc.terminate()
41+
stdout, stderr = "", "Process terminated due to timeout"
42+
returncode = -1
43+
finally:
44+
# Ensure all pipes are closed
45+
if proc.stdin:
46+
proc.stdin.close()
47+
if proc.stdout:
48+
proc.stdout.close()
49+
if proc.stderr:
50+
proc.stderr.close()
51+
52+
return subprocess.CompletedProcess(cmd_list, returncode, stdout, stderr)
2953

3054

3155
def run_behavioral_tests(

0 commit comments

Comments
 (0)