Lint and format entire repo, not just packages (#23)

Remove .codeflash/ from ruff extend-exclude, add per-file ignores
for .codeflash/, scripts/, evals/, and plugin/ (benchmark/script
patterns like print, eval, magic values). Remove shebangs. Widen
pre-commit hooks to check the full repo.
This commit is contained in:
Kevin Turcios 2026-04-15 03:16:15 -05:00 committed by GitHub
parent 33faedf427
commit 20f6c59f05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 594 additions and 298 deletions

View file

@ -1,25 +1,29 @@
#!/usr/bin/env python3
""" """
Benchmark for cache key computation in odoo/tools/cache.py Benchmark for cache key computation in odoo/tools/cache.py
Focuses on the determine_key() and key() methods which are pure Python. Focuses on the determine_key() and key() methods which are pure Python.
""" """
import time import time
from inspect import signature, Parameter from inspect import Parameter, signature
# Simulate the key computation logic from ormcache # Simulate the key computation logic from ormcache
def build_cache_key_eval(method, cache_args): def build_cache_key_eval(method, cache_args):
"""Current implementation using eval - generates lambda from string""" """Current implementation using eval - generates lambda from string"""
args = ', '.join( args = ", ".join(
str(params.replace(annotation=Parameter.empty)) str(params.replace(annotation=Parameter.empty))
for params in signature(method).parameters.values() for params in signature(method).parameters.values()
) )
values = ['self._name', 'method', *cache_args] values = ["self._name", "method", *cache_args]
code = f"lambda {args}: ({''.join(a for arg in values for a in (arg, ','))})" code = (
return eval(code, {'method': method}) f"lambda {args}: ({''.join(a for arg in values for a in (arg, ','))})"
)
return eval(code, {"method": method})
# Test methods with various signatures # Test methods with various signatures
class MockModel: class MockModel:
_name = 'test.model' _name = "test.model"
def simple_method(self, arg1, arg2): def simple_method(self, arg1, arg2):
return arg1 + arg2 return arg1 + arg2
@ -28,7 +32,8 @@ class MockModel:
return f"{model_name}:{mode}:{limit}:{offset}" return f"{model_name}:{mode}:{limit}:{offset}"
def many_args_method(self, a, b, c, d, e, f, g, h): def many_args_method(self, a, b, c, d, e, f, g, h):
return sum([a,b,c,d,e,f,g,h]) return sum([a, b, c, d, e, f, g, h])
def benchmark_key_computation(): def benchmark_key_computation():
"""Benchmark the key computation overhead""" """Benchmark the key computation overhead"""
@ -38,34 +43,32 @@ def benchmark_key_computation():
# Scenario 1: Simple method, many lookups # Scenario 1: Simple method, many lookups
print("Scenario 1: Simple method (2 args), 100k lookups") print("Scenario 1: Simple method (2 args), 100k lookups")
key_func = build_cache_key_eval(MockModel.simple_method, ['arg1', 'arg2']) key_func = build_cache_key_eval(MockModel.simple_method, ["arg1", "arg2"])
start = time.perf_counter() start = time.perf_counter()
for i in range(100_000): for i in range(100_000):
_ = key_func(model, i, i*2) _ = key_func(model, i, i * 2)
elapsed = time.perf_counter() - start elapsed = time.perf_counter() - start
print(f" Total: {elapsed:.3f}s") print(f" Total: {elapsed:.3f}s")
print(f" Per call: {elapsed/100_000*1e6:.2f} µs\n") print(f" Per call: {elapsed / 100_000 * 1e6:.2f} µs\n")
# Scenario 2: Complex method with defaults, many lookups # Scenario 2: Complex method with defaults, many lookups
print("Scenario 2: Complex method (4 args with defaults), 100k lookups") print("Scenario 2: Complex method (4 args with defaults), 100k lookups")
key_func = build_cache_key_eval( key_func = build_cache_key_eval(
MockModel.complex_method, MockModel.complex_method, ["model_name", "mode", "limit", "offset"]
['model_name', 'mode', 'limit', 'offset']
) )
start = time.perf_counter() start = time.perf_counter()
for i in range(100_000): for i in range(100_000):
_ = key_func(model, 'res.partner', 'read', 10, i) _ = key_func(model, "res.partner", "read", 10, i)
elapsed = time.perf_counter() - start elapsed = time.perf_counter() - start
print(f" Total: {elapsed:.3f}s") print(f" Total: {elapsed:.3f}s")
print(f" Per call: {elapsed/100_000*1e6:.2f} µs\n") print(f" Per call: {elapsed / 100_000 * 1e6:.2f} µs\n")
# Scenario 3: Many args method # Scenario 3: Many args method
print("Scenario 3: Many args method (8 args), 100k lookups") print("Scenario 3: Many args method (8 args), 100k lookups")
key_func = build_cache_key_eval( key_func = build_cache_key_eval(
MockModel.many_args_method, MockModel.many_args_method, ["a", "b", "c", "d", "e", "f", "g", "h"]
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
) )
start = time.perf_counter() start = time.perf_counter()
@ -73,16 +76,17 @@ def benchmark_key_computation():
_ = key_func(model, 1, 2, 3, 4, 5, 6, 7, 8) _ = key_func(model, 1, 2, 3, 4, 5, 6, 7, 8)
elapsed = time.perf_counter() - start elapsed = time.perf_counter() - start
print(f" Total: {elapsed:.3f}s") print(f" Total: {elapsed:.3f}s")
print(f" Per call: {elapsed/100_000*1e6:.2f} µs\n") print(f" Per call: {elapsed / 100_000 * 1e6:.2f} µs\n")
# Scenario 4: Key function creation overhead # Scenario 4: Key function creation overhead
print("Scenario 4: Key function creation (determine_key), 10k methods") print("Scenario 4: Key function creation (determine_key), 10k methods")
start = time.perf_counter() start = time.perf_counter()
for i in range(10_000): for i in range(10_000):
_ = build_cache_key_eval(MockModel.simple_method, ['arg1', 'arg2']) _ = build_cache_key_eval(MockModel.simple_method, ["arg1", "arg2"])
elapsed = time.perf_counter() - start elapsed = time.perf_counter() - start
print(f" Total: {elapsed:.3f}s") print(f" Total: {elapsed:.3f}s")
print(f" Per call: {elapsed/10_000*1e6:.2f} µs\n") print(f" Per call: {elapsed / 10_000 * 1e6:.2f} µs\n")
if __name__ == '__main__':
if __name__ == "__main__":
benchmark_key_computation() benchmark_key_computation()

View file

@ -1,20 +1,23 @@
#!/usr/bin/env python3
""" """
Benchmark for Odoo image processing utilities. Benchmark for Odoo image processing utilities.
Tests resize, format conversion, and thumbnail generation. Tests resize, format conversion, and thumbnail generation.
""" """
import sys
import os
import io
import timeit
from PIL import Image
import base64 import base64
import io
import os
import sys
import timeit
from PIL import Image
# Add odoo to path # Add odoo to path
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo")
# Activating venv programmatically by modifying path # Activating venv programmatically by modifying path
venv_site_packages = '/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages' venv_site_packages = (
"/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages"
)
if os.path.exists(venv_site_packages): if os.path.exists(venv_site_packages):
sys.path.insert(0, venv_site_packages) sys.path.insert(0, venv_site_packages)
@ -28,14 +31,14 @@ except ImportError as e:
def create_test_image(width=2048, height=2048): def create_test_image(width=2048, height=2048):
"""Create a test image in memory.""" """Create a test image in memory."""
img = Image.new('RGB', (width, height), color='red') img = Image.new("RGB", (width, height), color="red")
# Add some complexity # Add some complexity
for i in range(0, width, 100): for i in range(0, width, 100):
for j in range(0, height, 100): for j in range(0, height, 100):
img.putpixel((i, j), (0, 255, 0)) img.putpixel((i, j), (0, 255, 0))
buffer = io.BytesIO() buffer = io.BytesIO()
img.save(buffer, format='PNG') img.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -44,7 +47,7 @@ def benchmark_resize_pil(image_data, target_size=(512, 512)):
img = Image.open(io.BytesIO(image_data)) img = Image.open(io.BytesIO(image_data))
img_resized = img.resize(target_size, Image.Resampling.LANCZOS) img_resized = img.resize(target_size, Image.Resampling.LANCZOS)
buffer = io.BytesIO() buffer = io.BytesIO()
img_resized.save(buffer, format='PNG') img_resized.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -53,7 +56,7 @@ def benchmark_thumbnail_pil(image_data, max_size=(256, 256)):
img = Image.open(io.BytesIO(image_data)) img = Image.open(io.BytesIO(image_data))
img.thumbnail(max_size, Image.Resampling.LANCZOS) img.thumbnail(max_size, Image.Resampling.LANCZOS)
buffer = io.BytesIO() buffer = io.BytesIO()
img.save(buffer, format='PNG') img.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -63,13 +66,13 @@ def benchmark_format_conversion(image_data):
# Convert to JPEG # Convert to JPEG
buffer_jpeg = io.BytesIO() buffer_jpeg = io.BytesIO()
img_rgb = img.convert('RGB') img_rgb = img.convert("RGB")
img_rgb.save(buffer_jpeg, format='JPEG', quality=85) img_rgb.save(buffer_jpeg, format="JPEG", quality=85)
# Convert back to PNG # Convert back to PNG
img2 = Image.open(io.BytesIO(buffer_jpeg.getvalue())) img2 = Image.open(io.BytesIO(buffer_jpeg.getvalue()))
buffer_png = io.BytesIO() buffer_png = io.BytesIO()
img2.save(buffer_png, format='PNG') img2.save(buffer_png, format="PNG")
return buffer_png.getvalue() return buffer_png.getvalue()
@ -83,9 +86,9 @@ def benchmark_base64_operations(image_data):
def run_benchmarks(): def run_benchmarks():
"""Run all image benchmarks.""" """Run all image benchmarks."""
print("="*80) print("=" * 80)
print("Image Processing Benchmark") print("Image Processing Benchmark")
print("="*80) print("=" * 80)
# Create test images of different sizes # Create test images of different sizes
test_sizes = [ test_sizes = [
@ -105,50 +108,65 @@ def run_benchmarks():
print(f"Image size: {size_mb:.2f} MB") print(f"Image size: {size_mb:.2f} MB")
# Benchmark resize # Benchmark resize
time_resize = timeit.timeit( time_resize = (
lambda: benchmark_resize_pil(image_data, (512, 512)), timeit.timeit(
number=10 lambda: benchmark_resize_pil(image_data, (512, 512)), number=10
) / 10 )
print(f"Resize to 512x512: {time_resize*1000:.2f} ms") / 10
)
print(f"Resize to 512x512: {time_resize * 1000:.2f} ms")
# Benchmark thumbnail # Benchmark thumbnail
time_thumbnail = timeit.timeit( time_thumbnail = (
lambda: benchmark_thumbnail_pil(image_data, (256, 256)), timeit.timeit(
number=10 lambda: benchmark_thumbnail_pil(image_data, (256, 256)),
) / 10 number=10,
print(f"Thumbnail to 256x256: {time_thumbnail*1000:.2f} ms") )
/ 10
)
print(f"Thumbnail to 256x256: {time_thumbnail * 1000:.2f} ms")
# Benchmark format conversion # Benchmark format conversion
time_format = timeit.timeit( time_format = (
lambda: benchmark_format_conversion(image_data), timeit.timeit(
number=5 lambda: benchmark_format_conversion(image_data), number=5
) / 5 )
print(f"Format conversion (PNG->JPEG->PNG): {time_format*1000:.2f} ms") / 5
)
print(
f"Format conversion (PNG->JPEG->PNG): {time_format * 1000:.2f} ms"
)
# Benchmark base64 # Benchmark base64
time_base64 = timeit.timeit( time_base64 = (
lambda: benchmark_base64_operations(image_data), timeit.timeit(
number=100 lambda: benchmark_base64_operations(image_data), number=100
) / 100 )
print(f"Base64 encode/decode: {time_base64*1000:.2f} ms") / 100
)
print(f"Base64 encode/decode: {time_base64 * 1000:.2f} ms")
results.append({ results.append(
'size': label, {
'image_mb': size_mb, "size": label,
'resize_ms': time_resize * 1000, "image_mb": size_mb,
'thumbnail_ms': time_thumbnail * 1000, "resize_ms": time_resize * 1000,
'format_ms': time_format * 1000, "thumbnail_ms": time_thumbnail * 1000,
'base64_ms': time_base64 * 1000, "format_ms": time_format * 1000,
}) "base64_ms": time_base64 * 1000,
}
)
# Summary # Summary
print("\n" + "="*80) print("\n" + "=" * 80)
print("SUMMARY") print("SUMMARY")
print("="*80) print("=" * 80)
for r in results: for r in results:
print(f"\n{r['size']} ({r['image_mb']:.2f} MB):") print(f"\n{r['size']} ({r['image_mb']:.2f} MB):")
print(f" Total processing time: {r['resize_ms'] + r['thumbnail_ms'] + r['format_ms']:.2f} ms") print(
f" Total processing time: {r['resize_ms'] + r['thumbnail_ms'] + r['format_ms']:.2f} ms"
)
if __name__ == '__main__': if __name__ == "__main__":
run_benchmarks() run_benchmarks()

View file

@ -1,21 +1,24 @@
#!/usr/bin/env python3
""" """
Benchmark for Odoo image processing utilities - Optimized version 1. Benchmark for Odoo image processing utilities - Optimized version 1.
Optimization: Use numpy array for efficient image creation instead of per-pixel operations. Optimization: Use numpy array for efficient image creation instead of per-pixel operations.
""" """
import sys
import os
import io
import timeit
from PIL import Image
import base64 import base64
import io
import os
import sys
import timeit
import numpy as np import numpy as np
from PIL import Image
# Add odoo to path # Add odoo to path
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo")
# Activating venv programmatically by modifying path # Activating venv programmatically by modifying path
venv_site_packages = '/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages' venv_site_packages = (
"/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages"
)
if os.path.exists(venv_site_packages): if os.path.exists(venv_site_packages):
sys.path.insert(0, venv_site_packages) sys.path.insert(0, venv_site_packages)
@ -36,10 +39,10 @@ def create_test_image(width=2048, height=2048):
arr[::100, ::100] = [0, 255, 0] arr[::100, ::100] = [0, 255, 0]
# Convert numpy array to PIL Image # Convert numpy array to PIL Image
img = Image.fromarray(arr, 'RGB') img = Image.fromarray(arr, "RGB")
buffer = io.BytesIO() buffer = io.BytesIO()
img.save(buffer, format='PNG') img.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -48,7 +51,7 @@ def benchmark_resize_pil(image_data, target_size=(512, 512)):
img = Image.open(io.BytesIO(image_data)) img = Image.open(io.BytesIO(image_data))
img_resized = img.resize(target_size, Image.Resampling.LANCZOS) img_resized = img.resize(target_size, Image.Resampling.LANCZOS)
buffer = io.BytesIO() buffer = io.BytesIO()
img_resized.save(buffer, format='PNG') img_resized.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -57,7 +60,7 @@ def benchmark_thumbnail_pil(image_data, max_size=(256, 256)):
img = Image.open(io.BytesIO(image_data)) img = Image.open(io.BytesIO(image_data))
img.thumbnail(max_size, Image.Resampling.LANCZOS) img.thumbnail(max_size, Image.Resampling.LANCZOS)
buffer = io.BytesIO() buffer = io.BytesIO()
img.save(buffer, format='PNG') img.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -67,13 +70,13 @@ def benchmark_format_conversion(image_data):
# Convert to JPEG # Convert to JPEG
buffer_jpeg = io.BytesIO() buffer_jpeg = io.BytesIO()
img_rgb = img.convert('RGB') img_rgb = img.convert("RGB")
img_rgb.save(buffer_jpeg, format='JPEG', quality=85) img_rgb.save(buffer_jpeg, format="JPEG", quality=85)
# Convert back to PNG # Convert back to PNG
img2 = Image.open(io.BytesIO(buffer_jpeg.getvalue())) img2 = Image.open(io.BytesIO(buffer_jpeg.getvalue()))
buffer_png = io.BytesIO() buffer_png = io.BytesIO()
img2.save(buffer_png, format='PNG') img2.save(buffer_png, format="PNG")
return buffer_png.getvalue() return buffer_png.getvalue()
@ -87,9 +90,9 @@ def benchmark_base64_operations(image_data):
def run_benchmarks(): def run_benchmarks():
"""Run all image benchmarks.""" """Run all image benchmarks."""
print("="*80) print("=" * 80)
print("Image Processing Benchmark - Optimized") print("Image Processing Benchmark - Optimized")
print("="*80) print("=" * 80)
# Create test images of different sizes # Create test images of different sizes
test_sizes = [ test_sizes = [
@ -109,50 +112,65 @@ def run_benchmarks():
print(f"Image size: {size_mb:.2f} MB") print(f"Image size: {size_mb:.2f} MB")
# Benchmark resize # Benchmark resize
time_resize = timeit.timeit( time_resize = (
lambda: benchmark_resize_pil(image_data, (512, 512)), timeit.timeit(
number=10 lambda: benchmark_resize_pil(image_data, (512, 512)), number=10
) / 10 )
print(f"Resize to 512x512: {time_resize*1000:.2f} ms") / 10
)
print(f"Resize to 512x512: {time_resize * 1000:.2f} ms")
# Benchmark thumbnail # Benchmark thumbnail
time_thumbnail = timeit.timeit( time_thumbnail = (
lambda: benchmark_thumbnail_pil(image_data, (256, 256)), timeit.timeit(
number=10 lambda: benchmark_thumbnail_pil(image_data, (256, 256)),
) / 10 number=10,
print(f"Thumbnail to 256x256: {time_thumbnail*1000:.2f} ms") )
/ 10
)
print(f"Thumbnail to 256x256: {time_thumbnail * 1000:.2f} ms")
# Benchmark format conversion # Benchmark format conversion
time_format = timeit.timeit( time_format = (
lambda: benchmark_format_conversion(image_data), timeit.timeit(
number=5 lambda: benchmark_format_conversion(image_data), number=5
) / 5 )
print(f"Format conversion (PNG->JPEG->PNG): {time_format*1000:.2f} ms") / 5
)
print(
f"Format conversion (PNG->JPEG->PNG): {time_format * 1000:.2f} ms"
)
# Benchmark base64 # Benchmark base64
time_base64 = timeit.timeit( time_base64 = (
lambda: benchmark_base64_operations(image_data), timeit.timeit(
number=100 lambda: benchmark_base64_operations(image_data), number=100
) / 100 )
print(f"Base64 encode/decode: {time_base64*1000:.2f} ms") / 100
)
print(f"Base64 encode/decode: {time_base64 * 1000:.2f} ms")
results.append({ results.append(
'size': label, {
'image_mb': size_mb, "size": label,
'resize_ms': time_resize * 1000, "image_mb": size_mb,
'thumbnail_ms': time_thumbnail * 1000, "resize_ms": time_resize * 1000,
'format_ms': time_format * 1000, "thumbnail_ms": time_thumbnail * 1000,
'base64_ms': time_base64 * 1000, "format_ms": time_format * 1000,
}) "base64_ms": time_base64 * 1000,
}
)
# Summary # Summary
print("\n" + "="*80) print("\n" + "=" * 80)
print("SUMMARY") print("SUMMARY")
print("="*80) print("=" * 80)
for r in results: for r in results:
print(f"\n{r['size']} ({r['image_mb']:.2f} MB):") print(f"\n{r['size']} ({r['image_mb']:.2f} MB):")
print(f" Total processing time: {r['resize_ms'] + r['thumbnail_ms'] + r['format_ms']:.2f} ms") print(
f" Total processing time: {r['resize_ms'] + r['thumbnail_ms'] + r['format_ms']:.2f} ms"
)
if __name__ == '__main__': if __name__ == "__main__":
run_benchmarks() run_benchmarks()

View file

@ -1,20 +1,23 @@
#!/usr/bin/env python3
""" """
Benchmark for Odoo image processing utilities - Optimized version 2. Benchmark for Odoo image processing utilities - Optimized version 2.
Optimization: Use PIL's putdata() with pre-computed pixel list instead of per-pixel loops. Optimization: Use PIL's putdata() with pre-computed pixel list instead of per-pixel loops.
""" """
import sys
import os
import io
import timeit
from PIL import Image
import base64 import base64
import io
import os
import sys
import timeit
from PIL import Image
# Add odoo to path # Add odoo to path
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo")
# Activating venv programmatically by modifying path # Activating venv programmatically by modifying path
venv_site_packages = '/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages' venv_site_packages = (
"/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages"
)
if os.path.exists(venv_site_packages): if os.path.exists(venv_site_packages):
sys.path.insert(0, venv_site_packages) sys.path.insert(0, venv_site_packages)
@ -29,7 +32,7 @@ except ImportError as e:
def create_test_image(width=2048, height=2048): def create_test_image(width=2048, height=2048):
"""Create a test image in memory using PIL's native methods efficiently.""" """Create a test image in memory using PIL's native methods efficiently."""
# Create image with red background # Create image with red background
img = Image.new('RGB', (width, height), color=(255, 0, 0)) img = Image.new("RGB", (width, height), color=(255, 0, 0))
# Use load() to get pixel access object once # Use load() to get pixel access object once
pixels = img.load() pixels = img.load()
@ -40,7 +43,7 @@ def create_test_image(width=2048, height=2048):
pixels[i, j] = (0, 255, 0) pixels[i, j] = (0, 255, 0)
buffer = io.BytesIO() buffer = io.BytesIO()
img.save(buffer, format='PNG') img.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -49,7 +52,7 @@ def benchmark_resize_pil(image_data, target_size=(512, 512)):
img = Image.open(io.BytesIO(image_data)) img = Image.open(io.BytesIO(image_data))
img_resized = img.resize(target_size, Image.Resampling.LANCZOS) img_resized = img.resize(target_size, Image.Resampling.LANCZOS)
buffer = io.BytesIO() buffer = io.BytesIO()
img_resized.save(buffer, format='PNG') img_resized.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -58,7 +61,7 @@ def benchmark_thumbnail_pil(image_data, max_size=(256, 256)):
img = Image.open(io.BytesIO(image_data)) img = Image.open(io.BytesIO(image_data))
img.thumbnail(max_size, Image.Resampling.LANCZOS) img.thumbnail(max_size, Image.Resampling.LANCZOS)
buffer = io.BytesIO() buffer = io.BytesIO()
img.save(buffer, format='PNG') img.save(buffer, format="PNG")
return buffer.getvalue() return buffer.getvalue()
@ -68,13 +71,13 @@ def benchmark_format_conversion(image_data):
# Convert to JPEG # Convert to JPEG
buffer_jpeg = io.BytesIO() buffer_jpeg = io.BytesIO()
img_rgb = img.convert('RGB') img_rgb = img.convert("RGB")
img_rgb.save(buffer_jpeg, format='JPEG', quality=85) img_rgb.save(buffer_jpeg, format="JPEG", quality=85)
# Convert back to PNG # Convert back to PNG
img2 = Image.open(io.BytesIO(buffer_jpeg.getvalue())) img2 = Image.open(io.BytesIO(buffer_jpeg.getvalue()))
buffer_png = io.BytesIO() buffer_png = io.BytesIO()
img2.save(buffer_png, format='PNG') img2.save(buffer_png, format="PNG")
return buffer_png.getvalue() return buffer_png.getvalue()
@ -88,9 +91,9 @@ def benchmark_base64_operations(image_data):
def run_benchmarks(): def run_benchmarks():
"""Run all image benchmarks.""" """Run all image benchmarks."""
print("="*80) print("=" * 80)
print("Image Processing Benchmark - Optimized v2") print("Image Processing Benchmark - Optimized v2")
print("="*80) print("=" * 80)
# Create test images of different sizes # Create test images of different sizes
test_sizes = [ test_sizes = [
@ -110,50 +113,65 @@ def run_benchmarks():
print(f"Image size: {size_mb:.2f} MB") print(f"Image size: {size_mb:.2f} MB")
# Benchmark resize # Benchmark resize
time_resize = timeit.timeit( time_resize = (
lambda: benchmark_resize_pil(image_data, (512, 512)), timeit.timeit(
number=10 lambda: benchmark_resize_pil(image_data, (512, 512)), number=10
) / 10 )
print(f"Resize to 512x512: {time_resize*1000:.2f} ms") / 10
)
print(f"Resize to 512x512: {time_resize * 1000:.2f} ms")
# Benchmark thumbnail # Benchmark thumbnail
time_thumbnail = timeit.timeit( time_thumbnail = (
lambda: benchmark_thumbnail_pil(image_data, (256, 256)), timeit.timeit(
number=10 lambda: benchmark_thumbnail_pil(image_data, (256, 256)),
) / 10 number=10,
print(f"Thumbnail to 256x256: {time_thumbnail*1000:.2f} ms") )
/ 10
)
print(f"Thumbnail to 256x256: {time_thumbnail * 1000:.2f} ms")
# Benchmark format conversion # Benchmark format conversion
time_format = timeit.timeit( time_format = (
lambda: benchmark_format_conversion(image_data), timeit.timeit(
number=5 lambda: benchmark_format_conversion(image_data), number=5
) / 5 )
print(f"Format conversion (PNG->JPEG->PNG): {time_format*1000:.2f} ms") / 5
)
print(
f"Format conversion (PNG->JPEG->PNG): {time_format * 1000:.2f} ms"
)
# Benchmark base64 # Benchmark base64
time_base64 = timeit.timeit( time_base64 = (
lambda: benchmark_base64_operations(image_data), timeit.timeit(
number=100 lambda: benchmark_base64_operations(image_data), number=100
) / 100 )
print(f"Base64 encode/decode: {time_base64*1000:.2f} ms") / 100
)
print(f"Base64 encode/decode: {time_base64 * 1000:.2f} ms")
results.append({ results.append(
'size': label, {
'image_mb': size_mb, "size": label,
'resize_ms': time_resize * 1000, "image_mb": size_mb,
'thumbnail_ms': time_thumbnail * 1000, "resize_ms": time_resize * 1000,
'format_ms': time_format * 1000, "thumbnail_ms": time_thumbnail * 1000,
'base64_ms': time_base64 * 1000, "format_ms": time_format * 1000,
}) "base64_ms": time_base64 * 1000,
}
)
# Summary # Summary
print("\n" + "="*80) print("\n" + "=" * 80)
print("SUMMARY") print("SUMMARY")
print("="*80) print("=" * 80)
for r in results: for r in results:
print(f"\n{r['size']} ({r['image_mb']:.2f} MB):") print(f"\n{r['size']} ({r['image_mb']:.2f} MB):")
print(f" Total processing time: {r['resize_ms'] + r['thumbnail_ms'] + r['format_ms']:.2f} ms") print(
f" Total processing time: {r['resize_ms'] + r['thumbnail_ms'] + r['format_ms']:.2f} ms"
)
if __name__ == '__main__': if __name__ == "__main__":
run_benchmarks() run_benchmarks()

View file

@ -1,35 +1,41 @@
#!/usr/bin/env python3
"""Profile the cache key generation to find hotspots""" """Profile the cache key generation to find hotspots"""
import cProfile import cProfile
import pstats import pstats
from inspect import signature, Parameter from inspect import Parameter, signature
def build_cache_key_eval(method, cache_args): def build_cache_key_eval(method, cache_args):
"""Current implementation using eval""" """Current implementation using eval"""
args = ', '.join( args = ", ".join(
str(params.replace(annotation=Parameter.empty)) str(params.replace(annotation=Parameter.empty))
for params in signature(method).parameters.values() for params in signature(method).parameters.values()
) )
values = ['self._name', 'method', *cache_args] values = ["self._name", "method", *cache_args]
code = f"lambda {args}: ({''.join(a for arg in values for a in (arg, ','))})" code = (
return eval(code, {'method': method}) f"lambda {args}: ({''.join(a for arg in values for a in (arg, ','))})"
)
return eval(code, {"method": method})
class MockModel: class MockModel:
_name = 'test.model' _name = "test.model"
def simple_method(self, arg1, arg2): def simple_method(self, arg1, arg2):
return arg1 + arg2 return arg1 + arg2
# Profile the creation overhead # Profile the creation overhead
profiler = cProfile.Profile() profiler = cProfile.Profile()
profiler.enable() profiler.enable()
for i in range(10_000): for i in range(10_000):
_ = build_cache_key_eval(MockModel.simple_method, ['arg1', 'arg2']) _ = build_cache_key_eval(MockModel.simple_method, ["arg1", "arg2"])
profiler.disable() profiler.disable()
# Print stats # Print stats
stats = pstats.Stats(profiler) stats = pstats.Stats(profiler)
stats.sort_stats('cumulative') stats.sort_stats("cumulative")
print("=== Top 20 functions by cumulative time ===") print("=== Top 20 functions by cumulative time ===")
stats.print_stats(20) stats.print_stats(20)

View file

@ -1,29 +1,31 @@
#!/usr/bin/env python3
""" """
Unified profiler for image processing benchmark. Unified profiler for image processing benchmark.
Profiles both CPU (cProfile) and memory (tracemalloc). Profiles both CPU (cProfile) and memory (tracemalloc).
""" """
import cProfile import cProfile
import pstats
import io import io
import tracemalloc
import sys
import os import os
import pstats
import sys
import tracemalloc
# Add odoo to path # Add odoo to path
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo")
venv_site_packages = '/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages' venv_site_packages = (
"/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages"
)
if os.path.exists(venv_site_packages): if os.path.exists(venv_site_packages):
sys.path.insert(0, venv_site_packages) sys.path.insert(0, venv_site_packages)
# Import benchmark module # Import benchmark module
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash")
from benchmark_image import ( from benchmark_image import (
create_test_image, benchmark_base64_operations,
benchmark_format_conversion,
benchmark_resize_pil, benchmark_resize_pil,
benchmark_thumbnail_pil, benchmark_thumbnail_pil,
benchmark_format_conversion, create_test_image,
benchmark_base64_operations,
) )
@ -58,62 +60,67 @@ def profile_image_operations():
snapshot = tracemalloc.take_snapshot() snapshot = tracemalloc.take_snapshot()
# Print CPU profile # Print CPU profile
print("\n" + "="*80) print("\n" + "=" * 80)
print("CPU PROFILE (top 40 by cumulative time)") print("CPU PROFILE (top 40 by cumulative time)")
print("="*80) print("=" * 80)
s = io.StringIO() s = io.StringIO()
ps = pstats.Stats(profiler, stream=s) ps = pstats.Stats(profiler, stream=s)
ps.strip_dirs() ps.strip_dirs()
ps.sort_stats('cumulative') ps.sort_stats("cumulative")
ps.print_stats(40) ps.print_stats(40)
print(s.getvalue()) print(s.getvalue())
print("\n" + "="*80) print("\n" + "=" * 80)
print("CPU PROFILE (top 40 by total time)") print("CPU PROFILE (top 40 by total time)")
print("="*80) print("=" * 80)
s = io.StringIO() s = io.StringIO()
ps = pstats.Stats(profiler, stream=s) ps = pstats.Stats(profiler, stream=s)
ps.strip_dirs() ps.strip_dirs()
ps.sort_stats('tottime') ps.sort_stats("tottime")
ps.print_stats(40) ps.print_stats(40)
print(s.getvalue()) print(s.getvalue())
# Print memory profile # Print memory profile
print("\n" + "="*80) print("\n" + "=" * 80)
print("MEMORY PROFILE (top 40 allocations)") print("MEMORY PROFILE (top 40 allocations)")
print("="*80) print("=" * 80)
top_stats = snapshot.statistics('lineno') top_stats = snapshot.statistics("lineno")
for index, stat in enumerate(top_stats[:40], 1): for index, stat in enumerate(top_stats[:40], 1):
print(f"{index:2}. {stat}") print(f"{index:2}. {stat}")
print("\n" + "="*80) print("\n" + "=" * 80)
print("MEMORY PROFILE (grouped by file)") print("MEMORY PROFILE (grouped by file)")
print("="*80) print("=" * 80)
top_stats = snapshot.statistics('filename') top_stats = snapshot.statistics("filename")
for index, stat in enumerate(top_stats[:30], 1): for index, stat in enumerate(top_stats[:30], 1):
print(f"{index:2}. {stat}") print(f"{index:2}. {stat}")
# Save detailed profiles # Save detailed profiles
profiler.dump_stats('/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/cpu_profile.prof') profiler.dump_stats(
"/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/cpu_profile.prof"
)
# Summary # Summary
current, peak = tracemalloc.get_traced_memory() current, peak = tracemalloc.get_traced_memory()
print("\n" + "="*80) print("\n" + "=" * 80)
print("MEMORY SUMMARY") print("MEMORY SUMMARY")
print("="*80) print("=" * 80)
print(f"Current memory: {current / 1024 / 1024:.1f} MiB") print(f"Current memory: {current / 1024 / 1024:.1f} MiB")
print(f"Peak memory: {peak / 1024 / 1024:.1f} MiB") print(f"Peak memory: {peak / 1024 / 1024:.1f} MiB")
tracemalloc.stop() tracemalloc.stop()
# Save memory data # Save memory data
with open('/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/memory_baseline.txt', 'w') as f: with open(
"/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/memory_baseline.txt",
"w",
) as f:
f.write(f"Peak memory: {peak / 1024 / 1024:.1f} MiB\n") f.write(f"Peak memory: {peak / 1024 / 1024:.1f} MiB\n")
f.write(f"Current memory: {current / 1024 / 1024:.1f} MiB\n") f.write(f"Current memory: {current / 1024 / 1024:.1f} MiB\n")
return True return True
if __name__ == '__main__': if __name__ == "__main__":
success = profile_image_operations() success = profile_image_operations()
sys.exit(0 if success else 1) sys.exit(0 if success else 1)

View file

@ -1,29 +1,31 @@
#!/usr/bin/env python3
""" """
Unified profiler for optimized image processing benchmark. Unified profiler for optimized image processing benchmark.
Profiles both CPU (cProfile) and memory (tracemalloc). Profiles both CPU (cProfile) and memory (tracemalloc).
""" """
import cProfile import cProfile
import pstats
import io import io
import tracemalloc
import sys
import os import os
import pstats
import sys
import tracemalloc
# Add odoo to path # Add odoo to path
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo")
venv_site_packages = '/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages' venv_site_packages = (
"/Users/krrt7/Desktop/work/odoo_org/odoo/venv/lib/python3.14/site-packages"
)
if os.path.exists(venv_site_packages): if os.path.exists(venv_site_packages):
sys.path.insert(0, venv_site_packages) sys.path.insert(0, venv_site_packages)
# Import benchmark module # Import benchmark module
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash")
from benchmark_image_opt1 import ( from benchmark_image_opt1 import (
create_test_image, benchmark_base64_operations,
benchmark_format_conversion,
benchmark_resize_pil, benchmark_resize_pil,
benchmark_thumbnail_pil, benchmark_thumbnail_pil,
benchmark_format_conversion, create_test_image,
benchmark_base64_operations,
) )
@ -58,34 +60,36 @@ def profile_image_operations():
snapshot = tracemalloc.take_snapshot() snapshot = tracemalloc.take_snapshot()
# Print CPU profile # Print CPU profile
print("\n" + "="*80) print("\n" + "=" * 80)
print("CPU PROFILE (top 40 by cumulative time)") print("CPU PROFILE (top 40 by cumulative time)")
print("="*80) print("=" * 80)
s = io.StringIO() s = io.StringIO()
ps = pstats.Stats(profiler, stream=s) ps = pstats.Stats(profiler, stream=s)
ps.strip_dirs() ps.strip_dirs()
ps.sort_stats('cumulative') ps.sort_stats("cumulative")
ps.print_stats(40) ps.print_stats(40)
print(s.getvalue()) print(s.getvalue())
print("\n" + "="*80) print("\n" + "=" * 80)
print("CPU PROFILE (top 40 by total time)") print("CPU PROFILE (top 40 by total time)")
print("="*80) print("=" * 80)
s = io.StringIO() s = io.StringIO()
ps = pstats.Stats(profiler, stream=s) ps = pstats.Stats(profiler, stream=s)
ps.strip_dirs() ps.strip_dirs()
ps.sort_stats('tottime') ps.sort_stats("tottime")
ps.print_stats(40) ps.print_stats(40)
print(s.getvalue()) print(s.getvalue())
# Save detailed profiles # Save detailed profiles
profiler.dump_stats('/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/cpu_profile_opt1.prof') profiler.dump_stats(
"/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/cpu_profile_opt1.prof"
)
# Summary # Summary
current, peak = tracemalloc.get_traced_memory() current, peak = tracemalloc.get_traced_memory()
print("\n" + "="*80) print("\n" + "=" * 80)
print("MEMORY SUMMARY") print("MEMORY SUMMARY")
print("="*80) print("=" * 80)
print(f"Current memory: {current / 1024 / 1024:.1f} MiB") print(f"Current memory: {current / 1024 / 1024:.1f} MiB")
print(f"Peak memory: {peak / 1024 / 1024:.1f} MiB") print(f"Peak memory: {peak / 1024 / 1024:.1f} MiB")
@ -94,6 +98,6 @@ def profile_image_operations():
return True return True
if __name__ == '__main__': if __name__ == "__main__":
success = profile_image_operations() success = profile_image_operations()
sys.exit(0 if success else 1) sys.exit(0 if success else 1)

View file

@ -1,18 +1,18 @@
#!/usr/bin/env python3
""" """
Unified profiler for Odoo test_orm performance tests. Unified profiler for Odoo test_orm performance tests.
Profiles both CPU (cProfile) and memory (tracemalloc) in a single run. Profiles both CPU (cProfile) and memory (tracemalloc) in a single run.
""" """
import cProfile import cProfile
import pstats
import io import io
import tracemalloc import pstats
import sys import sys
import os import tracemalloc
import unittest import unittest
# Add odoo to path # Add odoo to path
sys.path.insert(0, '/Users/krrt7/Desktop/work/odoo_org/odoo') sys.path.insert(0, "/Users/krrt7/Desktop/work/odoo_org/odoo")
def profile_tests(): def profile_tests():
"""Run test_performance with CPU and memory profiling.""" """Run test_performance with CPU and memory profiling."""
@ -27,8 +27,8 @@ def profile_tests():
# Discover and run tests # Discover and run tests
loader = unittest.TestLoader() loader = unittest.TestLoader()
suite = loader.discover( suite = loader.discover(
start_dir='/Users/krrt7/Desktop/work/odoo_org/odoo/odoo/addons/test_orm/tests', start_dir="/Users/krrt7/Desktop/work/odoo_org/odoo/odoo/addons/test_orm/tests",
pattern='test_performance.py' pattern="test_performance.py",
) )
runner = unittest.TextTestRunner(verbosity=2) runner = unittest.TextTestRunner(verbosity=2)
@ -41,48 +41,50 @@ def profile_tests():
snapshot = tracemalloc.take_snapshot() snapshot = tracemalloc.take_snapshot()
# Print CPU profile # Print CPU profile
print("\n" + "="*80) print("\n" + "=" * 80)
print("CPU PROFILE (top 30 by cumulative time)") print("CPU PROFILE (top 30 by cumulative time)")
print("="*80) print("=" * 80)
s = io.StringIO() s = io.StringIO()
ps = pstats.Stats(profiler, stream=s) ps = pstats.Stats(profiler, stream=s)
ps.strip_dirs() ps.strip_dirs()
ps.sort_stats('cumulative') ps.sort_stats("cumulative")
ps.print_stats(30) ps.print_stats(30)
print(s.getvalue()) print(s.getvalue())
print("\n" + "="*80) print("\n" + "=" * 80)
print("CPU PROFILE (top 30 by total time)") print("CPU PROFILE (top 30 by total time)")
print("="*80) print("=" * 80)
s = io.StringIO() s = io.StringIO()
ps = pstats.Stats(profiler, stream=s) ps = pstats.Stats(profiler, stream=s)
ps.strip_dirs() ps.strip_dirs()
ps.sort_stats('tottime') ps.sort_stats("tottime")
ps.print_stats(30) ps.print_stats(30)
print(s.getvalue()) print(s.getvalue())
# Print memory profile # Print memory profile
print("\n" + "="*80) print("\n" + "=" * 80)
print("MEMORY PROFILE (top 30 allocations)") print("MEMORY PROFILE (top 30 allocations)")
print("="*80) print("=" * 80)
top_stats = snapshot.statistics('lineno') top_stats = snapshot.statistics("lineno")
for index, stat in enumerate(top_stats[:30], 1): for index, stat in enumerate(top_stats[:30], 1):
print(f"{index:2}. {stat}") print(f"{index:2}. {stat}")
print("\n" + "="*80) print("\n" + "=" * 80)
print("MEMORY PROFILE (grouped by file)") print("MEMORY PROFILE (grouped by file)")
print("="*80) print("=" * 80)
top_stats = snapshot.statistics('filename') top_stats = snapshot.statistics("filename")
for index, stat in enumerate(top_stats[:20], 1): for index, stat in enumerate(top_stats[:20], 1):
print(f"{index:2}. {stat}") print(f"{index:2}. {stat}")
# Save detailed profiles # Save detailed profiles
profiler.dump_stats('/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/cpu_profile.prof') profiler.dump_stats(
"/Users/krrt7/Desktop/work/odoo_org/odoo/.codeflash/cpu_profile.prof"
)
# Summary # Summary
print("\n" + "="*80) print("\n" + "=" * 80)
print("SUMMARY") print("SUMMARY")
print("="*80) print("=" * 80)
print(f"Tests run: {result.testsRun}") print(f"Tests run: {result.testsRun}")
print(f"Failures: {len(result.failures)}") print(f"Failures: {len(result.failures)}")
print(f"Errors: {len(result.errors)}") print(f"Errors: {len(result.errors)}")
@ -95,6 +97,7 @@ def profile_tests():
return result.wasSuccessful() return result.wasSuccessful()
if __name__ == '__main__':
if __name__ == "__main__":
success = profile_tests() success = profile_tests()
sys.exit(0 if success else 1) sys.exit(0 if success else 1)

View file

@ -1,9 +1,10 @@
#!/usr/bin/env python3
"""Parse python -X importtime output and produce a sorted breakdown.""" """Parse python -X importtime output and produce a sorted breakdown."""
import os
import re
import subprocess import subprocess
import sys import sys
import re
import os
def parse_importtime(stderr_lines): def parse_importtime(stderr_lines):
pattern = re.compile( pattern = re.compile(
@ -20,13 +21,15 @@ def parse_importtime(stderr_lines):
results.append((module, self_us, cumul_us, indent)) results.append((module, self_us, cumul_us, indent))
return results return results
def main(): def main():
target = sys.argv[1] if len(sys.argv) > 1 else "import rich" target = sys.argv[1] if len(sys.argv) > 1 else "import rich"
venv_python = os.path.expanduser("~/rich/.venv/bin/python") venv_python = os.path.expanduser("~/rich/.venv/bin/python")
proc = subprocess.run( proc = subprocess.run(
[venv_python, "-X", "importtime", "-c", target], [venv_python, "-X", "importtime", "-c", target],
capture_output=True, text=True capture_output=True,
text=True,
) )
entries = parse_importtime(proc.stderr.splitlines()) entries = parse_importtime(proc.stderr.splitlines())
entries.sort(key=lambda e: e[1], reverse=True) entries.sort(key=lambda e: e[1], reverse=True)
@ -43,5 +46,6 @@ def main():
f.write(f"{mod}\t{self_us}\t{cumul_us}\t{depth}\n") f.write(f"{mod}\t{self_us}\t{cumul_us}\t{depth}\n")
print(f"\nTSV written to {sys.argv[2]}") print(f"\nTSV written to {sys.argv[2]}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -8,19 +8,24 @@ Compares:
Usage: Usage:
python3.13 bench_runtime.py python3.13 bench_runtime.py
""" """
import timeit
import sys
import os import os
import sys
import timeit
sys.path.insert(0, os.path.expanduser("~/rich")) sys.path.insert(0, os.path.expanduser("~/rich"))
def bench(label, stmt, setup, number=500_000): def bench(label, stmt, setup, number=500_000):
times = timeit.repeat(stmt, setup, number=number, repeat=5) times = timeit.repeat(stmt, setup, number=number, repeat=5)
best = min(times) best = min(times)
per_call_ns = best / number * 1e9 per_call_ns = best / number * 1e9
print(f" {label}: {best*1000:.1f}ms total, {per_call_ns:.0f}ns/call ({number:,} iterations, best of 5)") print(
f" {label}: {best * 1000:.1f}ms total, {per_call_ns:.0f}ns/call ({number:,} iterations, best of 5)"
)
return best return best
print(f"Python {sys.version}") print(f"Python {sys.version}")
print(f"Rich path: {os.path.expanduser('~/rich')}") print(f"Rich path: {os.path.expanduser('~/rich')}")
print() print()
@ -38,11 +43,14 @@ opts_b = c.options.copy()
bench("__eq__ (equal objects)", "opts_a == opts_b", eq_setup) bench("__eq__ (equal objects)", "opts_a == opts_b", eq_setup)
bench("__eq__ (same object)", "opts_a == opts_a", eq_setup) bench("__eq__ (same object)", "opts_a == opts_a", eq_setup)
eq_setup_diff = eq_setup + """\ eq_setup_diff = (
eq_setup
+ """\
from rich.console import ConsoleDimensions from rich.console import ConsoleDimensions
opts_c = opts_b.copy() opts_c = opts_b.copy()
opts_c.size = ConsoleDimensions(999, 999) opts_c.size = ConsoleDimensions(999, 999)
""" """
)
bench("__eq__ (differ at size)", "opts_a == opts_c", eq_setup_diff) bench("__eq__ (differ at size)", "opts_a == opts_c", eq_setup_diff)
print() print()
@ -57,8 +65,11 @@ opts = c.options
""" """
bench("update(width=80)", "opts.update(width=80)", update_setup) bench("update(width=80)", "opts.update(width=80)", update_setup)
bench("update() no changes", "opts.update()", update_setup) bench("update() no changes", "opts.update()", update_setup)
bench("update(width=80, no_wrap=True, highlight=False)", bench(
"opts.update(width=80, no_wrap=True, highlight=False)", update_setup) "update(width=80, no_wrap=True, highlight=False)",
"opts.update(width=80, no_wrap=True, highlight=False)",
update_setup,
)
print() print()
# --- 3. _emoji_replace --- # --- 3. _emoji_replace ---
@ -68,8 +79,18 @@ import sys, os
sys.path.insert(0, os.path.expanduser("~/rich")) sys.path.insert(0, os.path.expanduser("~/rich"))
from rich._emoji_replace import _emoji_replace from rich._emoji_replace import _emoji_replace
""" """
bench("_emoji_replace (with emoji)", '_emoji_replace("Hello :wave: world :smile:")', emoji_setup, number=200_000) bench(
bench("_emoji_replace (no emoji)", '_emoji_replace("Hello world, no emojis here")', emoji_setup, number=200_000) "_emoji_replace (with emoji)",
'_emoji_replace("Hello :wave: world :smile:")',
emoji_setup,
number=200_000,
)
bench(
"_emoji_replace (no emoji)",
'_emoji_replace("Hello world, no emojis here")',
emoji_setup,
number=200_000,
)
print() print()
print("Done.") print("Done.")

View file

@ -8,19 +8,24 @@ Targets:
Usage: Usage:
cd ~/rich && ~/venv313/bin/python ~/bench/bench_runtime2.py cd ~/rich && ~/venv313/bin/python ~/bench/bench_runtime2.py
""" """
import timeit
import sys
import os import os
import sys
import timeit
sys.path.insert(0, os.path.expanduser("~/rich")) sys.path.insert(0, os.path.expanduser("~/rich"))
def bench(label, stmt, setup, number=500_000, repeat=7): def bench(label, stmt, setup, number=500_000, repeat=7):
times = timeit.repeat(stmt, setup, number=number, repeat=repeat) times = timeit.repeat(stmt, setup, number=number, repeat=repeat)
best = min(times) best = min(times)
per_call_ns = best / number * 1e9 per_call_ns = best / number * 1e9
print(f" {label}: {best*1000:.1f}ms/{number//1000}K calls, {per_call_ns:.0f}ns/call") print(
f" {label}: {best * 1000:.1f}ms/{number // 1000}K calls, {per_call_ns:.0f}ns/call"
)
return best return best
print(f"Python {sys.version}") print(f"Python {sys.version}")
print() print()
@ -32,43 +37,71 @@ from rich.style import Style
# --- 1. Style.__eq__ --- # --- 1. Style.__eq__ ---
print("=== Style.__eq__ ===") print("=== Style.__eq__ ===")
eq_setup = common_setup + """\ eq_setup = (
common_setup
+ """\
s1 = Style(bold=True, color="red") s1 = Style(bold=True, color="red")
s2 = Style(bold=True, color="red") s2 = Style(bold=True, color="red")
# Force hash caching # Force hash caching
hash(s1); hash(s2) hash(s1); hash(s2)
""" """
)
bench("identity (s1 == s1)", "s1 == s1", eq_setup, number=1_000_000) bench("identity (s1 == s1)", "s1 == s1", eq_setup, number=1_000_000)
bench("equal (s1 == s2)", "s1 == s2", eq_setup, number=1_000_000) bench("equal (s1 == s2)", "s1 == s2", eq_setup, number=1_000_000)
bench("not-equal (s1 != Style())", "s1 != Style()", eq_setup + "s3 = Style(); hash(s3)\n", number=1_000_000) bench(
"not-equal (s1 != Style())",
"s1 != Style()",
eq_setup + "s3 = Style(); hash(s3)\n",
number=1_000_000,
)
print() print()
# --- 2. Style.combine --- # --- 2. Style.combine ---
print("=== Style.combine ===") print("=== Style.combine ===")
combine_setup = common_setup + """\ combine_setup = (
common_setup
+ """\
styles = [Style(bold=True), Style(color="red"), Style(italic=True)] styles = [Style(bold=True), Style(color="red"), Style(italic=True)]
""" """
bench("combine(3 styles)", "Style.combine(styles)", combine_setup, number=200_000) )
bench(
"combine(3 styles)", "Style.combine(styles)", combine_setup, number=200_000
)
combine_setup_2 = common_setup + """\ combine_setup_2 = (
common_setup
+ """\
styles = [Style(bold=True), Style(color="red")] styles = [Style(bold=True), Style(color="red")]
""" """
bench("combine(2 styles)", "Style.combine(styles)", combine_setup_2, number=200_000) )
bench(
"combine(2 styles)",
"Style.combine(styles)",
combine_setup_2,
number=200_000,
)
print() print()
# --- 3. Style.chain --- # --- 3. Style.chain ---
print("=== Style.chain ===") print("=== Style.chain ===")
chain_setup = common_setup + """\ chain_setup = (
common_setup
+ """\
s1 = Style(bold=True) s1 = Style(bold=True)
s2 = Style(color="red") s2 = Style(color="red")
s3 = Style(italic=True) s3 = Style(italic=True)
""" """
bench("chain(3 styles)", "Style.chain(s1, s2, s3)", chain_setup, number=200_000) )
bench(
"chain(3 styles)", "Style.chain(s1, s2, s3)", chain_setup, number=200_000
)
print() print()
# --- 4. Segment.simplify --- # --- 4. Segment.simplify ---
print("=== Segment.simplify ===") print("=== Segment.simplify ===")
simplify_setup = common_setup + """\ simplify_setup = (
common_setup
+ """\
from rich.segment import Segment from rich.segment import Segment
style_a = Style(bold=True, color="red") style_a = Style(bold=True, color="red")
# Same object reference (common case) # Same object reference (common case)
@ -80,20 +113,45 @@ segs_equal = [Segment("hello ", style_a), Segment("world", style_b), Segment("!
style_c = Style(italic=True) style_c = Style(italic=True)
segs_diff = [Segment("hello ", style_a), Segment("world", style_c), Segment("! ", style_a)] segs_diff = [Segment("hello ", style_a), Segment("world", style_c), Segment("! ", style_a)]
""" """
bench("simplify (identity styles)", "list(Segment.simplify(segs_identity))", simplify_setup, number=200_000) )
bench("simplify (equal styles)", "list(Segment.simplify(segs_equal))", simplify_setup, number=200_000) bench(
bench("simplify (diff styles)", "list(Segment.simplify(segs_diff))", simplify_setup, number=200_000) "simplify (identity styles)",
"list(Segment.simplify(segs_identity))",
simplify_setup,
number=200_000,
)
bench(
"simplify (equal styles)",
"list(Segment.simplify(segs_equal))",
simplify_setup,
number=200_000,
)
bench(
"simplify (diff styles)",
"list(Segment.simplify(segs_diff))",
simplify_setup,
number=200_000,
)
print() print()
# --- 5. E2E Console.print --- # --- 5. E2E Console.print ---
print("=== E2E Console.print ===") print("=== E2E Console.print ===")
e2e_setup = common_setup + """\ e2e_setup = (
common_setup
+ """\
from rich.console import Console from rich.console import Console
from rich.text import Text from rich.text import Text
c = Console(file=open(os.devnull, "w"), color_system="truecolor") c = Console(file=open(os.devnull, "w"), color_system="truecolor")
markup = "[bold red]Error:[/bold red] Something [italic]went wrong[/italic] in [blue underline]module.py[/blue underline]:42" markup = "[bold red]Error:[/bold red] Something [italic]went wrong[/italic] in [blue underline]module.py[/blue underline]:42"
""" """
bench("Console.print(markup)", "c.print(markup)", e2e_setup, number=5_000, repeat=5) )
bench(
"Console.print(markup)",
"c.print(markup)",
e2e_setup,
number=5_000,
repeat=5,
)
print() print()
print("Done.") print("Done.")

View file

@ -1,17 +1,22 @@
"""Benchmark Text hot paths: construction, copy, divide, render.""" """Benchmark Text hot paths: construction, copy, divide, render."""
import timeit
import sys
import os import os
import sys
import timeit
sys.path.insert(0, os.path.expanduser("~/rich")) sys.path.insert(0, os.path.expanduser("~/rich"))
def bench(label, stmt, setup, number=200_000): def bench(label, stmt, setup, number=200_000):
times = timeit.repeat(stmt, setup, number=number, repeat=5) times = timeit.repeat(stmt, setup, number=number, repeat=5)
best = min(times) best = min(times)
per_call_ns = best / number * 1e9 per_call_ns = best / number * 1e9
print(f" {label}: {best*1000:.1f}ms total, {per_call_ns:.0f}ns/call ({number:,} iters, best of 5)") print(
f" {label}: {best * 1000:.1f}ms total, {per_call_ns:.0f}ns/call ({number:,} iters, best of 5)"
)
return best return best
print(f"Python {sys.version}") print(f"Python {sys.version}")
print() print()
@ -26,12 +31,18 @@ from rich.console import Console
# --- Text construction --- # --- Text construction ---
print("=== Text() construction ===") print("=== Text() construction ===")
bench("Text('hello world')", "Text('hello world')", common) bench("Text('hello world')", "Text('hello world')", common)
bench("Text('hello world', style='bold')", "Text('hello world', style='bold')", common) bench(
"Text('hello world', style='bold')",
"Text('hello world', style='bold')",
common,
)
print() print()
# --- Text.copy --- # --- Text.copy ---
print("=== Text.copy() ===") print("=== Text.copy() ===")
copy_setup = common + "t = Text('hello world', style='bold')\nt.stylize('red', 0, 5)\n" copy_setup = (
common + "t = Text('hello world', style='bold')\nt.stylize('red', 0, 5)\n"
)
bench("copy()", "t.copy()", copy_setup) bench("copy()", "t.copy()", copy_setup)
print() print()
@ -43,13 +54,20 @@ print()
# --- Text.divide --- # --- Text.divide ---
print("=== Text.divide() ===") print("=== Text.divide() ===")
div_setup = common + "t = Text('hello world, this is a longer text for divide testing')\nt.stylize('bold', 0, 5)\nt.stylize('red', 6, 11)\n" div_setup = (
bench("divide([10, 20, 30])", "t.divide([10, 20, 30])", div_setup, number=100_000) common
+ "t = Text('hello world, this is a longer text for divide testing')\nt.stylize('bold', 0, 5)\nt.stylize('red', 6, 11)\n"
)
bench(
"divide([10, 20, 30])", "t.divide([10, 20, 30])", div_setup, number=100_000
)
print() print()
# --- Text.render --- # --- Text.render ---
print("=== Text.render() ===") print("=== Text.render() ===")
render_setup = common + """\ render_setup = (
common
+ """\
c = Console(width=80) c = Console(width=80)
t0 = Text('hello world') t0 = Text('hello world')
t1 = Text('hello world') t1 = Text('hello world')
@ -58,6 +76,7 @@ t2 = Text('hello world')
t2.stylize('bold', 0, 5) t2.stylize('bold', 0, 5)
t2.stylize('red', 6, 11) t2.stylize('red', 6, 11)
""" """
)
bench("render (no spans)", "list(t0.render(c))", render_setup, number=100_000) bench("render (no spans)", "list(t0.render(c))", render_setup, number=100_000)
bench("render (1 span)", "list(t1.render(c))", render_setup, number=100_000) bench("render (1 span)", "list(t1.render(c))", render_setup, number=100_000)
bench("render (2 spans)", "list(t2.render(c))", render_setup, number=100_000) bench("render (2 spans)", "list(t2.render(c))", render_setup, number=100_000)
@ -65,11 +84,24 @@ print()
# --- E2E Console.print --- # --- E2E Console.print ---
print("=== Console.print() E2E ===") print("=== Console.print() E2E ===")
print_setup = common + """\ print_setup = (
common
+ """\
import io import io
c = Console(file=io.StringIO(), width=80) c = Console(file=io.StringIO(), width=80)
""" """
bench("print('hello')", "c.file.seek(0); c.print('hello')", print_setup, number=50_000) )
bench("print('[bold]hello[/bold]')", "c.file.seek(0); c.print('[bold]hello[/bold]')", print_setup, number=50_000) bench(
"print('hello')",
"c.file.seek(0); c.print('hello')",
print_setup,
number=50_000,
)
bench(
"print('[bold]hello[/bold]')",
"c.file.seek(0); c.print('[bold]hello[/bold]')",
print_setup,
number=50_000,
)
print("\nDone.") print("\nDone.")

View file

@ -40,9 +40,15 @@ REPO_ROOT = Path(__file__).resolve().parent.parent
# Single small doc to measure the hot path we're optimizing. # Single small doc to measure the hot path we're optimizing.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
FAST_FIXTURES = [ FAST_FIXTURES = [
("unstructured-api/sample-docs/embedded-images-tables.pdf", "img-tables-1p"), (
"unstructured-api/sample-docs/embedded-images-tables.pdf",
"img-tables-1p",
),
("unstructured_prop/tests/test_files/multi-column-2p.pdf", "multicol-2p"), ("unstructured_prop/tests/test_files/multi-column-2p.pdf", "multicol-2p"),
("unstructured-api/sample-docs/layout-parser-paper-with-table.pdf", "table-1p"), (
"unstructured-api/sample-docs/layout-parser-paper-with-table.pdf",
"table-1p",
),
] ]
HIRES_FIXTURE = ( HIRES_FIXTURE = (
@ -80,7 +86,9 @@ async def batch_serial(filepaths: list[Path], strategy: str) -> list[int]:
return results return results
async def batch_concurrent(filepaths: list[Path], strategy: str, concurrency: int) -> list[int]: async def batch_concurrent(
filepaths: list[Path], strategy: str, concurrency: int
) -> list[int]:
sem = asyncio.Semaphore(concurrency) sem = asyncio.Semaphore(concurrency)
async def worker(fp: Path) -> int: async def worker(fp: Path) -> int:

View file

@ -3,14 +3,14 @@ repos:
hooks: hooks:
- id: ruff-check - id: ruff-check
name: ruff check name: ruff check
entry: uv run ruff check packages/ entry: uv run ruff check
language: system language: system
pass_filenames: false pass_filenames: false
types: [python] types: [python]
- id: ruff-format - id: ruff-format
name: ruff format name: ruff format
entry: uv run ruff format --check packages/ entry: uv run ruff format --check
language: system language: system
pass_filenames: false pass_filenames: false
types: [python] types: [python]

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""LLM-graded eval scorer. """LLM-graded eval scorer.
Feeds the manifest rubric and full conversation to Claude, which scores Feeds the manifest rubric and full conversation to Claude, which scores

View file

@ -1,7 +1,6 @@
"""Batch processing module with async interface.""" """Batch processing module with async interface."""
async def async_batch_process(items: list[dict]) -> list[dict]: async def async_batch_process(items: list[dict]) -> list[dict]:
"""Process a batch of items, deduplicating by ID. """Process a batch of items, deduplicating by ID.

View file

@ -50,7 +50,6 @@ src = [
"packages/github-app", "packages/github-app",
] ]
extend-exclude = [ extend-exclude = [
".codeflash/",
"packages/codeflash-python/tests/code_to_optimize", "packages/codeflash-python/tests/code_to_optimize",
"packages/codeflash-python/src/codeflash_python/ai/_tabulate.py", "packages/codeflash-python/src/codeflash_python/ai/_tabulate.py",
] ]
@ -235,6 +234,107 @@ ignore = [
"W", # whitespace issues in test data strings "W", # whitespace issues in test data strings
] ]
".codeflash/**" = [
"B007", # unused loop variable in benchmarks
"B023", # function binding loop variable in timeit lambdas
"BLE001", # broad Exception catches in scripts
"C901", # complex benchmark functions
"E402", # imports after sys.path manipulation
"F841", # unused locals in benchmark setup
"PERF102", # dict values iteration fine in scripts
"PERF401", # list comprehension not always clearer
"PLC0415", # imports in functions fine in scripts
"PLR0912", # many branches in benchmark harnesses
"PLR0913", # many args in benchmark functions
"PLR0915", # many statements in benchmark functions
"PLR2004", # magic values in benchmarks
"PLW1510", # subprocess.run without check
"PTH110", # os.path.exists in scripts
"PTH111", # os.path.expanduser in scripts
"PT006", # parametrize types fine in benchmarks
"PTH123", # open() in scripts
"S101", # assert in scripts
"S108", # temp paths in scripts
"S307", # eval used for benchmark code generation
"S311", # random in benchmarks
"S324", # hashlib in benchmarks
"S603", # subprocess calls in scripts
"S607", # partial executable path in scripts
"SIM105", # contextlib.suppress suggestion
"SIM110", # any() suggestion
"SLF001", # private member access in benchmarks
"T201", # print is the output mechanism
]
"scripts/*" = [
"BLE001", # broad Exception catches in scripts
"C901", # complex analysis functions
"FA102", # future annotations not needed in scripts
"PERF102", # dict keys iteration fine in scripts
"PERF203", # try-except in loop fine in scripts
"PERF401", # list comprehension not always clearer
"PLC0415", # imports in functions fine in scripts
"PLR0911", # many return statements in analysis
"PLR0912", # many branches in analysis scripts
"PLR0913", # many args in script functions
"PLR0915", # many statements in analysis scripts
"PLR2004", # magic values in scripts
"PLW1510", # subprocess.run without check
"PTH111", # os.path.expanduser in scripts
"PTH117", # os.path.isabs in scripts
"PTH118", # os.path.join in scripts
"PTH123", # open() in scripts
"PTH208", # os.listdir in scripts
"S112", # try-except-continue in scripts
"S311", # random in scripts
"S324", # hashlib in scripts
"S603", # subprocess calls in scripts
"S607", # partial executable path in scripts
"SIM102", # collapsible if fine in scripts
"SIM105", # contextlib.suppress suggestion
"SIM110", # any() suggestion
"T201", # print is the output mechanism
"TC003", # type-checking imports fine in scripts
"TRY300", # try-except-return fine in scripts
]
"evals/**" = [
"B007", # unused loop variable
"C401", # generator to set fine in evals
"C901", # complex test/eval functions
"F841", # unused locals fine in eval setup
"FA102", # future annotations not needed in evals
"PERF102", # dict values iteration fine in evals
"PERF203", # try-except in loop fine in evals
"PERF401", # list comprehension not always clearer
"PLC0206", # dict keys iteration fine in evals
"PLC0415", # imports in functions fine in evals
"PLR0911", # many return statements
"PLR0912", # many branches
"PLR0913", # many args in eval functions
"PLR0915", # many statements
"PLR2004", # magic values in eval scenarios
"PLW1510", # subprocess.run without check
"PT006", # parametrize types fine in evals
"PTH123", # open() fine in evals
"RUF003", # ambiguous unicode fine in test data
"S101", # assert in evals
"S311", # random fine in evals
"S324", # hashlib fine in evals
"S603", # subprocess calls in evals
"S607", # partial executable path in evals
"SIM105", # contextlib.suppress suggestion
"SIM110", # any() suggestion
"SLF001", # private member access in evals
"T201", # print in eval harnesses
]
"plugin/**" = [
"B007", # unused loop variable in reference scripts
"PTH100", # os.path.abspath in scripts
"PTH110", # os.path.exists in scripts
"PTH123", # open() in scripts
"S108", # temp paths in scripts
"SLF001", # private member access in plugin scripts
"T201", # print is the output mechanism
]
"reports/*" = [ "reports/*" = [
"C901", # complex layout builders are expected in Dash apps "C901", # complex layout builders are expected in Dash apps
"E501", # long strings in inline HTML "E501", # long strings in inline HTML

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
# /// script # /// script
# requires-python = ">=3.11" # requires-python = ">=3.11"
# /// # ///

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
# /// script # /// script
# requires-python = ">=3.11" # requires-python = ">=3.11"
# /// # ///

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python3
# /// script # /// script
# requires-python = ">=3.11" # requires-python = ">=3.11"
# /// # ///