-
Notifications
You must be signed in to change notification settings - Fork 264
[codex] Fix Pydantic default third-party filtering #2054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4e3e8ca
67d3828
bee0c9f
0ec2705
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,16 +2,17 @@ | |
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The new test functions added before the existing snapshot-based tests have shifted line numbers in this file, but the inline snapshots still contain the old Prompt for AI agents |
||
| import importlib.metadata | ||
| import os | ||
| from typing import TYPE_CHECKING, Annotated, Any | ||
| import sysconfig | ||
| from types import SimpleNamespace | ||
| from typing import TYPE_CHECKING, Annotated, Any, cast | ||
| from unittest.mock import patch | ||
|
|
||
| import cloudpickle | ||
| import pydantic | ||
| import pytest | ||
| import sqlmodel | ||
| from dirty_equals import IsInt | ||
| from inline_snapshot import snapshot | ||
| from opentelemetry.sdk.metrics.export import AggregationTemporality, InMemoryMetricReader | ||
| from opentelemetry.sdk.metrics.export import InMemoryMetricReader | ||
| from opentelemetry.sdk.trace.export import SimpleSpanProcessor | ||
| from pydantic import ( | ||
| AfterValidator, | ||
|
|
@@ -26,6 +27,7 @@ | |
| from pydantic_core import core_schema | ||
|
|
||
| import logfire | ||
| import logfire.integrations.pydantic as logfire_pydantic | ||
| from logfire._internal.config import GLOBAL_CONFIG | ||
| from logfire._internal.utils import get_version | ||
| from logfire.integrations.pydantic import ( | ||
|
|
@@ -157,6 +159,95 @@ def test_logfire_plugin_include_exclude_models( | |
| assert result == (None, None, None) | ||
|
|
||
|
|
||
| def _new_pydantic_plugin_result( | ||
| module: str, | ||
| name: str, | ||
| *, | ||
| include: set[str] | None = None, | ||
| exclude: set[str] | None = None, | ||
| ) -> tuple[object | None, object | None, object | None]: | ||
| getattr(logfire_pydantic, '_module_is_non_user_code').cache_clear() | ||
| getattr(logfire_pydantic, '_non_user_code_prefixes').cache_clear() | ||
| logfire.configure( | ||
| send_to_logfire=False, | ||
| metrics=logfire.MetricsOptions(additional_readers=[InMemoryMetricReader()]), | ||
| ) | ||
| logfire.instrument_pydantic(record='all', include=include or set(), exclude=exclude or set()) | ||
| plugin = LogfirePydanticPlugin() | ||
| return cast( | ||
| tuple[object | None, object | None, object | None], | ||
| plugin.new_schema_validator( | ||
| core_schema.int_schema(), None, SchemaTypePath(module=module, name=name), 'BaseModel', None, {} | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| def test_pydantic_plugin_excludes_non_user_modules_by_default() -> None: | ||
| purelib = sysconfig.get_path('purelib') | ||
| assert purelib is not None | ||
|
|
||
| def fake_find_spec(module: str) -> SimpleNamespace | None: | ||
| if module == 'third_party_pkg.models': | ||
| return SimpleNamespace( | ||
| origin=os.path.join(purelib, 'third_party_pkg', 'models.py'), | ||
| submodule_search_locations=[], | ||
| ) | ||
| if module == 'tests.test_pydantic_plugin': | ||
| return SimpleNamespace(origin=__file__, submodule_search_locations=[]) | ||
| return None | ||
|
|
||
| with patch('logfire.integrations.pydantic.importlib.util.find_spec', side_effect=fake_find_spec): | ||
| assert _new_pydantic_plugin_result('third_party_pkg.models', 'ThirdPartyModel') == (None, None, None) | ||
| assert _new_pydantic_plugin_result('tests.test_pydantic_plugin', 'MyModel') != (None, None, None) | ||
|
|
||
|
|
||
| def test_pydantic_plugin_include_overrides_default_non_user_filter() -> None: | ||
| purelib = sysconfig.get_path('purelib') | ||
| assert purelib is not None | ||
|
|
||
| def fake_find_spec(module: str) -> SimpleNamespace | None: | ||
| if module == 'third_party_pkg.models': | ||
| return SimpleNamespace( | ||
| origin=os.path.join(purelib, 'third_party_pkg', 'models.py'), | ||
| submodule_search_locations=[], | ||
| ) | ||
| return None | ||
|
|
||
| with patch('logfire.integrations.pydantic.importlib.util.find_spec', side_effect=fake_find_spec): | ||
| assert _new_pydantic_plugin_result('third_party_pkg.models', 'ThirdPartyModel') == (None, None, None) | ||
| assert _new_pydantic_plugin_result( | ||
| 'third_party_pkg.models', | ||
| 'ThirdPartyModel', | ||
| include={'third_party_pkg.models::ThirdPartyModel'}, | ||
| ) != (None, None, None) | ||
|
|
||
|
|
||
| def test_pydantic_plugin_handles_find_spec_failures_as_user_code() -> None: | ||
| def fake_find_spec(module: str) -> SimpleNamespace | None: | ||
| if module == 'broken_pkg.models': | ||
| raise RuntimeError('broken parent import') | ||
| if module == 'invalid_name': | ||
| raise ValueError('invalid module name') | ||
| if module == 'namespace_pkg.models': | ||
| return SimpleNamespace(origin=None, submodule_search_locations=[]) | ||
| return None | ||
|
|
||
| with patch('logfire.integrations.pydantic.importlib.util.find_spec', side_effect=fake_find_spec): | ||
| assert _new_pydantic_plugin_result('broken_pkg.models', 'BrokenModel') != (None, None, None) | ||
| assert _new_pydantic_plugin_result('invalid_name', 'InvalidModel') != (None, None, None) | ||
| assert _new_pydantic_plugin_result('namespace_pkg.models', 'NamespaceModel') != (None, None, None) | ||
|
|
||
|
|
||
| def test_pydantic_plugin_excludes_builtin_modules_by_default() -> None: | ||
| def fake_find_spec(module: str) -> SimpleNamespace | None: | ||
| if module == 'builtins': | ||
| return SimpleNamespace(origin='built-in', submodule_search_locations=None) | ||
| return None | ||
|
|
||
| with patch('logfire.integrations.pydantic.importlib.util.find_spec', side_effect=fake_find_spec): | ||
| assert _new_pydantic_plugin_result('builtins', 'int') == (None, None, None) | ||
|
|
||
|
|
||
| def test_get_schema_name(): | ||
| # In particular this tests schemas with type 'definitions' | ||
|
|
||
|
|
@@ -236,7 +327,7 @@ class MyModel(BaseModel, plugin_settings={'logfire': {'record': 'failure'}}): | |
| 'exemplars': [], | ||
| }, | ||
| ], | ||
| 'aggregation_temporality': AggregationTemporality.DELTA, | ||
| 'aggregation_temporality': 1, | ||
| 'is_monotonic': True, | ||
| }, | ||
| } | ||
|
|
@@ -290,7 +381,7 @@ class MyModel(BaseModel, plugin_settings={'logfire': {'record': 'metrics'}}): | |
| 'exemplars': [], | ||
| }, | ||
| ], | ||
| 'aggregation_temporality': AggregationTemporality.DELTA, | ||
| 'aggregation_temporality': 1, | ||
| 'is_monotonic': True, | ||
| }, | ||
| } | ||
|
|
@@ -352,7 +443,7 @@ class MyModel(BaseModel, plugin_settings={'logfire': {'record': 'all'}}): | |
| 'exemplars': [], | ||
| } | ||
| ], | ||
| 'aggregation_temporality': AggregationTemporality.DELTA, | ||
| 'aggregation_temporality': 1, | ||
| 'is_monotonic': True, | ||
| }, | ||
| } | ||
|
|
@@ -438,7 +529,7 @@ class MyModel(BaseModel, plugin_settings={'logfire': {'record': 'failure'}}): | |
| 'exemplars': [], | ||
| } | ||
| ], | ||
| 'aggregation_temporality': AggregationTemporality.DELTA, | ||
| 'aggregation_temporality': 1, | ||
| 'is_monotonic': True, | ||
| }, | ||
| } | ||
|
|
@@ -1292,9 +1383,7 @@ class Hero(sqlmodel.SQLModel, table=True): | |
| 'logfire.level_num': 9, | ||
| 'logfire.span_type': 'span', | ||
| 'success': True, | ||
| 'result': '{"id":1}' | ||
| if get_version(pydantic.__version__) >= get_version('2.7.0') | ||
| else '"Hero(id=1)"', | ||
| 'result': '{"id":1}', | ||
| 'logfire.msg': 'Pydantic Hero validate_python succeeded', | ||
| 'logfire.json_schema': '{"type":"object","properties":{"schema_name":{},"validation_method":{},"input_data":{"type":"object"},"success":{},"result":{"type":"object","title":"Hero","x-python-datatype":"PydanticModel"}}}', | ||
| }, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.