Created
September 25, 2025 15:30
-
-
Save originalankur/fbc29274b964cd27193186fe7939ced5 to your computer and use it in GitHub Desktop.
AsciiDoc Python Code Extractor and Executor - reads AsciiDoc files, extracts Python code blocks, and executes them interactively with user confirmation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import re | |
| import os | |
| import sys | |
| import tempfile | |
| import subprocess | |
| from pathlib import Path | |
| from typing import List, Tuple | |
| class AsciiDocCodeExtractor: | |
| def __init__(self, file_path: str): | |
| self.file_path = Path(file_path) | |
| self.code_blocks = [] | |
| def extract_python_code_blocks(self) -> List[Tuple[str, int]]: | |
| """Extract Python code blocks from AsciiDoc file.""" | |
| if not self.file_path.exists(): | |
| print(f"Error: File {self.file_path} not found") | |
| return [] | |
| try: | |
| with open(self.file_path, 'r', encoding='utf-8') as file: | |
| content = file.read() | |
| except Exception as e: | |
| print(f"Error reading file: {e}") | |
| return [] | |
| # Pattern to match Python code blocks in AsciiDoc | |
| # Matches both [source,python] and [source, python] formats | |
| patterns = [ | |
| r'\[source,\s*python\]\s*\n----\n(.*?)\n----', | |
| r'\[source,\s*py\]\s*\n----\n(.*?)\n----', | |
| r'```python\n(.*?)\n```', # Also support markdown-style blocks | |
| r'```py\n(.*?)\n```' | |
| ] | |
| code_blocks = [] | |
| for pattern in patterns: | |
| matches = re.finditer(pattern, content, re.DOTALL | re.IGNORECASE) | |
| for match in matches: | |
| code = match.group(1).strip() | |
| # Only add non-empty code blocks | |
| if code: | |
| # Find line number | |
| line_num = content[:match.start()].count('\n') + 1 | |
| code_blocks.append((code, line_num)) | |
| # Sort by line number to maintain order | |
| code_blocks.sort(key=lambda x: x[1]) | |
| return code_blocks | |
| def display_code_block(self, code: str, block_num: int, line_num: int): | |
| """Display a code block with formatting.""" | |
| print(f"\n{'='*60}") | |
| print(f"Code Block #{block_num} (Line {line_num})") | |
| print(f"{'='*60}") | |
| print(code) | |
| print(f"{'='*60}") | |
| def execute_code_block(self, code: str, block_num: int) -> bool: | |
| """Execute a Python code block and return success status.""" | |
| try: | |
| # Create a temporary file for the code | |
| with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as temp_file: | |
| temp_file.write(code) | |
| temp_file_path = temp_file.name | |
| print(f"\nExecuting code block #{block_num}...") | |
| print("-" * 40) | |
| # Execute the Python file | |
| result = subprocess.run( | |
| [sys.executable, temp_file_path], | |
| capture_output=True, | |
| text=True, | |
| timeout=30 # 30 second timeout to ensure CI/CD doesn't block ( Talk to Ankur before removing. ) | |
| ) | |
| # Display output | |
| if result.stdout: | |
| print("STDOUT:") | |
| print(result.stdout) | |
| if result.stderr: | |
| print("STDERR:") | |
| print(result.stderr) | |
| if result.returncode == 0: | |
| print(f"✅ Code block #{block_num} executed successfully") | |
| return True | |
| else: | |
| print(f"❌ Code block #{block_num} failed with return code {result.returncode}") | |
| return False | |
| except subprocess.TimeoutExpired: | |
| print(f"⏰ Code block #{block_num} timed out after 30 seconds") | |
| return False | |
| except Exception as e: | |
| print(f"❌ Error executing code block #{block_num}: {e}") | |
| return False | |
| finally: | |
| # Clean up temporary file | |
| try: | |
| os.unlink(temp_file_path) | |
| except: | |
| pass | |
| def get_user_choice(self) -> str: | |
| """Get user choice for code execution.""" | |
| while True: | |
| choice = input("\nChoose an option:\n" | |
| " [y] Execute this code block\n" | |
| " [n] Skip this code block\n" | |
| " [a] Execute all remaining blocks\n" | |
| " [q] Quit\n" | |
| "Enter choice: ").lower().strip() | |
| if choice in ['y', 'n', 'a', 'q']: | |
| return choice | |
| print("Invalid choice. Please enter 'y', 'n', 'a', or 'q'.") | |
| def run_interactive_execution(self): | |
| """Main interactive execution loop.""" | |
| print(f"Extracting Python code blocks from: {self.file_path}") | |
| code_blocks = self.extract_python_code_blocks() | |
| if not code_blocks: | |
| print("No Python code blocks found in the file.") | |
| return | |
| print(f"Found {len(code_blocks)} Python code block(s)") | |
| execute_all = False | |
| executed_count = 0 | |
| success_count = 0 | |
| for i, (code, line_num) in enumerate(code_blocks, 1): | |
| self.display_code_block(code, i, line_num) | |
| if not execute_all: | |
| choice = self.get_user_choice() | |
| if choice == 'q': | |
| print("Exiting...") | |
| break | |
| elif choice == 'n': | |
| print(f"Skipping code block #{i}") | |
| continue | |
| elif choice == 'a': | |
| execute_all = True | |
| print("Executing all remaining code blocks...") | |
| # 'y' falls through to execution | |
| # Execute the code block | |
| executed_count += 1 | |
| if self.execute_code_block(code, i): | |
| success_count += 1 | |
| # Summary | |
| print(f"\n{'='*60}") | |
| print("EXECUTION SUMMARY") | |
| print(f"{'='*60}") | |
| print(f"Total code blocks found: {len(code_blocks)}") | |
| print(f"Code blocks executed: {executed_count}") | |
| print(f"Successful executions: {success_count}") | |
| print(f"Failed executions: {executed_count - success_count}") | |
| def main(): | |
| """Main function to handle command line arguments and run the extractor.""" | |
| if len(sys.argv) != 2: | |
| print("Usage: python asciidoc_code_executor.py <asciidoc_file>") | |
| print("\nExample:") | |
| print(" python asciidoc_code_executor.py document.adoc") | |
| sys.exit(1) | |
| file_path = sys.argv[1] | |
| if not os.path.exists(file_path): | |
| print(f"Error: File '{file_path}' not found") | |
| sys.exit(1) | |
| extractor = AsciiDocCodeExtractor(file_path) | |
| try: | |
| extractor.run_interactive_execution() | |
| except KeyboardInterrupt: | |
| print("\n\nInterrupted by user. Exiting...") | |
| sys.exit(0) | |
| except Exception as e: | |
| print(f"Unexpected error: {e}") | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment