Skip to content

Commit 9cb2f30

Browse files
committed
Add codecov.yml. Fix more mypy.
1 parent f771e66 commit 9cb2f30

File tree

3 files changed

+152
-1
lines changed

3 files changed

+152
-1
lines changed

.codecov.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Codecov Configuration
2+
# Documentation: https://docs.codecov.com/docs/codecovyml-reference
3+
4+
coverage:
5+
precision: 2 # Number of decimal places (0-5)
6+
round: down # How to round coverage (down/up/nearest)
7+
range: 70..100 # Color coding range (red at 70%, green at 100%)
8+
9+
status:
10+
# Project coverage: overall repository coverage
11+
project:
12+
default:
13+
target: 90% # Minimum coverage threshold
14+
threshold: null # No threshold - only fail if below target
15+
base: auto # Compare against base branch
16+
informational: false # Fail the check if below target
17+
18+
# Patch coverage: coverage on changed lines only
19+
patch:
20+
default:
21+
target: 80% # New code should have at least 80% coverage
22+
threshold: 0% # No wiggle room for patch coverage
23+
base: auto
24+
informational: false # Fail if new code doesn't meet target
25+
26+
# Pull request comment configuration
27+
comment:
28+
layout: "diff, flags, files, footer" # What to show in PR comments
29+
behavior: default # Comment on all PRs
30+
require_changes: false # Comment even if coverage unchanged
31+
require_base: false # Comment even without base report
32+
require_head: true # Only comment if head report exists
33+
34+
# Paths to ignore in coverage reports
35+
ignore:
36+
- "tests/*" # Test files
37+
- "tests/**/*" # All test subdirectories
38+
- "docs/*" # Documentation
39+
- "site/*" # Built documentation site
40+
- "htmlcov/*" # Coverage HTML reports
41+
- ".venv/*" # Virtual environment
42+
- ".tox/*" # Tox environments
43+
- "**/__pycache__/*" # Python cache
44+
- "**/conftest.py" # Pytest configuration
45+
- "update_tests.py" # Utility scripts
46+
47+
# GitHub Checks configuration
48+
github_checks:
49+
annotations: true # Show coverage annotations on changed files

src/sparkwheel/schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ def _validate_field(
656656
return
657657

658658
# Handle Any type - accept any value
659-
if expected_type is Any:
659+
if expected_type == Any:
660660
return
661661

662662
# Handle basic types (int, str, float, bool, etc.)

tests/test_schema.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,5 +1177,107 @@ class Config:
11771177
validate({"value": MISSING}, Config, allow_missing=True)
11781178

11791179

1180+
class TestAnyTypeValidation:
1181+
"""Test validation with Any type."""
1182+
1183+
def test_any_type_accepts_any_value(self):
1184+
"""Test that Any type accepts any value."""
1185+
from typing import Any
1186+
1187+
@dataclass
1188+
class Config:
1189+
value: Any
1190+
1191+
# Should accept any type
1192+
validate({"value": 42}, Config)
1193+
validate({"value": "string"}, Config)
1194+
validate({"value": [1, 2, 3]}, Config)
1195+
validate({"value": {"nested": "dict"}}, Config)
1196+
validate({"value": None}, Config)
1197+
1198+
1199+
class TestValidatorExceptionHandling:
1200+
"""Test validator exception handling."""
1201+
1202+
def test_validator_with_bad_init(self):
1203+
"""Test validator when dataclass __init__ raises exception."""
1204+
from sparkwheel.schema import validator
1205+
1206+
@dataclass
1207+
class Config:
1208+
value: int
1209+
1210+
def __post_init__(self):
1211+
# This will raise during validation
1212+
if self.value < 0:
1213+
raise ValueError("Value must be positive")
1214+
1215+
@validator
1216+
def check_value(self):
1217+
# This validator won't run if __init__ fails
1218+
assert self.value > 0
1219+
1220+
# Should still validate the types even if instance creation fails
1221+
validate({"value": -5}, Config)
1222+
1223+
1224+
class TestUnionValidationSuccess:
1225+
"""Test union validation success path."""
1226+
1227+
def test_union_first_type_succeeds(self):
1228+
"""Test union validation when first type succeeds."""
1229+
1230+
@dataclass
1231+
class Config:
1232+
value: int | str
1233+
1234+
# First type (int) should succeed
1235+
validate({"value": 42}, Config)
1236+
1237+
def test_union_second_type_succeeds(self):
1238+
"""Test union validation when second type succeeds."""
1239+
1240+
@dataclass
1241+
class Config:
1242+
value: int | str
1243+
1244+
# Second type (str) should succeed
1245+
validate({"value": "hello"}, Config)
1246+
1247+
1248+
class TestMetadataExceptionHandling:
1249+
"""Test metadata source location exception handling."""
1250+
1251+
def test_metadata_get_raises_exception(self):
1252+
"""Test _get_source_location when metadata.get raises."""
1253+
1254+
class BadMetadata:
1255+
def get(self, key):
1256+
raise RuntimeError("Bad metadata")
1257+
1258+
@dataclass
1259+
class Config:
1260+
value: int
1261+
1262+
# Should handle exception gracefully
1263+
with pytest.raises(ValidationError, match="Missing required field"):
1264+
validate({}, Config, metadata=BadMetadata())
1265+
1266+
def test_metadata_returns_non_source_location(self):
1267+
"""Test _get_source_location when metadata returns wrong type."""
1268+
1269+
class BadMetadata:
1270+
def get(self, key):
1271+
return "not a source location"
1272+
1273+
@dataclass
1274+
class Config:
1275+
value: int
1276+
1277+
# Should handle gracefully
1278+
with pytest.raises(ValidationError, match="Missing required field"):
1279+
validate({}, Config, metadata=BadMetadata())
1280+
1281+
11801282
if __name__ == "__main__":
11811283
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)