Update repo
Python CI / test (push) Failing after 6m22s

This commit is contained in:
2025-11-04 19:25:44 +01:00
parent 25e7f1423e
commit 993095caf9
10 changed files with 525 additions and 59 deletions
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""
Debug script to test text positioning in the line breaking system
"""
import sys
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
# Add pyWebLayout to path
sys.path.insert(0, str(Path(__file__).parent))
from pyWebLayout.style import Font
from pyWebLayout.concrete.text import Text, Line
from pyWebLayout.style.layout import Alignment
def test_simple_text_rendering():
"""Test basic text rendering to debug positioning issues"""
# Create a simple image
width, height = 300, 200
image = Image.new('RGB', (width, height), 'white')
draw = ImageDraw.Draw(image)
# Draw a border for reference
draw.rectangle([0, 0, width-1, height-1], outline=(200, 200, 200), width=2)
# Create a font
font = Font(font_size=12)
# Test 1: Direct PIL text rendering
print("Test 1: Direct PIL text rendering")
draw.text((10, 30), "Direct PIL text", font=font.font, fill=(0, 0, 0))
# Test 2: Using our Text class
print("Test 2: Using Text class")
text_obj = Text("Text class rendering", font, draw)
text_obj.set_origin([10, 60]) # Set position
print(f"Text origin: {text_obj.origin}")
text_obj.render()
# Test 3: Using Line class
print("Test 3: Using Line class")
line = Line(
spacing=(2, 6),
origin=(10, 100),
size=(280, 20),
draw=draw,
font=font,
halign=Alignment.LEFT
)
# Create a simple word to add to the line
from pyWebLayout.abstract.inline import Word
word = Word("Line class rendering", font)
success, overflow = line.add_word(word)
print(f"Word added successfully: {success}")
print(f"Line origin: {line.origin}")
print(f"Line baseline: {line._baseline}")
print(f"Text objects in line: {len(line.text_objects)}")
if line.text_objects:
for i, text in enumerate(line.text_objects):
print(f" Text {i}: '{text.text}' at origin {text.origin}")
line.render()
# Save the debug image
image.save("debug_text_positioning.png")
print("Debug image saved as debug_text_positioning.png")
if __name__ == "__main__":
test_simple_text_rendering()
+434
View File
@@ -0,0 +1,434 @@
#!/usr/bin/env python3
"""
Simple EPUB page renderer tool.
This tool uses the pyWebLayout epub_reader and layout modules to:
1. Load an EPUB file
2. Render the first X pages according to command line arguments
3. Save the pages as PNG images
Usage:
python epub_page_renderer.py book.epub --pages 5 --output-dir rendered_pages
"""
import os
import sys
import argparse
from pathlib import Path
from typing import Optional, List
# Add the parent directory to sys.path to import pyWebLayout
sys.path.insert(0, str(Path(__file__).parent.parent))
try:
from pyWebLayout.io.readers.epub_reader import read_epub
from pyWebLayout.layout.ereader_layout import BidirectionalLayouter, RenderingPosition
from pyWebLayout.layout.document_layouter import paragraph_layouter
from pyWebLayout.concrete.page import Page
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.style.fonts import Font
from pyWebLayout.abstract.block import Block
from PIL import Image, ImageDraw
except ImportError as e:
print(f"Error importing required modules: {e}")
print("Make sure pyWebLayout is properly installed and PIL is available")
sys.exit(1)
def render_page_to_image(page: Page) -> Image.Image:
"""
Render a Page object to a PIL Image using pyWebLayout's built-in rendering.
Args:
page: The Page object to render
Returns:
PIL Image object
"""
try:
# Use the Page's built-in render method
rendered_image = page.render()
if isinstance(rendered_image, Image.Image):
return rendered_image
else:
# If render() doesn't return a PIL Image, create error image
error_image = Image.new('RGB', page.size, 'white')
draw = ImageDraw.Draw(error_image)
draw.text((20, 20), "Error: Page.render() did not return PIL Image", fill='red')
return error_image
except Exception as e:
# Create error image if rendering fails
error_image = Image.new('RGB', page.size, 'white')
draw = ImageDraw.Draw(error_image)
draw.text((20, 20), f"Rendering error: {str(e)}", fill='red')
print(f"Warning: Error rendering page: {e}")
return error_image
def extract_text_from_page(page: Page) -> str:
"""
Extract text content from a Page object for verification purposes.
Args:
page: The Page object to extract text from
Returns:
String containing the page's text content
"""
text_lines = []
text_lines.append(f"=== PAGE CONTENT ===")
text_lines.append("")
try:
# Recursively extract text from page children
def extract_from_element(element, indent_level=0):
indent = " " * indent_level
# Import abstract block types
from pyWebLayout.abstract.block import Paragraph, Heading, HList, Table, Image as AbstractImage
from pyWebLayout.concrete.text import Line
# Handle Line objects (concrete)
if isinstance(element, Line):
line_text = []
if hasattr(element, '_text_objects') and element._text_objects:
for text_obj in element._text_objects:
if hasattr(text_obj, 'text'):
line_text.append(str(text_obj.text))
if line_text:
text_lines.append(f"{indent}{' '.join(line_text)}")
# Handle abstract block objects
elif isinstance(element, (Paragraph, Heading)):
# Extract text from paragraph/heading
paragraph_text = extract_text_from_paragraph(element)
if paragraph_text:
block_type = "HEADING" if isinstance(element, Heading) else "PARAGRAPH"
text_lines.append(f"{indent}{block_type}: {paragraph_text}")
elif isinstance(element, HList):
text_lines.append(f"{indent}LIST:")
# Extract text from list items
try:
for item in element.items():
item_text = extract_text_from_paragraph(item)
if item_text:
text_lines.append(f"{indent} - {item_text}")
except:
text_lines.append(f"{indent} (List content extraction failed)")
elif isinstance(element, Table):
text_lines.append(f"{indent}[TABLE]")
elif isinstance(element, AbstractImage):
alt_text = getattr(element, 'alt_text', '')
src = getattr(element, 'source', 'Unknown')
text_lines.append(f"{indent}[IMAGE: {alt_text or src}]")
# Handle containers with children
elif hasattr(element, '_children') and element._children:
for child in element._children:
extract_from_element(child, indent_level + 1)
# Handle text elements
elif hasattr(element, 'text'):
text = str(element.text).strip()
if text:
text_lines.append(f"{indent}{text}")
# Handle other object types by showing their class name
else:
class_name = element.__class__.__name__
text_lines.append(f"{indent}[{class_name}]")
# Helper function to extract text from paragraph-like objects
def extract_text_from_paragraph(para_obj):
words = []
try:
# Try to get words from the paragraph
if hasattr(para_obj, 'words_iter') and callable(para_obj.words_iter):
for _, word in para_obj.words_iter():
if hasattr(word, 'text'):
words.append(word.text)
else:
words.append(str(word))
elif hasattr(para_obj, '_words'):
# Direct access to words list
for word in para_obj._words:
if hasattr(word, 'text'):
words.append(word.text)
else:
words.append(str(word))
except Exception as e:
return f"(Text extraction error: {str(e)})"
return ' '.join(words) if words else "(No text)"
# Extract text from page children
if hasattr(page, '_children'):
for child in page._children:
extract_from_element(child)
# If no text was extracted, add a note
if len(text_lines) <= 2: # Only header and empty line
text_lines.append("(No text content found)")
except Exception as e:
text_lines.append(f"Error extracting text: {str(e)}")
import traceback
text_lines.append(traceback.format_exc())
return "\n".join(text_lines)
def get_all_blocks_from_book(book) -> List[Block]:
"""
Extract all blocks from all chapters in the book.
Args:
book: The Book object from epub_reader
Returns:
List of all Block objects
"""
all_blocks = []
# Iterate through all chapters
for chapter in book.chapters:
# Get blocks from the chapter
if hasattr(chapter, '_blocks'):
all_blocks.extend(chapter._blocks)
return all_blocks
def main():
"""Main function to handle command line arguments and process the EPUB."""
parser = argparse.ArgumentParser(
description='Render EPUB pages to images using pyWebLayout',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python epub_page_renderer.py book.epub --pages 5
python epub_page_renderer.py book.epub --pages 10 --output-dir my_output --width 600 --height 800
"""
)
parser.add_argument(
'epub_file',
help='Path to the EPUB file to render'
)
parser.add_argument(
'--pages', '-p',
type=int,
default=5,
help='Number of pages to render (default: 5)'
)
parser.add_argument(
'--output-dir', '-o',
default='rendered_pages',
help='Output directory for rendered images (default: rendered_pages)'
)
parser.add_argument(
'--width', '-w',
type=int,
default=800,
help='Page width in pixels (default: 800)'
)
parser.add_argument(
'--height', '-t',
type=int,
default=1000,
help='Page height in pixels (default: 1000)'
)
parser.add_argument(
'--margin', '-m',
type=int,
default=40,
help='Page margin in pixels (default: 40)'
)
parser.add_argument(
'--align', '-a',
choices=['left', 'justify'],
default='left',
help='Text alignment: left or justify (default: left)'
)
args = parser.parse_args()
# Validate arguments
if not os.path.exists(args.epub_file):
print(f"Error: EPUB file '{args.epub_file}' not found")
return 1
if args.pages <= 0:
print("Error: Number of pages must be positive")
return 1
# Create output directory
try:
os.makedirs(args.output_dir, exist_ok=True)
except OSError as e:
print(f"Error creating output directory: {e}")
return 1
print(f"Loading EPUB file: {args.epub_file}")
# Load the EPUB file
try:
book = read_epub(args.epub_file)
print(f"Successfully loaded EPUB: {book.get_title() or 'Unknown Title'}")
# Print book information
author = book.get_metadata('AUTHOR')
if author:
print(f"Author: {author}")
print(f"Chapters: {len(book.chapters) if hasattr(book, 'chapters') else 'Unknown'}")
except Exception as e:
print(f"Error loading EPUB file: {e}")
import traceback
traceback.print_exc()
return 1
# Extract all blocks from the book
print("Extracting content blocks...")
try:
all_blocks = get_all_blocks_from_book(book)
print(f"Extracted {len(all_blocks)} content blocks")
if not all_blocks:
print("No content blocks found in EPUB. The book might be empty.")
return 1
# Apply alignment setting to all paragraphs and headings
from pyWebLayout.abstract.block import Paragraph, Heading
from pyWebLayout.style.alignment import Alignment
alignment = Alignment.JUSTIFY if args.align == 'justify' else Alignment.LEFT
print(f"Applying {args.align} alignment to all text blocks...")
# Note: We'll pass alignment to the layouter which will handle it during rendering
# The alignment is applied at the Line level in paragraph_layouter
except Exception as e:
print(f"Error extracting blocks: {e}")
import traceback
traceback.print_exc()
return 1
# Set up page style and layouter
page_size = (args.width, args.height)
page_style = PageStyle(
background_color=(255, 255, 255),
border_width=args.margin,
border_color=(200, 200, 200),
padding=(10, 10, 10, 10), # top, right, bottom, left
line_spacing=5,
inter_block_spacing=15
)
print(f"Setting up layouter with page size {page_size} and {args.align} alignment")
try:
layouter = BidirectionalLayouter(
blocks=all_blocks,
page_style=page_style,
page_size=page_size,
alignment_override=alignment
)
except Exception as e:
print(f"Error setting up layouter: {e}")
import traceback
traceback.print_exc()
return 1
# Render pages
print(f"Rendering up to {args.pages} pages...")
try:
pages = []
current_position = RenderingPosition() # Start from beginning
for page_num in range(args.pages):
print(f"Rendering page {page_num + 1}/{args.pages}...")
try:
# Render the page
page, next_position = layouter.render_page_forward(current_position)
pages.append(page)
# Check if we've reached the end of the document
if next_position.block_index >= len(all_blocks):
print(f"Reached end of document after {page_num + 1} pages")
break
# Update position for next page
current_position = next_position
except Exception as e:
print(f"Error rendering page {page_num + 1}: {e}")
import traceback
traceback.print_exc()
break
if not pages:
print("No pages were generated.")
return 1
print(f"Generated {len(pages)} pages")
# Save each page to an image and extract text
for i, page in enumerate(pages):
print(f"Saving page {i + 1}/{len(pages)}...")
try:
# Create image from page using pyWebLayout's built-in rendering
image = render_page_to_image(page)
# Save the image
output_filename = f"page_{i + 1:03d}.png"
output_path = os.path.join(args.output_dir, output_filename)
image.save(output_path, 'PNG')
# Extract and save text content for verification
page_text = extract_text_from_page(page)
text_filename = f"page_{i + 1:03d}.txt"
text_path = os.path.join(args.output_dir, text_filename)
with open(text_path, 'w', encoding='utf-8') as f:
f.write(page_text)
print(f"Saved: {output_path} and {text_path}")
except Exception as e:
print(f"Error saving page {i + 1}: {e}")
import traceback
traceback.print_exc()
continue
print(f"\nCompleted! Rendered {len(pages)} pages to {args.output_dir}")
# Calculate progress through the book
if len(all_blocks) > 0:
progress = (current_position.block_index / len(all_blocks)) * 100
print(f"Progress through book: {progress:.1f}%")
except Exception as e:
print(f"Error during pagination/rendering: {e}")
import traceback
traceback.print_exc()
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,380 @@
#!/usr/bin/env python3
"""
EPUB page renderer using DocumentLayouter.
This tool uses pyWebLayout's DocumentLayouter to render EPUB content:
1. Load an EPUB file
2. Extract all blocks (paragraphs, images, etc.)
3. Use DocumentLayouter to layout blocks on pages
4. Save the pages as PNG images
Usage:
python epub_page_renderer_documentlayouter.py book.epub --pages 5 --output-dir rendered_pages
"""
import os
import sys
import argparse
from pathlib import Path
from typing import Optional, List, Tuple
# Add the parent directory to sys.path to import pyWebLayout
sys.path.insert(0, str(Path(__file__).parent.parent))
try:
from pyWebLayout.io.readers.epub_reader import read_epub
from pyWebLayout.layout.document_layouter import DocumentLayouter, paragraph_layouter, image_layouter
from pyWebLayout.concrete.page import Page
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.style.fonts import Font
from pyWebLayout.style.alignment import Alignment
from pyWebLayout.abstract.block import Block, Paragraph, Heading, HList, Table, Image as AbstractImage
from pyWebLayout.style.concrete_style import RenderingContext, StyleResolver
from PIL import Image, ImageDraw
except ImportError as e:
print(f"Error importing required modules: {e}")
print("Make sure pyWebLayout is properly installed and PIL is available")
sys.exit(1)
def get_all_blocks_from_book(book) -> List[Block]:
"""
Extract all blocks from all chapters in the book.
Args:
book: The Book object from epub_reader
Returns:
List of all Block objects
"""
all_blocks = []
# Iterate through all chapters
for chapter in book.chapters:
# Get blocks from the chapter
if hasattr(chapter, '_blocks'):
all_blocks.extend(chapter._blocks)
return all_blocks
def create_page(page_style: PageStyle, page_size: Tuple[int, int]) -> Page:
"""
Create a new Page with the given style and size.
Args:
page_style: Style configuration for the page
page_size: (width, height) tuple
Returns:
A new Page object
"""
page = Page(
size=page_size,
style=page_style
)
return page
def layout_blocks_on_pages(blocks: List[Block], page_style: PageStyle,
page_size: Tuple[int, int], max_pages: int,
alignment_override: Optional[Alignment] = None) -> List[Page]:
"""
Layout blocks across multiple pages using DocumentLayouter.
Args:
blocks: List of abstract blocks to layout
page_style: Style configuration for pages
page_size: (width, height) tuple for pages
max_pages: Maximum number of pages to generate
alignment_override: Optional alignment to override paragraph alignment
Returns:
List of rendered Page objects
"""
pages = []
current_block_index = 0
continuation_word_index = 0
continuation_pretext = None
# Create rendering context
rendering_context = RenderingContext(base_font_size=16)
while current_block_index < len(blocks) and len(pages) < max_pages:
# Create a new page
page = create_page(page_style, page_size)
page_has_content = False
# Try to layout blocks on this page
while current_block_index < len(blocks):
block = blocks[current_block_index]
if isinstance(block, (Paragraph, Heading)):
# Layout paragraph/heading
success, failed_word_index, remaining_pretext = paragraph_layouter(
block,
page,
start_word=continuation_word_index,
pretext=continuation_pretext,
alignment_override=alignment_override
)
if success:
# Block fully laid out, move to next block
page_has_content = True
current_block_index += 1
continuation_word_index = 0
continuation_pretext = None
else:
# Block partially laid out or page is full
if failed_word_index is not None:
# Partial layout - continue on next page
page_has_content = True
continuation_word_index = failed_word_index
continuation_pretext = remaining_pretext
# Break to create a new page
break
elif isinstance(block, AbstractImage):
# Layout image
success = image_layouter(block, page)
if success:
page_has_content = True
current_block_index += 1
continuation_word_index = 0
continuation_pretext = None
else:
# Image doesn't fit, try on next page
break
elif isinstance(block, HList):
# Layout list items as paragraphs
try:
list_items = list(block.items())
for item in list_items:
if isinstance(item, Paragraph):
success, failed_word_index, remaining_pretext = paragraph_layouter(
item,
page,
start_word=continuation_word_index,
pretext=continuation_pretext,
alignment_override=alignment_override
)
if not success:
# Can't fit more on this page
page_has_content = True
break
continuation_word_index = 0
continuation_pretext = None
# Move to next block after processing list
page_has_content = True
current_block_index += 1
except Exception as e:
print(f"Warning: Error processing list: {e}")
current_block_index += 1
elif isinstance(block, Table):
# Skip tables for now (not implemented)
print(f"Warning: Skipping table (not yet implemented)")
current_block_index += 1
else:
# Unknown block type, skip
print(f"Warning: Skipping unknown block type: {type(block).__name__}")
current_block_index += 1
# Add page if it has content
if page_has_content:
pages.append(page)
else:
# No content could be added to this page, stop
break
return pages
def main():
"""Main function to handle command line arguments and process the EPUB."""
parser = argparse.ArgumentParser(
description='Render EPUB pages using DocumentLayouter',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python epub_page_renderer_documentlayouter.py book.epub --pages 5
python epub_page_renderer_documentlayouter.py book.epub --pages 10 --output-dir my_output --width 600 --height 800
"""
)
parser.add_argument(
'epub_file',
help='Path to the EPUB file to render'
)
parser.add_argument(
'--pages', '-p',
type=int,
default=5,
help='Number of pages to render (default: 5)'
)
parser.add_argument(
'--output-dir', '-o',
default='rendered_pages',
help='Output directory for rendered images (default: rendered_pages)'
)
parser.add_argument(
'--width', '-w',
type=int,
default=800,
help='Page width in pixels (default: 800)'
)
parser.add_argument(
'--height', '-t',
type=int,
default=1000,
help='Page height in pixels (default: 1000)'
)
parser.add_argument(
'--margin', '-m',
type=int,
default=40,
help='Page margin in pixels (default: 40)'
)
parser.add_argument(
'--align', '-a',
choices=['left', 'justify'],
default='left',
help='Text alignment: left or justify (default: left)'
)
args = parser.parse_args()
# Validate arguments
if not os.path.exists(args.epub_file):
print(f"Error: EPUB file '{args.epub_file}' not found")
return 1
if args.pages <= 0:
print("Error: Number of pages must be positive")
return 1
# Create output directory
try:
os.makedirs(args.output_dir, exist_ok=True)
except OSError as e:
print(f"Error creating output directory: {e}")
return 1
print(f"Loading EPUB file: {args.epub_file}")
# Load the EPUB file
try:
book = read_epub(args.epub_file)
print(f"Successfully loaded EPUB: {book.get_title() or 'Unknown Title'}")
# Print book information
author = book.get_metadata('AUTHOR')
if author:
print(f"Author: {author}")
print(f"Chapters: {len(book.chapters) if hasattr(book, 'chapters') else 'Unknown'}")
except Exception as e:
print(f"Error loading EPUB file: {e}")
import traceback
traceback.print_exc()
return 1
# Extract all blocks from the book
print("Extracting content blocks...")
try:
all_blocks = get_all_blocks_from_book(book)
print(f"Extracted {len(all_blocks)} content blocks")
if not all_blocks:
print("No content blocks found in EPUB. The book might be empty.")
return 1
except Exception as e:
print(f"Error extracting blocks: {e}")
import traceback
traceback.print_exc()
return 1
# Set up page style
page_size = (args.width, args.height)
page_style = PageStyle(
background_color=(255, 255, 255),
border_width=args.margin,
border_color=(200, 200, 200),
padding=(10, 10, 10, 10), # top, right, bottom, left
line_spacing=5,
inter_block_spacing=15
)
# Set alignment
alignment = Alignment.JUSTIFY if args.align == 'justify' else Alignment.LEFT
print(f"Setting up layouter with page size {page_size} and {args.align} alignment")
# Layout blocks on pages
print(f"Rendering up to {args.pages} pages using DocumentLayouter...")
try:
pages = layout_blocks_on_pages(
all_blocks,
page_style,
page_size,
args.pages,
alignment_override=alignment
)
if not pages:
print("No pages were generated.")
return 1
print(f"Generated {len(pages)} pages")
# Save each page to an image
for i, page in enumerate(pages):
print(f"Saving page {i + 1}/{len(pages)}...")
try:
# Render the page
image = page.render()
# Save the image
output_filename = f"page_{i + 1:03d}.png"
output_path = os.path.join(args.output_dir, output_filename)
image.save(output_path, 'PNG')
print(f"Saved: {output_path}")
except Exception as e:
print(f"Error saving page {i + 1}: {e}")
import traceback
traceback.print_exc()
continue
print(f"\nCompleted! Rendered {len(pages)} pages to {args.output_dir}")
except Exception as e:
print(f"Error during pagination/rendering: {e}")
import traceback
traceback.print_exc()
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""
Simple coverage runner for Coverage Gutters extension.
Generates coverage.xml file needed by the VSCode Coverage Gutters extension.
Uses the same approach as CI for consistency.
"""
import subprocess
import sys
import os
def main():
"""Run coverage for Coverage Gutters."""
print("Generating coverage for Coverage Gutters...")
print("Using the same pytest approach as CI...")
try:
# Run tests with coverage and generate all report formats (same as CI)
cmd = [
sys.executable, "-m", "pytest",
"tests/",
"-v",
"--cov=pyWebLayout",
"--cov-report=term-missing",
"--cov-report=json",
"--cov-report=html",
"--cov-report=xml"
]
print(f"Running: {' '.join(cmd)}")
result = subprocess.run(cmd, check=True)
# Check if coverage.xml was created
if os.path.exists("coverage.xml"):
print("✓ coverage.xml generated successfully!")
print("✓ coverage.json generated for CI compatibility")
print("✓ HTML coverage report generated in htmlcov/")
print("\nCoverage Gutters should now be able to display coverage data.")
print("\nTo use Coverage Gutters in VSCode:")
print("1. Open Command Palette (Ctrl+Shift+P)")
print("2. Run 'Coverage Gutters: Remove Coverage' (to clear cache)")
print("3. Run 'Coverage Gutters: Display Coverage'")
print("4. Or use the Coverage Gutters buttons in the status bar")
# Show file info
size = os.path.getsize("coverage.xml")
print(f"\nGenerated coverage.xml: {size} bytes")
else:
print("✗ coverage.xml was not generated")
except subprocess.CalledProcessError as e:
print(f"Error running tests: {e}")
print("This may indicate test failures or missing dependencies.")
sys.exit(1)
except FileNotFoundError:
print("pytest not found. Please install it with: pip install pytest pytest-cov")
sys.exit(1)
if __name__ == "__main__":
main()
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""
Update coverage gutters configuration and fix coverage paths.
This script ensures Coverage Gutters can properly display coverage information.
"""
import os
import json
def main():
"""Main function to fix coverage gutters configuration."""
print("=== Coverage Gutters Fix ===")
print(f"Current working directory: {os.getcwd()}")
# 1. Check if coverage.xml exists
if os.path.exists('coverage.xml'):
print("✓ coverage.xml exists")
# Check file size and basic content
size = os.path.getsize('coverage.xml')
print(f"✓ coverage.xml size: {size} bytes")
# Read first few lines to verify it's valid XML
try:
with open('coverage.xml', 'r') as f:
first_line = f.readline().strip()
if first_line.startswith('<?xml'):
print("✓ coverage.xml appears to be valid XML")
else:
print("✗ coverage.xml does not start with XML declaration")
except Exception as e:
print(f"✗ Error reading coverage.xml: {e}")
else:
print("✗ coverage.xml does not exist")
print("Running coverage to generate coverage.xml...")
os.system("python -m coverage run --source=pyWebLayout -m unittest tests.test_abstract_inline")
os.system("python -m coverage xml")
# 2. Check VSCode settings
vscode_settings_path = '.vscode/settings.json'
if os.path.exists(vscode_settings_path):
print("✓ VSCode settings.json exists")
with open(vscode_settings_path, 'r') as f:
try:
settings = json.load(f)
gutters_config = {k: v for k, v in settings.items() if 'coverage-gutters' in k}
if gutters_config:
print("✓ Coverage Gutters settings found:")
for key, value in gutters_config.items():
print(f" {key}: {value}")
else:
print("✗ No Coverage Gutters settings found")
except json.JSONDecodeError as e:
print(f"✗ Error parsing VSCode settings: {e}")
else:
print("✗ VSCode settings.json not found")
# 3. Check if inline.py file exists
inline_file = 'pyWebLayout/abstract/inline.py'
if os.path.exists(inline_file):
print(f"{inline_file} exists")
# Check file size
size = os.path.getsize(inline_file)
print(f"{inline_file} size: {size} bytes")
else:
print(f"{inline_file} does not exist")
# 4. Run a fresh coverage collection specifically for the inline module
print("\n=== Running Fresh Coverage ===")
try:
print("Running tests with coverage...")
os.system("python -m coverage erase") # Clear old coverage data
os.system("python -m coverage run --source=pyWebLayout -m unittest tests -v")
os.system("python -m coverage xml -o coverage.xml")
os.system("python -m coverage report --include='pyWebLayout/abstract/inline.py'")
print("✓ Fresh coverage data generated")
except Exception as e:
print(f"✗ Error generating coverage: {e}")
# 5. Instructions for manual verification
print("\n=== Manual Verification Steps ===")
print("1. In VSCode, open the Command Palette (Ctrl+Shift+P)")
print("2. Run 'Coverage Gutters: Display Coverage'")
print("3. If coverage doesn't appear, try:")
print(" - 'Coverage Gutters: Remove Coverage'")
print(" - 'Coverage Gutters: Display Coverage' again")
print("4. Check that coverage.xml contains data for pyWebLayout/abstract/inline.py")
print("5. The file should show 100% coverage (all lines covered)")
print("\n=== Troubleshooting ===")
print("If coverage still doesn't show:")
print("1. Restart VSCode")
print("2. Check Coverage Gutters extension is enabled")
print("3. Try running: python run_coverage_gutters.py")
print("4. Check VSCode Output panel for Coverage Gutters logs")
if __name__ == "__main__":
main()