From c90777f0da6f84ef718936c0c56c8a19fe0f3bf9 Mon Sep 17 00:00:00 2001 From: Miki Palet <64255955+mikipalet@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:25:36 +0100 Subject: [PATCH 1/4] fix: rename TikTokSettings to TikTokPlatformData to match generated models The model was renamed to TikTokPlatformData in the generated code but the explicit imports in __init__.py and tests still referenced the old TikTokSettings name, causing an ImportError on any SDK import. Fixes #12 Co-Authored-By: Claude Opus 4.6 --- src/late/models/__init__.py | 4 ++-- tests/test_exhaustive.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/late/models/__init__.py b/src/late/models/__init__.py index 2795ce7..56bc4de 100644 --- a/src/late/models/__init__.py +++ b/src/late/models/__init__.py @@ -60,7 +60,7 @@ # Enums Status, # Platform-specific - TikTokSettings, + TikTokPlatformData, TranscriptResponse, TranscriptSegment, TwitterPlatformData, @@ -95,7 +95,7 @@ "Type", "Visibility", # Platform-specific - "TikTokSettings", + "TikTokPlatformData", "TwitterPlatformData", "InstagramPlatformData", "FacebookPlatformData", diff --git a/tests/test_exhaustive.py b/tests/test_exhaustive.py index 9f6a243..0417aa9 100644 --- a/tests/test_exhaustive.py +++ b/tests/test_exhaustive.py @@ -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 @@ -280,10 +280,10 @@ 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 + """Test TikTokPlatformData model creation.""" + from late.models import TikTokPlatformData - settings = TikTokSettings( + settings = TikTokPlatformData( allow_comment=True, allow_duet=True, allow_stitch=True, From 74f2b69457b7f4114abd7b012e5b23857d8e896b Mon Sep 17 00:00:00 2001 From: Miki Palet <64255955+mikipalet@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:28:16 +0100 Subject: [PATCH 2/4] fix: prevent broken SDK releases from stale model imports Two changes to stop this class of bug: 1. Remove `|| echo` from test step in generate.yml so test failures actually block the release pipeline instead of silently publishing a broken SDK to PyPI. 2. Add import validation to generate_models.py that checks every explicit import in models/__init__.py exists in the generated models. Fails the pipeline if the OpenAPI spec renamed/removed a model that __init__.py still references. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/generate.yml | 2 +- scripts/generate_models.py | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 058c92d..9a5e259 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -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 diff --git a/scripts/generate_models.py b/scripts/generate_models.py index 6f9f396..a82d989 100644 --- a/scripts/generate_models.py +++ b/scripts/generate_models.py @@ -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.") From a55689ca5c8c9d8b109b657a8efb70d148881f2c Mon Sep 17 00:00:00 2001 From: Miki Palet <64255955+mikipalet@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:30:32 +0100 Subject: [PATCH 3/4] fix: ignore TCH003 lint rule for auto-generated models The generated models file uses runtime imports for `datetime.date` which triggers ruff's TCH003 rule. Since this file is auto-generated by datamodel-code-generator, we should ignore this rule like the other generated-code-specific ignores. Co-Authored-By: Claude Opus 4.6 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6665fcb..7cb2707 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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"] From 69dd81295f5ef0af7c309f08030993b88e51b2ab Mon Sep 17 00:00:00 2001 From: Miki Palet <64255955+mikipalet@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:31:52 +0100 Subject: [PATCH 4/4] fix: update tests to match generated model field names - Status enum is success/failed (webhook log status), not draft/scheduled - TikTokPlatformData uses camelCase fields (allowComment, privacyLevel) Co-Authored-By: Claude Opus 4.6 --- tests/test_exhaustive.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/test_exhaustive.py b/tests/test_exhaustive.py index 0417aa9..ed86fec 100644 --- a/tests/test_exhaustive.py +++ b/tests/test_exhaustive.py @@ -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.""" @@ -284,13 +284,13 @@ def test_tiktok_settings_creation(self): from late.models import TikTokPlatformData settings = TikTokPlatformData( - allow_comment=True, - allow_duet=True, - allow_stitch=True, - privacy_level="PUBLIC", + 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."""