diff --git a/.gitignore b/.gitignore index f4fa4c3de..ee1ac48a8 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,7 @@ htmlcov/ # testing: parts of old trulens_eval package for backwards compatibility testing _trulens_eval/ data/ + +# Build objects +**/poetry.lock +!poetry.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6f27a625b..e6001e069 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,11 @@ repos: hooks: - id: trailing-whitespace - id: check-added-large-files - exclude: src/dashboard/trulens/dashboard/.*/dist/.* + exclude: | + (?x)^( + src/dashboard/trulens/dashboard/.*/dist/.*| + poetry.lock + )$ - id: check-yaml exclude: meta.yaml - id: destroyed-symlinks @@ -44,42 +48,3 @@ repos: hooks: - id: poetry-check name: trulens-poetry-check - - id: poetry-check - name: trulens-core-poetry-check - args: [-C src/core] - - id: poetry-check - name: trulens-benchmark-poetry-check - args: [-C src/benchmark] - - id: poetry-check - name: trulens-feedback-poetry-check - args: [-C src/feedback] - - id: poetry-check - name: trulens-apps-langchain-poetry-check - args: [-C src/apps/langchain] - - id: poetry-check - name: trulens-apps-llamaindex-poetry-check - args: [-C src/apps/llamaindex] - - id: poetry-check - name: trulens-apps-nemo-poetry-check - args: [-C src/apps/nemo] - - id: poetry-check - name: trulens-providers-bedrock-poetry-check - args: [-C src/providers/bedrock] - - id: poetry-check - name: trulens-providers-cortex-poetry-check - args: [-C src/providers/cortex] - - id: poetry-check - name: trulens-providers-huggingface-poetry-check - args: [-C src/providers/huggingface] - - id: poetry-check - name: trulens-providers-langchain-poetry-check - args: [-C src/providers/langchain] - - id: poetry-check - name: trulens-providers-litellm-poetry-check - args: [-C src/providers/litellm] - - id: poetry-check - name: trulens-providers-openai-poetry-check - args: [-C src/providers/openai] - - id: poetry-check - name: trulens-connectors-snowflake-poetry-check - args: [-C src/connectors/snowflake] diff --git a/Makefile b/Makefile index 515b5a46f..25f4ebcf3 100644 --- a/Makefile +++ b/Makefile @@ -39,8 +39,8 @@ env-tests: jsondiff env-tests-required: - poetry install --only required - make env-tests + poetry install --only required \ + && make env-tests env-tests-optional: env env-tests poetry run pip install \ diff --git a/examples/expositional/models/snowflake_cortex/cortex_finetuning_experiments.ipynb b/examples/expositional/models/snowflake_cortex/cortex_finetuning_experiments.ipynb index 37e3c03fa..b52ddebf7 100644 --- a/examples/expositional/models/snowflake_cortex/cortex_finetuning_experiments.ipynb +++ b/examples/expositional/models/snowflake_cortex/cortex_finetuning_experiments.ipynb @@ -6,7 +6,7 @@ "source": [ "# Cortex Finetuning Experiments\n", "\n", - "This notebook takes you through evaluating a series of " + "This notebook takes you through evaluating a series of fine-tuning experiments with Snowflake Cortex, and uses TruLens to evaluate the fine-tuning effectiveness." ] }, { diff --git a/pyproject.toml b/pyproject.toml index 48bdad15b..2c2730ebb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,10 @@ classifiers = [ "License :: OSI Approved :: MIT License", ] +[[tool.poetry.source]] +name = "pypi-public" +url = "https://pypi.org/simple/" + [tool.poetry.dependencies] python = "^3.8.1,!=3.9.7" trulens-core = { version = "^1.0.0", extras = [ @@ -91,10 +95,6 @@ trulens-semconv = { path = "src/semconv", develop = true } # Remove after deprecation period. trulens_eval = { path = "src/trulens_eval", develop = true } -[[tool.poetry.source]] -name = "pypi-public" -url = "https://pypi.org/simple/" - [tool.ruff] line-length = 80 extend-exclude = [ diff --git a/src/connectors/snowflake/trulens/connectors/snowflake/connector.py b/src/connectors/snowflake/trulens/connectors/snowflake/connector.py index ea4334dae..00f34e529 100644 --- a/src/connectors/snowflake/trulens/connectors/snowflake/connector.py +++ b/src/connectors/snowflake/trulens/connectors/snowflake/connector.py @@ -155,6 +155,20 @@ def _validate_snowpark_session_with_connection_parameters( self.password_known = True return snowpark_session_connection_parameters + @staticmethod + def _validate_snowpark_session_paramstyle( + snowpark_session: Session, + ) -> None: + if snowpark_session.connection._paramstyle == "pyformat": + # If this is the case, sql executions with bindings will fail later + # on so we fail fast here. + raise ValueError( + "The Snowpark session must have paramstyle 'qmark'! To ensure" + " this, during `snowflake.connector.connect` pass in" + " `paramstyle='qmark'` or set" + " `snowflake.connector.paramstyle = 'qmark'` beforehand." + ) + def _init_with_snowpark_session( self, snowpark_session: Session, @@ -166,6 +180,7 @@ def _init_with_snowpark_session( database_check_revision: bool, connection_parameters: Dict[str, str], ): + self._validate_snowpark_session_paramstyle(snowpark_session) database_args = self._set_up_database_args( database_args, snowpark_session, diff --git a/src/core/trulens/experimental/otel_tracing/core/trace/__init__.py b/src/core/trulens/experimental/otel_tracing/core/trace/__init__.py index 500428eb1..33c5f25e3 100644 --- a/src/core/trulens/experimental/otel_tracing/core/trace/__init__.py +++ b/src/core/trulens/experimental/otel_tracing/core/trace/__init__.py @@ -1,7 +1,9 @@ """ -Modules in this folder depend on each other than makes it impossible to import -them linearly. Because of this, they need to be imported here and the delayed -typing information needs to be filled in afterwards with model_rebuild`. +Modules in this folder depend on each other which makes it impossible to import +them without circular import errors. Because of this, some imports need to be +put into `if TYPE_CHECKING` blocks and classes that depend on those imports need +to be "rebuilt" with `model_rebuild`. This only applies to `pydantic.BaseModel` +classes. """ from . import context as core_context @@ -10,12 +12,12 @@ from . import span as core_span from . import trace as core_trace -core_trace.Tracer.model_rebuild() -core_span.Span.model_rebuild() -core_otel.Span.model_rebuild() core_context.SpanContext.model_rebuild() core_context.TraceState.model_rebuild() -core_sem.TypedSpan.model_rebuild() -core_trace.TracerProvider.model_rebuild() +core_otel.Span.model_rebuild() core_otel.Tracer.model_rebuild() core_otel.TracerProvider.model_rebuild() +core_span.Span.model_rebuild() +core_sem.TypedSpan.model_rebuild() +core_trace.Tracer.model_rebuild() +core_trace.TracerProvider.model_rebuild() diff --git a/tests/e2e/test_snowflake_connection.py b/tests/e2e/test_snowflake_connection.py index a5d6fdca1..b437c4fb0 100644 --- a/tests/e2e/test_snowflake_connection.py +++ b/tests/e2e/test_snowflake_connection.py @@ -5,6 +5,9 @@ from unittest import main import uuid +import snowflake.connector +from snowflake.snowpark import Session +from trulens.connectors.snowflake import SnowflakeConnector from trulens.dashboard import run_dashboard from trulens.dashboard import stop_dashboard @@ -68,6 +71,37 @@ def test_run_leaderboard_without_password(self): except Exception: pass + @optional_test + def test_paramstyle_pyformat(self): + default_paramstyle = snowflake.connector.paramstyle + try: + # pyformat paramstyle should fail fast. + snowflake.connector.paramstyle = "pyformat" + schema_name = self.create_and_use_schema( + "test_paramstyle_pyformat", append_uuid=True + ) + snowflake_connection = snowflake.connector.connect( + **self._snowflake_connection_parameters, schema=schema_name + ) + snowpark_session = Session.builder.configs({ + "connection": snowflake_connection + }).create() + with self.assertRaisesRegex( + ValueError, "The Snowpark session must have paramstyle 'qmark'!" + ): + SnowflakeConnector(snowpark_session=snowpark_session) + # qmark paramstyle should be fine. + snowflake.connector.paramstyle = "qmark" + snowflake_connection = snowflake.connector.connect( + **self._snowflake_connection_parameters, schema=schema_name + ) + snowpark_session = Session.builder.configs({ + "connection": snowflake_connection + }).create() + SnowflakeConnector(snowpark_session=snowpark_session) + finally: + snowflake.connector.paramstyle = default_paramstyle + if __name__ == "__main__": main() diff --git a/tests/e2e/test_snowflake_notebooks.py b/tests/e2e/test_snowflake_notebooks.py index 015e823e8..a656f67ee 100644 --- a/tests/e2e/test_snowflake_notebooks.py +++ b/tests/e2e/test_snowflake_notebooks.py @@ -1,7 +1,6 @@ import tempfile from typing import Sequence from unittest import main -import uuid from trulens.connectors.snowflake.utils.server_side_evaluation_artifacts import ( _TRULENS_PACKAGES, @@ -18,8 +17,7 @@ class TestSnowflakeNotebooks(SnowflakeTestCase): def test_simple(self) -> None: - schema_name = f"test_simple_{str(uuid.uuid4()).replace('-', '_')}" - self.create_and_use_schema(schema_name) + self.create_and_use_schema("test_simple", append_uuid=True) self._upload_and_run_notebook("simple", _TRULENS_PACKAGES) def test_staged_packages(self) -> None: diff --git a/tests/util/snowflake_test_case.py b/tests/util/snowflake_test_case.py index 53a2f5f43..da1905185 100644 --- a/tests/util/snowflake_test_case.py +++ b/tests/util/snowflake_test_case.py @@ -117,10 +117,19 @@ def run_query( ) -> List[Row]: return self._snowpark_session.sql(q, bindings).collect() - def create_and_use_schema(self, schema_name: str) -> None: + def create_and_use_schema( + self, schema_name: str, append_uuid: bool = False + ) -> str: + schema_name = schema_name.upper() + if append_uuid: + schema_name = ( + f"{schema_name}__{str(uuid.uuid4()).replace('-', '_')}" + ) + self._schema = schema_name self.run_query("CREATE SCHEMA IDENTIFIER(?)", [schema_name]) self._snowflake_schemas_to_delete.add(schema_name) self._snowpark_session.use_schema(schema_name) + return schema_name if __name__ == "__main__":