phlo-core-plugins 0.1.0__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/PKG-INFO +3 -2
  2. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/pyproject.toml +32 -23
  3. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/__init__.py +1 -1
  4. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/quality/freshness_check.py +16 -3
  5. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/quality/null_check.py +16 -3
  6. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/quality/schema_check.py +14 -3
  7. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/quality/uniqueness_check.py +16 -3
  8. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/sources/rest_api.py +33 -0
  9. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core_plugins.egg-info/PKG-INFO +3 -2
  10. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core_plugins.egg-info/requires.txt +3 -1
  11. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/tests/test_integration_core_plugins.py +10 -0
  12. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/tests/test_quality_plugins.py +15 -0
  13. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/tests/test_rest_api_plugin.py +13 -0
  14. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/README.md +0 -0
  15. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/setup.cfg +0 -0
  16. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/quality/__init__.py +0 -0
  17. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core/sources/__init__.py +0 -0
  18. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core_plugins.egg-info/SOURCES.txt +0 -0
  19. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core_plugins.egg-info/dependency_links.txt +0 -0
  20. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core_plugins.egg-info/entry_points.txt +0 -0
  21. {phlo_core_plugins-0.1.0 → phlo_core_plugins-0.2.3}/src/phlo_core_plugins.egg-info/top_level.txt +0 -0
@@ -1,17 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phlo-core-plugins
3
- Version: 0.1.0
3
+ Version: 0.2.3
4
4
  Summary: Core plugin package for Phlo
5
5
  Author-email: Phlo Team <team@phlo.dev>
6
6
  License: MIT
7
7
  Requires-Python: >=3.11
8
8
  Description-Content-Type: text/plain
9
9
  Requires-Dist: phlo>=0.1.0
10
- Requires-Dist: phlo-quality>=0.1.0
11
10
  Requires-Dist: requests>=2.28.0
12
11
  Provides-Extra: dev
13
12
  Requires-Dist: pytest>=7.0; extra == "dev"
14
13
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
15
14
  Requires-Dist: ruff>=0.1.0; extra == "dev"
15
+ Provides-Extra: quality
16
+ Requires-Dist: phlo-pandera>=0.1.0; extra == "quality"
16
17
 
17
18
  Core plugin package for Phlo.
@@ -1,45 +1,54 @@
1
1
  [build-system]
2
- requires = ["setuptools>=45", "wheel"]
3
2
  build-backend = "setuptools.build_meta"
3
+ requires = [
4
+ "setuptools>=45",
5
+ "wheel",
6
+ ]
4
7
 
5
8
  [project]
6
- name = "phlo-core-plugins"
7
- version = "0.1.0"
8
- description = "Core plugin package for Phlo"
9
- readme = {text = "Core plugin package for Phlo.", content-type = "text/plain"}
10
- requires-python = ">=3.11"
11
- authors = [
12
- {name = "Phlo Team", email = "team@phlo.dev"},
13
- ]
14
- license = {text = "MIT"}
15
9
  dependencies = [
16
10
  "phlo>=0.1.0",
17
- "phlo-quality>=0.1.0",
18
11
  "requests>=2.28.0",
19
12
  ]
13
+ description = "Core plugin package for Phlo"
14
+ name = "phlo-core-plugins"
15
+ requires-python = ">=3.11"
16
+ version = "0.2.3"
20
17
 
21
- [project.optional-dependencies]
22
- dev = [
23
- "pytest>=7.0",
24
- "pytest-cov>=4.0",
25
- "ruff>=0.1.0",
26
- ]
18
+ [[project.authors]]
19
+ email = "team@phlo.dev"
20
+ name = "Phlo Team"
27
21
 
28
22
  [project.entry-points."phlo.plugins.quality"]
29
- null_check = "phlo_core.quality.null_check:NullCheckPlugin"
30
- uniqueness_check = "phlo_core.quality.uniqueness_check:UniquenessCheckPlugin"
31
23
  freshness_check = "phlo_core.quality.freshness_check:FreshnessCheckPlugin"
24
+ null_check = "phlo_core.quality.null_check:NullCheckPlugin"
32
25
  schema_check = "phlo_core.quality.schema_check:SchemaCheckPlugin"
26
+ uniqueness_check = "phlo_core.quality.uniqueness_check:UniquenessCheckPlugin"
33
27
 
34
28
  [project.entry-points."phlo.plugins.sources"]
35
29
  rest_api = "phlo_core.sources.rest_api:RestAPIPlugin"
36
30
 
37
- [tool.setuptools]
38
- package-dir = {"" = "src"}
31
+ [project.license]
32
+ text = "MIT"
39
33
 
40
- [tool.setuptools.packages.find]
41
- where = ["src"]
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=7.0",
37
+ "pytest-cov>=4.0",
38
+ "ruff>=0.1.0",
39
+ ]
40
+ quality = ["phlo-pandera>=0.1.0"]
41
+
42
+ [project.readme]
43
+ content-type = "text/plain"
44
+ text = "Core plugin package for Phlo."
42
45
 
43
46
  [tool.ruff]
44
47
  line-length = 100
45
48
  target-version = "py311"
49
+
50
+ [tool.setuptools.package-dir]
51
+ "" = "src"
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
@@ -13,4 +13,4 @@ __all__ = [
13
13
  "SchemaCheckPlugin",
14
14
  "RestAPIPlugin",
15
15
  ]
16
- __version__ = "0.1.0"
16
+ __version__ = "0.2.3"
@@ -1,16 +1,17 @@
1
1
  """Freshness check plugin."""
2
2
 
3
3
  from datetime import datetime
4
+ from typing import Any
4
5
 
5
6
  from phlo.plugins import PluginMetadata, QualityCheckPlugin
6
- from phlo_quality.checks import FreshnessCheck
7
7
 
8
8
 
9
- class FreshnessCheckPlugin(QualityCheckPlugin[FreshnessCheck]):
9
+ class FreshnessCheckPlugin(QualityCheckPlugin[Any]):
10
10
  """Plugin for freshness checks."""
11
11
 
12
12
  @property
13
13
  def metadata(self) -> PluginMetadata:
14
+ """Return plugin metadata for the freshness-check plugin."""
14
15
  return PluginMetadata(
15
16
  name="freshness_check",
16
17
  version="0.1.0",
@@ -24,7 +25,19 @@ class FreshnessCheckPlugin(QualityCheckPlugin[FreshnessCheck]):
24
25
  timestamp_column: str,
25
26
  max_age_hours: float,
26
27
  reference_time: datetime | None = None,
27
- ) -> FreshnessCheck:
28
+ ) -> Any:
29
+ """Create a freshness check instance.
30
+
31
+ Args:
32
+ timestamp_column: Timestamp column used for freshness calculations.
33
+ max_age_hours: Maximum allowed age in hours.
34
+ reference_time: Optional reference time for age evaluation.
35
+
36
+ Returns:
37
+ Configured freshness-check instance.
38
+ """
39
+ from phlo_pandera.checks import FreshnessCheck
40
+
28
41
  return FreshnessCheck(
29
42
  timestamp_column=timestamp_column,
30
43
  max_age_hours=max_age_hours,
@@ -1,14 +1,16 @@
1
1
  """Null check plugin."""
2
2
 
3
+ from typing import Any
4
+
3
5
  from phlo.plugins import PluginMetadata, QualityCheckPlugin
4
- from phlo_quality.checks import NullCheck
5
6
 
6
7
 
7
- class NullCheckPlugin(QualityCheckPlugin[NullCheck]):
8
+ class NullCheckPlugin(QualityCheckPlugin[Any]):
8
9
  """Plugin for NullCheck quality checks."""
9
10
 
10
11
  @property
11
12
  def metadata(self) -> PluginMetadata:
13
+ """Return plugin metadata for the null-check quality plugin."""
12
14
  return PluginMetadata(
13
15
  name="null_check",
14
16
  version="0.1.0",
@@ -17,5 +19,16 @@ class NullCheckPlugin(QualityCheckPlugin[NullCheck]):
17
19
  tags=["quality", "nulls"],
18
20
  )
19
21
 
20
- def create_check(self, columns: list[str], allow_threshold: float = 0.0) -> NullCheck:
22
+ def create_check(self, columns: list[str], allow_threshold: float = 0.0) -> Any:
23
+ """Create a null check instance.
24
+
25
+ Args:
26
+ columns: Column names to validate for null values.
27
+ allow_threshold: Maximum allowed null ratio per column.
28
+
29
+ Returns:
30
+ Configured null-check instance.
31
+ """
32
+ from phlo_pandera.checks import NullCheck
33
+
21
34
  return NullCheck(columns=columns, allow_threshold=allow_threshold)
@@ -3,14 +3,14 @@
3
3
  from typing import Any
4
4
 
5
5
  from phlo.plugins import PluginMetadata, QualityCheckPlugin
6
- from phlo_quality.checks_extra import SchemaCheck
7
6
 
8
7
 
9
- class SchemaCheckPlugin(QualityCheckPlugin[SchemaCheck]):
8
+ class SchemaCheckPlugin(QualityCheckPlugin[Any]):
10
9
  """Plugin for schema checks."""
11
10
 
12
11
  @property
13
12
  def metadata(self) -> PluginMetadata:
13
+ """Return plugin metadata for the schema-check plugin."""
14
14
  return PluginMetadata(
15
15
  name="schema_check",
16
16
  version="0.1.0",
@@ -19,5 +19,16 @@ class SchemaCheckPlugin(QualityCheckPlugin[SchemaCheck]):
19
19
  tags=["quality", "schema"],
20
20
  )
21
21
 
22
- def create_check(self, schema: Any, lazy: bool = True) -> SchemaCheck:
22
+ def create_check(self, schema: Any, lazy: bool = True) -> Any:
23
+ """Create a schema check instance.
24
+
25
+ Args:
26
+ schema: Expected schema object for validation.
27
+ lazy: Whether to collect all validation errors before failing.
28
+
29
+ Returns:
30
+ Configured schema-check instance.
31
+ """
32
+ from phlo_pandera.checks_extra import SchemaCheck
33
+
23
34
  return SchemaCheck(schema=schema, lazy=lazy)
@@ -1,14 +1,16 @@
1
1
  """Uniqueness check plugin."""
2
2
 
3
+ from typing import Any
4
+
3
5
  from phlo.plugins import PluginMetadata, QualityCheckPlugin
4
- from phlo_quality.checks import UniqueCheck
5
6
 
6
7
 
7
- class UniquenessCheckPlugin(QualityCheckPlugin[UniqueCheck]):
8
+ class UniquenessCheckPlugin(QualityCheckPlugin[Any]):
8
9
  """Plugin for uniqueness checks."""
9
10
 
10
11
  @property
11
12
  def metadata(self) -> PluginMetadata:
13
+ """Return plugin metadata for the uniqueness-check plugin."""
12
14
  return PluginMetadata(
13
15
  name="uniqueness_check",
14
16
  version="0.1.0",
@@ -17,5 +19,16 @@ class UniquenessCheckPlugin(QualityCheckPlugin[UniqueCheck]):
17
19
  tags=["quality", "uniqueness"],
18
20
  )
19
21
 
20
- def create_check(self, columns: list[str], allow_threshold: float = 0.0) -> UniqueCheck:
22
+ def create_check(self, columns: list[str], allow_threshold: float = 0.0) -> Any:
23
+ """Create a uniqueness check instance.
24
+
25
+ Args:
26
+ columns: Column names that must be unique.
27
+ allow_threshold: Maximum allowed duplicate ratio.
28
+
29
+ Returns:
30
+ Configured uniqueness-check instance.
31
+ """
32
+ from phlo_pandera.checks import UniqueCheck
33
+
21
34
  return UniqueCheck(columns=columns, allow_threshold=allow_threshold)
@@ -12,6 +12,11 @@ class RestAPIPlugin(SourceConnectorPlugin):
12
12
 
13
13
  @property
14
14
  def metadata(self) -> PluginMetadata:
15
+ """Get plugin metadata.
16
+
17
+ Returns:
18
+ PluginMetadata: Metadata for the REST API source connector plugin.
19
+ """
15
20
  return PluginMetadata(
16
21
  name="rest_api",
17
22
  version="0.1.0",
@@ -21,6 +26,14 @@ class RestAPIPlugin(SourceConnectorPlugin):
21
26
  )
22
27
 
23
28
  def fetch_data(self, config: dict[str, Any]):
29
+ """Fetch records from a REST API endpoint.
30
+
31
+ Args:
32
+ config: Source configuration containing request and extraction options.
33
+
34
+ Yields:
35
+ dict[str, Any]: Records extracted from the response payload.
36
+ """
24
37
  url = config["url"]
25
38
  headers = config.get("headers", {})
26
39
  params = config.get("params", {})
@@ -36,10 +49,30 @@ class RestAPIPlugin(SourceConnectorPlugin):
36
49
  yield record
37
50
 
38
51
  def get_schema(self, config: dict[str, Any]) -> dict[str, str] | None:
52
+ """Get optional static schema for records.
53
+
54
+ Args:
55
+ config: Source configuration that may include a schema mapping.
56
+
57
+ Returns:
58
+ dict[str, str] | None: Schema mapping if present.
59
+ """
39
60
  return config.get("schema")
40
61
 
41
62
 
42
63
  def _extract_records(payload: Any, records_path: str | None) -> list[dict[str, Any]]:
64
+ """Extract record objects from a JSON payload.
65
+
66
+ Args:
67
+ payload: Parsed JSON response payload.
68
+ records_path: Dot-separated path to list or object records in payload.
69
+
70
+ Returns:
71
+ list[dict[str, Any]]: Normalized list of record objects.
72
+
73
+ Raises:
74
+ ValueError: If records path is missing or payload shape is unsupported.
75
+ """
43
76
  if records_path:
44
77
  current = payload
45
78
  for key in records_path.split("."):
@@ -1,17 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: phlo-core-plugins
3
- Version: 0.1.0
3
+ Version: 0.2.3
4
4
  Summary: Core plugin package for Phlo
5
5
  Author-email: Phlo Team <team@phlo.dev>
6
6
  License: MIT
7
7
  Requires-Python: >=3.11
8
8
  Description-Content-Type: text/plain
9
9
  Requires-Dist: phlo>=0.1.0
10
- Requires-Dist: phlo-quality>=0.1.0
11
10
  Requires-Dist: requests>=2.28.0
12
11
  Provides-Extra: dev
13
12
  Requires-Dist: pytest>=7.0; extra == "dev"
14
13
  Requires-Dist: pytest-cov>=4.0; extra == "dev"
15
14
  Requires-Dist: ruff>=0.1.0; extra == "dev"
15
+ Provides-Extra: quality
16
+ Requires-Dist: phlo-pandera>=0.1.0; extra == "quality"
16
17
 
17
18
  Core plugin package for Phlo.
@@ -1,8 +1,10 @@
1
1
  phlo>=0.1.0
2
- phlo-quality>=0.1.0
3
2
  requests>=2.28.0
4
3
 
5
4
  [dev]
6
5
  pytest>=7.0
7
6
  pytest-cov>=4.0
8
7
  ruff>=0.1.0
8
+
9
+ [quality]
10
+ phlo-pandera>=0.1.0
@@ -64,12 +64,16 @@ class TestServicePlugin:
64
64
 
65
65
  # Attempting to instantiate directly should fail or have abstract methods
66
66
  class ConcretePlugin(ServicePlugin):
67
+ """Concrete service plugin for abstract base class testing."""
68
+
67
69
  @property
68
70
  def metadata(self):
71
+ """Get test plugin metadata."""
69
72
  return PluginMetadata("test", "1.0.0", "test")
70
73
 
71
74
  @property
72
75
  def service_definition(self):
76
+ """Get minimal service definition for test instantiation."""
73
77
  return {"services": {}}
74
78
 
75
79
  plugin = ConcretePlugin()
@@ -156,19 +160,25 @@ class TestCatalogPlugin:
156
160
  from phlo.plugins.base import CatalogPlugin, PluginMetadata
157
161
 
158
162
  class MockCatalog(CatalogPlugin):
163
+ """Concrete catalog plugin used to validate interface behavior."""
164
+
159
165
  @property
160
166
  def metadata(self):
167
+ """Get mock catalog metadata."""
161
168
  return PluginMetadata("mock", "1.0.0", "Mock catalog")
162
169
 
163
170
  @property
164
171
  def targets(self) -> list[str]:
172
+ """Get supported target engines."""
165
173
  return ["trino"]
166
174
 
167
175
  @property
168
176
  def catalog_name(self):
177
+ """Get catalog identifier."""
169
178
  return "mock"
170
179
 
171
180
  def get_properties(self):
181
+ """Get connector properties for the catalog."""
172
182
  return {"connector.name": "mock"}
173
183
 
174
184
  catalog = MockCatalog()
@@ -7,30 +7,45 @@ from phlo_core.quality.uniqueness_check import UniquenessCheckPlugin
7
7
 
8
8
 
9
9
  def test_null_check_plugin():
10
+ """Verify null check plugin creates an expected check name."""
10
11
  plugin = NullCheckPlugin()
11
12
  check = plugin.create_check(columns=["id"])
12
13
  assert check.name == "null_check_id"
13
14
 
14
15
 
15
16
  def test_uniqueness_check_plugin():
17
+ """Verify uniqueness check plugin creates an expected check name."""
16
18
  plugin = UniquenessCheckPlugin()
17
19
  check = plugin.create_check(columns=["id"])
18
20
  assert check.name == "unique_check_id"
19
21
 
20
22
 
21
23
  def test_freshness_check_plugin():
24
+ """Verify freshness check plugin creates an expected check name."""
22
25
  plugin = FreshnessCheckPlugin()
23
26
  check = plugin.create_check(timestamp_column="ts", max_age_hours=2)
24
27
  assert check.name == "freshness_check_ts"
25
28
 
26
29
 
27
30
  def test_schema_check_plugin():
31
+ """Verify schema check plugin creates an expected check name."""
28
32
  plugin = SchemaCheckPlugin()
29
33
 
30
34
  class DummySchema:
35
+ """Minimal schema stub used by the schema check plugin test."""
36
+
31
37
  __name__ = "DummySchema"
32
38
 
33
39
  def validate(self, df, lazy=True):
40
+ """Return input dataframe unchanged.
41
+
42
+ Args:
43
+ df: Input dataframe-like object.
44
+ lazy: Validation mode flag.
45
+
46
+ Returns:
47
+ The unmodified input dataframe.
48
+ """
34
49
  return df
35
50
 
36
51
  check = plugin.create_check(schema=DummySchema)
@@ -4,21 +4,32 @@ from phlo_core.sources.rest_api import RestAPIPlugin
4
4
 
5
5
 
6
6
  class DummyResponse:
7
+ """Simple response test double for requests.get."""
8
+
7
9
  def __init__(self, payload):
10
+ """Initialize the response with a JSON payload.
11
+
12
+ Args:
13
+ payload: JSON-serializable data returned by ``json()``.
14
+ """
8
15
  self._payload = payload
9
16
 
10
17
  def raise_for_status(self):
18
+ """Simulate a successful HTTP status response."""
11
19
  return None
12
20
 
13
21
  def json(self):
22
+ """Return the configured payload."""
14
23
  return self._payload
15
24
 
16
25
 
17
26
  def test_rest_api_plugin_fetches_records(monkeypatch):
27
+ """Verify the plugin yields records from a top-level list payload."""
18
28
  plugin = RestAPIPlugin()
19
29
  payload = [{"id": 1}, {"id": 2}]
20
30
 
21
31
  def dummy_get(url, headers=None, params=None, timeout=30):
32
+ """Return a dummy response for the request call."""
22
33
  return DummyResponse(payload)
23
34
 
24
35
  monkeypatch.setattr("phlo_core.sources.rest_api.requests.get", dummy_get)
@@ -28,10 +39,12 @@ def test_rest_api_plugin_fetches_records(monkeypatch):
28
39
 
29
40
 
30
41
  def test_rest_api_plugin_records_path(monkeypatch):
42
+ """Verify the plugin resolves records via a nested records path."""
31
43
  plugin = RestAPIPlugin()
32
44
  payload = {"data": {"items": [{"id": 3}]}}
33
45
 
34
46
  def dummy_get(url, headers=None, params=None, timeout=30):
47
+ """Return a dummy response for the request call."""
35
48
  return DummyResponse(payload)
36
49
 
37
50
  monkeypatch.setattr("phlo_core.sources.rest_api.requests.get", dummy_get)