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
16 changes: 16 additions & 0 deletions core/collectors/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

from __future__ import annotations

import warnings
from abc import ABC, abstractmethod

from django.core.management import call_command

from core.collectors.base_collector import _CollectorLifecycleMixin


# TODO(v1.0): remove CollectorBase after migrating callers to AbstractCollector.
class CollectorBase(_CollectorLifecycleMixin, ABC):
"""
Legacy abstract base for collectors run via management commands or YAML schedules.
Expand Down Expand Up @@ -43,6 +45,17 @@ class CollectorBase(_CollectorLifecycleMixin, ABC):
by the command layer without calling :meth:`~_CollectorLifecycleMixin.handle_error`.
"""

def __init_subclass__(cls, **kwargs: object) -> None:
super().__init_subclass__(**kwargs)
if getattr(cls, "_skip_collector_base_deprecation", False):
return
warnings.warn(
"CollectorBase is deprecated; use AbstractCollector instead. "
"CollectorBase will be removed in v1.0.",
DeprecationWarning,
stacklevel=2,
)

@abstractmethod
def run(self) -> None:
"""
Expand All @@ -61,6 +74,9 @@ def run(self) -> None:
class DjangoCommandCollector(CollectorBase):
"""Runs a registered Django management command by name."""

# Library adapter: not an app-defined collector; skip CollectorBase deprecation noise.
_skip_collector_base_deprecation = True

__slots__ = ("command_name",)

def __init__(self, command_name: str) -> None:
Expand Down
19 changes: 19 additions & 0 deletions core/tests/test_collectors_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Tests for core.collectors base types."""

import warnings
from io import StringIO
from unittest.mock import patch

Expand All @@ -12,6 +13,24 @@
from core.collectors.command_base import BaseCollectorCommand


def test_collector_base_subclass_emits_deprecation_warning():
assert getattr(DjangoCommandCollector, "_skip_collector_base_deprecation") is True
Comment thread
leostar0412 marked this conversation as resolved.

with warnings.catch_warnings(record=True) as recorded:
warnings.simplefilter("always", DeprecationWarning)

class _WarnProbeCollector(CollectorBase):
def run(self) -> None:
return None

dep = [w for w in recorded if issubclass(w.category, DeprecationWarning)]
assert dep, "expected DeprecationWarning when subclassing CollectorBase"
msg = str(dep[-1].message)
assert "CollectorBase" in msg
assert "AbstractCollector" in msg
assert "v1.0" in msg


@pytest.mark.django_db
def test_django_command_collector_run_calls_call_command():
with patch("core.collectors.base.call_command") as m:
Expand Down
2 changes: 1 addition & 1 deletion docs/Core_public_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The `core` Django app holds shared infrastructure. Treat the following as the **

| Import | Purpose |
|--------|---------|
| `core.collectors.CollectorBase` | Legacy abstract `run()`, optional `sync_pinecone()`, `handle_error()` with structured logging. |
| `core.collectors.CollectorBase` | **Deprecated** (removed in v1.0): legacy abstract `run()`, optional `sync_pinecone()`, `handle_error()` with structured logging. Subclassing emits `DeprecationWarning` at class definition time. Prefer `AbstractCollector`|
| `core.collectors.AbstractCollector` | Preferred contract: `name`, `validate_config()`, `collect()`; concrete `run()` runs validate then collect; same lifecycle hooks as `CollectorBase`. |
| `core.collectors.CollectorRunnable` | `Protocol` for objects returned from `get_collector()` (`run`, `sync_pinecone`, `handle_error`). |
| `core.collectors.BaseCollectorCommand` | Thin `BaseCommand` adapter: runs `get_collector(**opts).run()` then `sync_pinecone()`. |
Expand Down
Loading