Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/generate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
uv run ruff format src/late/mcp/generated_tools.py || true

- name: Run tests
run: uv run pytest tests -v --tb=short || echo "Tests failed but continuing..."
run: uv run pytest tests -v --tb=short

- name: Check for changes
id: changes
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ ignore = [
]

[tool.ruff.lint.per-file-ignores]
"**/models/_generated/*" = ["UP006", "UP007", "UP035", "W291"] # Allow old-style annotations and trailing whitespace in generated code
"**/models/_generated/**" = ["UP006", "UP007", "UP035", "W291"]
"**/models/_generated/*" = ["UP006", "UP007", "UP035", "W291", "TCH003"] # Allow old-style annotations, trailing whitespace, and non-TYPE_CHECKING imports in generated code
"**/models/_generated/**" = ["UP006", "UP007", "UP035", "W291", "TCH003"]
"**/resources/_generated/*" = ["UP006", "UP007", "UP035", "W291", "ARG002"] # Allow old-style annotations in generated resources
"**/resources/_generated/**" = ["UP006", "UP007", "UP035", "W291", "ARG002"]

Expand Down
47 changes: 47 additions & 0 deletions scripts/generate_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,53 @@ def main() -> int:
print("Models generated successfully!")
print(f"Output: {output_dir}")
print()

# Validate that the parent models/__init__.py explicit imports
# all exist in the generated models. This catches renames/removals
# in the OpenAPI spec that would break the SDK at import time.
parent_init = output_dir.parent / "__init__.py"
if parent_init.exists():
print("Validating models/__init__.py imports against generated models...")
generated_source = output_file.read_text()

import ast
import re

# Extract all class/enum names from generated models
tree = ast.parse(generated_source)
generated_names = {
node.name
for node in ast.walk(tree)
if isinstance(node, ast.ClassDef)
}

# Extract explicit import names from the parent __init__.py
# (looks for "from ._generated.models import (...)" blocks)
init_source = parent_init.read_text()
import_names: list[str] = []
for match in re.finditer(
r"from \._generated\.models import \((.*?)\)",
init_source,
re.DOTALL,
):
block = match.group(1)
for name in re.findall(r"\b([A-Z]\w+)\b", block):
import_names.append(name)

missing = [name for name in import_names if name not in generated_names]
if missing:
print()
print("ERROR: models/__init__.py imports names that don't exist in generated models:")
for name in missing:
print(f" - {name}")
print()
print("The OpenAPI spec likely renamed or removed these models.")
print("Update src/late/models/__init__.py to match the generated models.")
return 1

print(f" All {len(import_names)} explicit imports verified.")

print()
print("Note: These are base models. Use the curated models in")
print("src/late/models/ for the public API.")

Expand Down
4 changes: 2 additions & 2 deletions src/late/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
# Enums
Status,
# Platform-specific
TikTokSettings,
TikTokPlatformData,
TranscriptResponse,
TranscriptSegment,
TwitterPlatformData,
Expand Down Expand Up @@ -95,7 +95,7 @@
"Type",
"Visibility",
# Platform-specific
"TikTokSettings",
"TikTokPlatformData",
"TwitterPlatformData",
"InstagramPlatformData",
"FacebookPlatformData",
Expand Down
40 changes: 20 additions & 20 deletions tests/test_exhaustive.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,12 +233,12 @@ def test_import_platform_models(self):
InstagramPlatformData,
LinkedInPlatformData,
PinterestPlatformData,
TikTokSettings,
TikTokPlatformData,
TwitterPlatformData,
YouTubePlatformData,
)

assert TikTokSettings is not None
assert TikTokPlatformData is not None
assert TwitterPlatformData is not None
assert InstagramPlatformData is not None
assert FacebookPlatformData is not None
Expand All @@ -258,17 +258,17 @@ class TestModelsValidation:
"""Test model validation."""

def test_status_enum_values(self):
"""Test Status enum has expected values."""
"""Test Status enum has expected values.

Note: The generated Status enum is for webhook log status (success/failed).
Post status values (draft/scheduled/published/failed) are in Status3.
"""
from late.models import Status

# Enums use uppercase attribute names
assert hasattr(Status, "DRAFT")
assert hasattr(Status, "SCHEDULED")
assert hasattr(Status, "PUBLISHED")
assert hasattr(Status, "SUCCESS")
assert hasattr(Status, "FAILED")
# Values are lowercase strings
assert Status.DRAFT.value == "draft"
assert Status.SCHEDULED.value == "scheduled"
assert Status.SUCCESS.value == "success"
assert Status.FAILED.value == "failed"

def test_type_enum_values(self):
"""Test Type enum has expected values."""
Expand All @@ -280,17 +280,17 @@ def test_type_enum_values(self):
assert Type.VIDEO.value == "video"

def test_tiktok_settings_creation(self):
"""Test TikTokSettings model creation."""
from late.models import TikTokSettings

settings = TikTokSettings(
allow_comment=True,
allow_duet=True,
allow_stitch=True,
privacy_level="PUBLIC",
"""Test TikTokPlatformData model creation."""
from late.models import TikTokPlatformData

settings = TikTokPlatformData(
allowComment=True,
allowDuet=True,
allowStitch=True,
privacyLevel="PUBLIC",
)
assert settings.allow_comment is True
assert settings.privacy_level == "PUBLIC"
assert settings.allowComment is True
assert settings.privacyLevel == "PUBLIC"

def test_media_item_creation(self):
"""Test MediaItem with type enum."""
Expand Down