"""
Code quality tools and configuration for the application.
"""
import streamlit as st
import subprocess
import os
from pathlib import Path
import tempfile
import json
def render_code_quality_tools():
"""
Render the code quality tools interface.
"""
st.markdown("
Code Quality Tools
", unsafe_allow_html=True)
# Tabs for different tools
tab1, tab2, tab3, tab4 = st.tabs(["Linting", "Formatting", "Type Checking", "Testing"])
with tab1:
render_linting_tools()
with tab2:
render_formatting_tools()
with tab3:
render_type_checking_tools()
with tab4:
render_testing_tools()
def render_linting_tools():
"""
Render linting tools interface.
"""
st.markdown("### Linting with Pylint/Flake8")
st.markdown("""
Linting tools help identify potential errors, enforce coding standards, and encourage best practices.
**Available Tools:**
- **Pylint**: Comprehensive linter that checks for errors and enforces a coding standard
- **Flake8**: Wrapper around PyFlakes, pycodestyle, and McCabe complexity checker
""")
# File upload for linting
uploaded_file = st.file_uploader("Upload Python file for linting", type=["py"])
linter = st.radio("Select linter", ["Pylint", "Flake8"])
if uploaded_file and st.button("Run Linter"):
with st.spinner(f"Running {linter}..."):
# Save uploaded file to a temporary file
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as tmp_file:
tmp_file.write(uploaded_file.getvalue())
tmp_path = tmp_file.name
try:
if linter == "Pylint":
# Run pylint
result = subprocess.run(
["pylint", tmp_path],
capture_output=True,
text=True
)
else:
# Run flake8
result = subprocess.run(
["flake8", tmp_path],
capture_output=True,
text=True
)
# Display results
st.subheader("Linting Results")
if result.returncode == 0:
st.success("No issues found!")
else:
st.error("Issues found:")
st.code(result.stdout or result.stderr, language="text")
except Exception as e:
st.error(f"Error running {linter}: {str(e)}")
finally:
# Clean up temporary file
os.unlink(tmp_path)
def render_formatting_tools():
"""
Render code formatting tools interface.
"""
st.markdown("### Code Formatting with Black & isort")
st.markdown("""
Code formatters automatically reformat your code to follow a consistent style.
**Available Tools:**
- **Black**: The uncompromising Python code formatter
- **isort**: A utility to sort imports alphabetically and automatically separate them into sections
""")
# File upload for formatting
uploaded_file = st.file_uploader("Upload Python file for formatting", type=["py"])
formatter = st.radio("Select formatter", ["Black", "isort", "Both"])
if uploaded_file and st.button("Format Code"):
with st.spinner(f"Running {formatter}..."):
# Get original code
original_code = uploaded_file.getvalue().decode("utf-8")
# Save uploaded file to a temporary file
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as tmp_file:
tmp_file.write(uploaded_file.getvalue())
tmp_path = tmp_file.name
try:
formatted_code = ""
if formatter in ["Black", "Both"]:
# Run black
result = subprocess.run(
["black", tmp_path],
capture_output=True,
text=True
)
with open(tmp_path, "r") as f:
formatted_code = f.read()
if formatter in ["isort", "Both"]:
# If both, use the code formatted by black
if formatter == "Both":
with open(tmp_path, "w") as f:
f.write(formatted_code)
# Run isort
result = subprocess.run(
["isort", tmp_path],
capture_output=True,
text=True
)
with open(tmp_path, "r") as f:
formatted_code = f.read()
# Display results side by side
st.subheader("Formatting Results")
col1, col2 = st.columns(2)
with col1:
st.markdown("#### Original Code")
st.code(original_code, language="python")
with col2:
st.markdown("#### Formatted Code")
st.code(formatted_code, language="python")
except Exception as e:
st.error(f"Error running {formatter}: {str(e)}")
finally:
# Clean up temporary file
os.unlink(tmp_path)
def render_type_checking_tools():
"""
Render type checking tools interface.
"""
st.markdown("### Type Checking with mypy")
st.markdown("""
Static type checking helps catch type errors before runtime.
**Available Tool:**
- **mypy**: Optional static typing for Python
""")
# File upload for type checking
uploaded_file = st.file_uploader("Upload Python file for type checking", type=["py"])
if uploaded_file and st.button("Check Types"):
with st.spinner("Running mypy..."):
# Save uploaded file to a temporary file
with tempfile.NamedTemporaryFile(suffix=".py", delete=False) as tmp_file:
tmp_file.write(uploaded_file.getvalue())
tmp_path = tmp_file.name
try:
# Run mypy
result = subprocess.run(
["mypy", tmp_path],
capture_output=True,
text=True
)
# Display results
st.subheader("Type Checking Results")
if result.returncode == 0:
st.success("No type issues found!")
else:
st.error("Type issues found:")
st.code(result.stdout or result.stderr, language="text")
except Exception as e:
st.error(f"Error running mypy: {str(e)}")
finally:
# Clean up temporary file
os.unlink(tmp_path)
def render_testing_tools():
"""
Render testing tools interface.
"""
st.markdown("### Testing with pytest")
st.markdown("""
Testing frameworks help ensure your code works as expected.
**Available Tool:**
- **pytest**: Simple and powerful testing framework
""")
# Test file upload
test_file = st.file_uploader("Upload test file", type=["py"])
# Code file upload (optional)
code_file = st.file_uploader("Upload code file to test (optional)", type=["py"])
if test_file and st.button("Run Tests"):
with st.spinner("Running tests..."):
# Create temporary directory for test files
with tempfile.TemporaryDirectory() as tmp_dir:
# Save test file
test_path = os.path.join(tmp_dir, "test_" + test_file.name)
with open(test_path, "wb") as f:
f.write(test_file.getvalue())
# Save code file if provided
if code_file:
code_path = os.path.join(tmp_dir, code_file.name)
with open(code_path, "wb") as f:
f.write(code_file.getvalue())
try:
# Run pytest
result = subprocess.run(
["pytest", "-v", test_path],
capture_output=True,
text=True
)
# Display results
st.subheader("Test Results")
st.code(result.stdout, language="text")
if result.returncode == 0:
st.success("All tests passed!")
else:
st.error("Some tests failed.")
except Exception as e:
st.error(f"Error running tests: {str(e)}")
def create_pylintrc():
"""
Create a sample pylintrc configuration file.
"""
pylintrc = """[MASTER]
# Python version
py-version = 3.8
# Parallel processing
jobs = 1
[MESSAGES CONTROL]
# Disable specific messages
disable=
C0111, # missing-docstring
C0103, # invalid-name
R0903, # too-few-public-methods
R0913, # too-many-arguments
W0511, # fixme
[FORMAT]
# Maximum line length
max-line-length = 100
# Expected indentation
indent-string = ' '
[DESIGN]
# Maximum number of locals for function / method body
max-locals = 15
# Maximum number of arguments for function / method
max-args = 5
# Maximum number of attributes for a class
max-attributes = 7
"""
return pylintrc
def create_flake8_config():
"""
Create a sample flake8 configuration file.
"""
flake8_config = """[flake8]
max-line-length = 100
exclude = .git,__pycache__,build,dist
ignore =
E203, # whitespace before ':'
E501, # line too long
W503 # line break before binary operator
"""
return flake8_config
def create_mypy_config():
"""
Create a sample mypy configuration file.
"""
mypy_config = """[mypy]
python_version = 3.8
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = False
disallow_incomplete_defs = False
[mypy.plugins.numpy.*]
follow_imports = skip
[mypy.plugins.pandas.*]
follow_imports = skip
"""
return mypy_config
def create_pytest_config():
"""
Create a sample pytest configuration file.
"""
pytest_config = """[pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
markers =
slow: marks tests as slow (deselect with '-m "not slow"')
integration: marks tests as integration tests
"""
return pytest_config