Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Python Code Linter | PEP 8 & Flake8 Compliance</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .code-editor { | |
| min-height: 300px; | |
| font-family: 'Courier New', monospace; | |
| font-size: 14px; | |
| line-height: 1.5; | |
| tab-size: 4; | |
| } | |
| .report-item { | |
| transition: all 0.2s ease; | |
| } | |
| .report-item:hover { | |
| transform: translateX(5px); | |
| } | |
| .tab-active { | |
| border-bottom: 3px solid #3b82f6; | |
| } | |
| .error-badge { | |
| position: absolute; | |
| top: -8px; | |
| right: -8px; | |
| font-size: 12px; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { | |
| opacity: 1; | |
| } | |
| 50% { | |
| opacity: 0.5; | |
| } | |
| } | |
| .animate-pulse { | |
| animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Header --> | |
| <header class="mb-8 text-center"> | |
| <h1 class="text-4xl font-bold text-blue-600 mb-2"> | |
| <i class="fas fa-code mr-2"></i>Python Code Linter | |
| </h1> | |
| <p class="text-gray-600 text-lg"> | |
| Improve your Python code to meet Flake8 and PEP 8 standards | |
| </p> | |
| </header> | |
| <!-- Main Content --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden"> | |
| <!-- Tabs --> | |
| <div class="flex border-b"> | |
| <button id="paste-tab" class="tab-active px-6 py-4 font-medium text-blue-600 focus:outline-none"> | |
| <i class="fas fa-paste mr-2"></i>Paste Code | |
| </button> | |
| <button id="upload-tab" class="px-6 py-4 font-medium text-gray-500 hover:text-blue-600 focus:outline-none"> | |
| <i class="fas fa-upload mr-2"></i>Upload File | |
| </button> | |
| <div class="ml-auto px-6 py-4"> | |
| <span id="error-count" class="hidden bg-red-100 text-red-800 text-xs font-medium px-2.5 py-0.5 rounded-full"> | |
| <span id="error-count-value">0</span> issues found | |
| </span> | |
| </div> | |
| </div> | |
| <!-- Tab Content --> | |
| <div class="p-6"> | |
| <!-- Paste Code Tab Content --> | |
| <div id="paste-content" class="space-y-6"> | |
| <div class="flex space-x-4"> | |
| <div class="flex-1 relative"> | |
| <label for="python-code" class="block mb-2 text-sm font-medium text-gray-700"> | |
| Original Python Code | |
| <span class="text-gray-500 text-xs">(Ctrl+Enter to lint)</span> | |
| </label> | |
| <div class="relative"> | |
| <textarea id="python-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" placeholder="Paste your Python code here..."></textarea> | |
| <button id="clear-code" class="absolute top-2 right-2 p-2 text-gray-400 hover:text-gray-600"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="flex-1 relative"> | |
| <label for="linted-code" class="block mb-2 text-sm font-medium text-gray-700"> | |
| Linted Python Code | |
| </label> | |
| <div class="relative"> | |
| <pre id="linted-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg bg-gray-50 overflow-auto" style="white-space: pre-wrap;"></pre> | |
| <button id="copy-code" class="absolute top-2 right-2 p-2 text-gray-400 hover:text-gray-600"> | |
| <i class="fas fa-copy"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex justify-center"> | |
| <button id="lint-button" class="px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"> | |
| <i class="fas fa-magic mr-2"></i>Lint My Code | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Upload File Tab Content --> | |
| <div id="upload-content" class="hidden space-y-6"> | |
| <div class="flex items-center justify-center w-full"> | |
| <label for="file-upload" class="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100"> | |
| <div class="flex flex-col items-center justify-center pt-5 pb-6"> | |
| <i class="fas fa-file-upload text-4xl text-gray-400 mb-3"></i> | |
| <p class="mb-2 text-sm text-gray-500"> | |
| <span class="font-semibold">Click to upload</span> or drag and drop | |
| </p> | |
| <p class="text-xs text-gray-500">.py files only</p> | |
| </div> | |
| <input id="file-upload" type="file" class="hidden" accept=".py" /> | |
| </label> | |
| </div> | |
| <div class="hidden" id="upload-preview"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <div> | |
| <span id="uploaded-filename" class="font-medium text-gray-700"></span> | |
| <span id="uploaded-filesize" class="text-sm text-gray-500 ml-2"></span> | |
| </div> | |
| <button id="remove-file" class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-trash mr-1"></i>Remove | |
| </button> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <h3 class="text-sm font-medium text-gray-700 mb-2">Original Code</h3> | |
| <pre id="upload-original-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg bg-gray-50 overflow-auto max-h-64"></pre> | |
| </div> | |
| <div> | |
| <h3 class="text-sm font-medium text-gray-700 mb-2">Linted Code</h3> | |
| <pre id="upload-linted-code" class="code-editor w-full p-4 border border-gray-300 rounded-lg bg-gray-50 overflow-auto max-h-64"></pre> | |
| </div> | |
| </div> | |
| <div class="mt-4 flex justify-center"> | |
| <button id="lint-upload-button" class="px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"> | |
| <i class="fas fa-magic mr-2"></i>Lint Uploaded File | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Linting Report --> | |
| <div id="report-section" class="hidden mt-8"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4"> | |
| <i class="fas fa-clipboard-list mr-2"></i>Linting Report | |
| </h2> | |
| <div class="flex mb-4"> | |
| <div class="flex-1"> | |
| <div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800"> | |
| <i class="fas fa-check-circle mr-1"></i> | |
| <span id="fixed-count">0</span> issues fixed | |
| </div> | |
| </div> | |
| <div class="flex-1 text-right"> | |
| <div class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-yellow-100 text-yellow-800"> | |
| <i class="fas fa-exclamation-triangle mr-1"></i> | |
| <span id="remaining-count">0</span> issues remaining | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 rounded-lg border border-gray-200 overflow-hidden"> | |
| <div id="report-items" class="divide-y divide-gray-200 max-h-96 overflow-y-auto"> | |
| <!-- Report items will be added here dynamically --> | |
| </div> | |
| </div> | |
| <div class="mt-4 text-sm text-gray-500"> | |
| <i class="fas fa-info-circle mr-1"></i> PEP 8 is the official style guide for Python code. Flake8 is a tool that checks your code against PEP 8 and other quality standards. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Features Section --> | |
| <div class="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <div class="bg-white p-6 rounded-lg shadow-md"> | |
| <div class="text-blue-500 text-2xl mb-3"> | |
| <i class="fas fa-broom"></i> | |
| </div> | |
| <h3 class="font-semibold text-lg mb-2">Code Cleanup</h3> | |
| <p class="text-gray-600">Automatically fix indentation, whitespace, line length, and other PEP 8 violations to make your code more readable.</p> | |
| </div> | |
| <div class="bg-white p-6 rounded-lg shadow-md"> | |
| <div class="text-blue-500 text-2xl mb-3"> | |
| <i class="fas fa-search"></i> | |
| </div> | |
| <h3 class="font-semibold text-lg mb-2">Comprehensive Checks</h3> | |
| <p class="text-gray-600">Detects and reports on style issues, syntax errors, and potential bugs using Flake8's powerful analysis.</p> | |
| </div> | |
| <div class="bg-white p-6 rounded-lg shadow-md"> | |
| <div class="text-blue-500 text-2xl mb-3"> | |
| <i class="fas fa-graduation-cap"></i> | |
| </div> | |
| <h3 class="font-semibold text-lg mb-2">Learn Best Practices</h3> | |
| <p class="text-gray-600">Detailed explanations help you understand and remember Python coding standards for future projects.</p> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <footer class="mt-12 text-center text-gray-500 text-sm"> | |
| <p>Made with <i class="fas fa-heart text-red-500"></i> for Python developers</p> | |
| <p class="mt-1">This tool helps improve code quality but doesn't replace thorough testing and code reviews.</p> | |
| </footer> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Tab switching | |
| const pasteTab = document.getElementById('paste-tab'); | |
| const uploadTab = document.getElementById('upload-tab'); | |
| const pasteContent = document.getElementById('paste-content'); | |
| const uploadContent = document.getElementById('upload-content'); | |
| pasteTab.addEventListener('click', function() { | |
| pasteTab.classList.add('tab-active'); | |
| pasteTab.classList.remove('text-gray-500'); | |
| pasteTab.classList.add('text-blue-600'); | |
| uploadTab.classList.remove('tab-active'); | |
| uploadTab.classList.add('text-gray-500'); | |
| uploadTab.classList.remove('text-blue-600'); | |
| pasteContent.classList.remove('hidden'); | |
| uploadContent.classList.add('hidden'); | |
| }); | |
| uploadTab.addEventListener('click', function() { | |
| uploadTab.classList.add('tab-active'); | |
| uploadTab.classList.remove('text-gray-500'); | |
| uploadTab.classList.add('text-blue-600'); | |
| pasteTab.classList.remove('tab-active'); | |
| pasteTab.classList.add('text-gray-500'); | |
| pasteTab.classList.remove('text-blue-600'); | |
| uploadContent.classList.remove('hidden'); | |
| pasteContent.classList.add('hidden'); | |
| }); | |
| // Code editor functionality | |
| const pythonCode = document.getElementById('python-code'); | |
| const lintedCode = document.getElementById('linted-code'); | |
| const clearCode = document.getElementById('clear-code'); | |
| const copyCode = document.getElementById('copy-code'); | |
| const lintButton = document.getElementById('lint-button'); | |
| const errorCount = document.getElementById('error-count'); | |
| const errorCountValue = document.getElementById('error-count-value'); | |
| const reportSection = document.getElementById('report-section'); | |
| const reportItems = document.getElementById('report-items'); | |
| const fixedCount = document.getElementById('fixed-count'); | |
| const remainingCount = document.getElementById('remaining-count'); | |
| // Sample linting function (in a real app, this would call a backend service) | |
| function lintPythonCode(code) { | |
| // This is a mock implementation | |
| // In reality, you would send this to a backend that runs flake8 and autopep8 | |
| // Mock fixes | |
| const mockFixes = [ | |
| { line: 2, column: 5, message: "E302 expected 2 blank lines before function definition", fixed: true }, | |
| { line: 5, column: 80, message: "E501 line too long (82 > 79 characters)", fixed: true }, | |
| { line: 7, column: 1, message: "E303 too many blank lines (3)", fixed: true }, | |
| { line: 9, column: 17, message: "E225 missing whitespace around operator", fixed: true }, | |
| { line: 12, column: 1, message: "W391 blank line at end of file", fixed: true }, | |
| { line: 3, column: 1, message: "E265 block comment should start with '# '", fixed: false }, | |
| ]; | |
| // Mock linted code (just some basic formatting for demo) | |
| let linted = code; | |
| // Add two blank lines before functions | |
| linted = linted.replace(/def (\w+)\(/g, '\n\n$&'); | |
| // Fix line length (very simplistic) | |
| linted = linted.split('\n').map(line => { | |
| if (line.length > 79) { | |
| return line.substring(0, 76) + '...'; | |
| } | |
| return line; | |
| }).join('\n'); | |
| // Trim trailing whitespace | |
| linted = linted.split('\n').map(line => line.trimEnd()).join('\n'); | |
| // Ensure exactly one blank line at end of file | |
| linted = linted.trimEnd() + '\n'; | |
| return { | |
| lintedCode: linted, | |
| issues: mockFixes, | |
| originalCode: code | |
| }; | |
| } | |
| // Generate a report item | |
| function createReportItem(issue, index) { | |
| const item = document.createElement('div'); | |
| item.className = 'report-item p-4 hover:bg-gray-100'; | |
| const badgeClass = issue.fixed ? | |
| 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800'; | |
| const icon = issue.fixed ? 'check-circle' : 'exclamation-triangle'; | |
| item.innerHTML = ` | |
| <div class="flex items-start"> | |
| <div class="flex-shrink-0 pt-0.5"> | |
| <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${badgeClass}"> | |
| <i class="fas fa-${icon} mr-1"></i> | |
| ${issue.fixed ? 'Fixed' : 'Warning'} | |
| </span> | |
| </div> | |
| <div class="ml-3"> | |
| <div class="text-sm font-medium text-gray-900"> | |
| Line ${issue.line}, Column ${issue.column} | |
| </div> | |
| <div class="text-sm text-gray-500"> | |
| ${issue.message} | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| return item; | |
| } | |
| // Clear code editor | |
| clearCode.addEventListener('click', function() { | |
| pythonCode.value = ''; | |
| lintedCode.textContent = ''; | |
| reportSection.classList.add('hidden'); | |
| errorCount.classList.add('hidden'); | |
| }); | |
| // Copy linted code | |
| copyCode.addEventListener('click', function() { | |
| if (lintedCode.textContent.trim()) { | |
| navigator.clipboard.writeText(lintedCode.textContent); | |
| // Show feedback | |
| const originalIcon = copyCode.querySelector('i'); | |
| originalIcon.classList.remove('fa-copy'); | |
| originalIcon.classList.add('fa-check'); | |
| setTimeout(() => { | |
| originalIcon.classList.remove('fa-check'); | |
| originalIcon.classList.add('fa-copy'); | |
| }, 2000); | |
| } | |
| }); | |
| // Lint code | |
| function performLint() { | |
| const code = pythonCode.value.trim(); | |
| if (!code) return; | |
| // Show loading state | |
| lintButton.innerHTML = '<i class="fas fa-spinner animate-spin mr-2"></i> Linting...'; | |
| lintButton.disabled = true; | |
| // Simulate API call delay | |
| setTimeout(() => { | |
| const result = lintPythonCode(code); | |
| // Update UI with results | |
| lintedCode.textContent = result.lintedCode; | |
| // Show error count | |
| const totalIssues = result.issues.length; | |
| const fixedIssues = result.issues.filter(i => i.fixed).length; | |
| if (totalIssues > 0) { | |
| errorCountValue.textContent = totalIssues; | |
| errorCount.classList.remove('hidden'); | |
| } else { | |
| errorCount.classList.add('hidden'); | |
| } | |
| // Generate report | |
| reportItems.innerHTML = ''; | |
| result.issues.forEach((issue, index) => { | |
| reportItems.appendChild(createReportItem(issue, index)); | |
| }); | |
| // Update counts | |
| fixedCount.textContent = fixedIssues; | |
| remainingCount.textContent = totalIssues - fixedIssues; | |
| // Show report | |
| reportSection.classList.remove('hidden'); | |
| // Reset button | |
| lintButton.innerHTML = '<i class="fas fa-magic mr-2"></i>Lint My Code'; | |
| lintButton.disabled = false; | |
| }, 1000); | |
| } | |
| lintButton.addEventListener('click', performLint); | |
| // Add Ctrl+Enter shortcut to lint code | |
| pythonCode.addEventListener('keydown', function(e) { | |
| if (e.ctrlKey && e.key === 'Enter') { | |
| performLint(); | |
| } | |
| }); | |
| // File upload functionality | |
| const fileUpload = document.getElementById('file-upload'); | |
| const uploadPreview = document.getElementById('upload-preview'); | |
| const uploadedFilename = document.getElementById('uploaded-filename'); | |
| const uploadedFilesize = document.getElementById('uploaded-filesize'); | |
| const uploadOriginalCode = document.getElementById('upload-original-code'); | |
| const uploadLintedCode = document.getElementById('upload-linted-code'); | |
| const removeFile = document.getElementById('remove-file'); | |
| const lintUploadButton = document.getElementById('lint-upload-button'); | |
| fileUpload.addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| if (!file.name.endsWith('.py')) { | |
| alert('Please upload a Python (.py) file'); | |
| return; | |
| } | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const content = e.target.result; | |
| // Update UI | |
| uploadedFilename.textContent = file.name; | |
| uploadedFilesize.textContent = formatFileSize(file.size); | |
| uploadOriginalCode.textContent = content; | |
| uploadLintedCode.textContent = ''; | |
| // Show preview | |
| uploadPreview.classList.remove('hidden'); | |
| }; | |
| reader.readAsText(file); | |
| }); | |
| removeFile.addEventListener('click', function() { | |
| fileUpload.value = ''; | |
| uploadPreview.classList.add('hidden'); | |
| }); | |
| lintUploadButton.addEventListener('click', function() { | |
| const code = uploadOriginalCode.textContent.trim(); | |
| if (!code) return; | |
| // Show loading state | |
| lintUploadButton.innerHTML = '<i class="fas fa-spinner animate-spin mr-2"></i> Linting...'; | |
| lintUploadButton.disabled = true; | |
| // Simulate API call delay | |
| setTimeout(() => { | |
| const result = lintPythonCode(code); | |
| // Update UI with results | |
| uploadLintedCode.textContent = result.lintedCode; | |
| // Generate report | |
| reportItems.innerHTML = ''; | |
| result.issues.forEach((issue, index) => { | |
| reportItems.appendChild(createReportItem(issue, index)); | |
| }); | |
| // Update counts | |
| const totalIssues = result.issues.length; | |
| const fixedIssues = result.issues.filter(i => i.fixed).length; | |
| fixedCount.textContent = fixedIssues; | |
| remainingCount.textContent = totalIssues - fixedIssues; | |
| // Show report | |
| reportSection.classList.remove('hidden'); | |
| // Reset button | |
| lintUploadButton.innerHTML = '<i class="fas fa-magic mr-2"></i>Lint Uploaded File'; | |
| lintUploadButton.disabled = false; | |
| }, 1000); | |
| }); | |
| // Helper function to format file size | |
| function formatFileSize(bytes) { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| } | |
| // Sample code for first-time users | |
| if (!pythonCode.value.trim()) { | |
| pythonCode.value = `# This is a sample Python code with some style issues | |
| def calculate_sum(a,b): | |
| result=a+b | |
| return result | |
| def print_message(msg): | |
| #This function prints a message | |
| print("Message:",msg) | |
| print_message("Hello World!") | |
| total=calculate_sum(5,7) | |
| print("The total is:",total) | |
| `; | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=S-Dreamer/python-code-linter" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |