Skip to content

Commit 696363c

Browse files
committed
feat: enhance development setup with Black and Ruff configuration, update README, and remove unused loader module
1 parent 3ec4ff0 commit 696363c

File tree

9 files changed

+112
-106
lines changed

9 files changed

+112
-106
lines changed

.vscode/settings.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
{
22
"python.testing.pytestArgs": ["tests"],
33
"python.testing.unittestEnabled": false,
4-
"python.testing.pytestEnabled": true
4+
"python.testing.pytestEnabled": true,
5+
"[python]": {
6+
"editor.formatOnSave": true,
7+
"editor.defaultFormatter": "ms-python.black-formatter",
8+
"editor.codeActionsOnSave": {
9+
"source.organizeImports": "explicit",
10+
"source.fixAll": "explicit"
11+
}
12+
},
13+
"editor.formatOnSave": true,
14+
"python.analysis.typeCheckingMode": "basic",
15+
"ruff.organizeImports": true,
16+
"ruff.fixAll": true
517
}

docs/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,32 @@ This template provides a foundation for Python projects with:
1717

1818
- Modern Python project structure
1919
- Development tooling configuration
20+
- Black for code formatting
21+
- Ruff for linting and import sorting
22+
- Type checking support
2023
- Testing framework setup
2124
- Docker support
2225
- Documentation templates
2326
- CI/CD examples
2427

28+
## Development Setup
29+
30+
### Code Quality Tools
31+
32+
This project uses modern Python code quality tools:
33+
34+
- **Black**: Code formatter that enforces a consistent style
35+
- **Ruff**: Fast Python linter and import sorter
36+
- Enforces PEP 8 style guide
37+
- Sorts imports automatically
38+
- Checks for common errors and anti-patterns
39+
- Type checking enforcement
40+
41+
VSCode is configured to automatically:
42+
- Format code on save using Black
43+
- Run Ruff for linting and import sorting
44+
- Provide type checking feedback
45+
2546
## Quick Links
2647

2748
- [Installation Guide](getting-started.md#installation)

main.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
from typing import Dict
77

88

9-
10-
11-
12-
139
def main() -> None:
1410
"""Main application entry point."""
1511
try:
@@ -22,4 +18,4 @@ def main() -> None:
2218

2319

2420
if __name__ == "__main__":
25-
main()
21+
main()

pyproject.toml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,30 @@ dependencies = [
1010
"numpy>=2.2.3",
1111
"pytest>=8.3.4",
1212
]
13+
14+
[tool.black]
15+
line-length = 88
16+
target-version = ["py311"]
17+
include = '\.pyi?$'
18+
19+
[tool.ruff]
20+
line-length = 88
21+
target-version = "py311"
22+
select = [
23+
"E", # pycodestyle errors
24+
"W", # pycodestyle warnings
25+
"F", # pyflakes
26+
"I", # isort
27+
"C", # flake8-comprehensions
28+
"B", # flake8-bugbear
29+
]
30+
ignore = []
31+
32+
[tool.ruff.per-file-ignores]
33+
"__init__.py" = ["F401"]
34+
35+
[tool.ruff.isort]
36+
known-first-party = ["src"]
37+
38+
[tool.ruff.mccabe]
39+
max-complexity = 10

src/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""
22
Main application package.
33
This is the root package of the application.
4-
"""
4+
"""

src/common/config.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66
from pathlib import Path
77
from typing import Any, Optional
88

9+
910
@dataclass
1011
class AppConfig:
1112
"""Application configuration."""
13+
1214
app_name: str
1315
environment: str
1416
debug: bool
1517
log_level: str
16-
18+
1719
@classmethod
1820
def from_env(cls) -> "AppConfig":
1921
"""Create configuration from environment variables.
20-
22+
2123
Returns:
2224
AppConfig: Application configuration instance
2325
"""
@@ -28,35 +30,39 @@ def from_env(cls) -> "AppConfig":
2830
log_level=os.getenv("LOG_LEVEL", "INFO"),
2931
)
3032

33+
3134
def load_json_config(path: Path) -> dict[str, Any]:
3235
"""Load configuration from JSON file.
33-
36+
3437
Args:
3538
path: Path to JSON configuration file
36-
39+
3740
Returns:
3841
dict[str, Any]: Configuration dictionary
39-
42+
4043
Raises:
4144
FileNotFoundError: If configuration file doesn't exist
4245
json.JSONDecodeError: If configuration file is invalid JSON
4346
"""
4447
if not path.exists():
4548
raise FileNotFoundError(f"Configuration file not found: {path}")
46-
49+
4750
with path.open() as f:
4851
return json.load(f)
4952

53+
5054
class ConfigurationError(Exception):
5155
"""Base class for configuration errors."""
56+
5257
pass
5358

59+
5460
def get_config_path(config_name: str) -> Path:
5561
"""Get configuration file path.
56-
62+
5763
Args:
5864
config_name: Name of the configuration file
59-
65+
6066
Returns:
6167
Path: Path to configuration file
6268
"""
@@ -66,10 +72,10 @@ def get_config_path(config_name: str) -> Path:
6672
Path("~/.config/template-python"), # User config directory
6773
Path("/etc/template-python"), # System config directory
6874
]
69-
75+
7076
for location in locations:
7177
path = location.expanduser() / f"{config_name}.json"
7278
if path.exists():
7379
return path
74-
75-
return locations[0].expanduser() / f"{config_name}.json"
80+
81+
return locations[0].expanduser() / f"{config_name}.json"

src/shared/utils/validation.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,50 @@
55

66
# Common validation patterns
77
EMAIL_PATTERN: Pattern = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
8-
UUID_PATTERN: Pattern = re.compile(r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$")
8+
UUID_PATTERN: Pattern = re.compile(
9+
r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
10+
)
11+
912

1013
def validate_email(email: str) -> bool:
1114
"""Validate email format.
12-
15+
1316
Args:
1417
email: Email address to validate
15-
18+
1619
Returns:
1720
bool: True if email format is valid, False otherwise
1821
"""
1922
return bool(EMAIL_PATTERN.match(email))
2023

24+
2125
def validate_uuid(uuid: str) -> bool:
2226
"""Validate UUID format.
23-
27+
2428
Args:
2529
uuid: UUID string to validate
26-
30+
2731
Returns:
2832
bool: True if UUID format is valid, False otherwise
2933
"""
3034
return bool(UUID_PATTERN.match(uuid.lower()))
3135

32-
def validate_required_fields(data: dict[str, Any], required_fields: list[str]) -> tuple[bool, Optional[str]]:
36+
37+
def validate_required_fields(
38+
data: dict[str, Any], required_fields: list[str]
39+
) -> tuple[bool, Optional[str]]:
3340
"""Validate required fields in a dictionary.
34-
41+
3542
Args:
3643
data: Dictionary containing data to validate
3744
required_fields: List of required field names
38-
45+
3946
Returns:
4047
tuple[bool, Optional[str]]: (is_valid, error_message)
4148
"""
4249
missing_fields = [field for field in required_fields if field not in data]
43-
50+
4451
if missing_fields:
4552
return False, f"Missing required fields: {', '.join(missing_fields)}"
46-
47-
return True, None
53+
54+
return True, None

src/utils/loader.py

Lines changed: 0 additions & 65 deletions
This file was deleted.

src/utils/math.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,53 @@
44

55
Number = Union[int, float]
66

7+
78
def calculate_mean(numbers: List[Number]) -> float:
89
"""Calculate the arithmetic mean of a list of numbers.
9-
10+
1011
Args:
1112
numbers: A list of integers or floating point numbers
12-
13+
1314
Returns:
1415
float: The arithmetic mean of the input numbers
15-
16+
1617
Raises:
1718
ValueError: If the input list is empty
1819
TypeError: If any element is not a number
1920
"""
2021
if not numbers:
2122
raise ValueError("Cannot calculate mean of empty list")
22-
23+
2324
if not all(isinstance(x, (int, float)) for x in numbers):
2425
raise TypeError("All elements must be numbers")
25-
26+
2627
return sum(numbers) / len(numbers)
2728

29+
2830
def calculate_median(numbers: List[Number]) -> float:
2931
"""Calculate the median value from a list of numbers.
30-
32+
3133
Args:
3234
numbers: A list of integers or floating point numbers
33-
35+
3436
Returns:
3537
float: The median value of the input numbers
36-
38+
3739
Raises:
3840
ValueError: If the input list is empty
3941
TypeError: If any element is not a number
4042
"""
4143
if not numbers:
4244
raise ValueError("Cannot calculate median of empty list")
43-
45+
4446
if not all(isinstance(x, (int, float)) for x in numbers):
4547
raise TypeError("All elements must be numbers")
46-
48+
4749
sorted_numbers = sorted(numbers)
4850
length = len(sorted_numbers)
49-
51+
5052
if length % 2 == 0:
5153
mid = length // 2
5254
return (sorted_numbers[mid - 1] + sorted_numbers[mid]) / 2
5355
else:
54-
return sorted_numbers[length // 2]
56+
return sorted_numbers[length // 2]

0 commit comments

Comments
 (0)