Skip to content
Open
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
26 changes: 24 additions & 2 deletions py/src/braintrust/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,17 @@ def is_equal(expected, output):

# Check env var at import time for auto-instrumentation
import os
from typing import TYPE_CHECKING


if TYPE_CHECKING:
# These names must precede the generated wildcard so type checkers keep the
# runtime resource classes for the four names shared by both modules.
from .logger import Dataset as Dataset # noqa: I001
from .logger import Experiment as Experiment
from .logger import Project as Project
from .logger import Prompt as Prompt
from .generated_types import *


if os.getenv("BRAINTRUST_INSTRUMENT_THREADS", "").lower() in ("true", "1", "yes"):
Expand All@@ -60,15 +71,22 @@ def is_equal(expected, output):
except Exception:
pass # Never break on import

from . import generated_types as _generated_types # noqa: I001
from .audit import *
from .auto import auto_instrument as auto_instrument
from .dataset_pipeline import *
from .framework import *
from .framework2 import *
from .functions.invoke import *
from .functions.stream import *
from .generated_types import *
from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk

# Keep this before the logger wildcard so its existing runtime collision
# precedence remains unchanged while new generated names are picked up.
for _name in _generated_types.__all__:
if _name not in {"Dataset", "Experiment", "Project", "Prompt"}:
globals()[_name] = getattr(_generated_types, _name)

from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk # noqa: I001
from .integrations.anthropic import wrap_anthropic as wrap_anthropic
from .integrations.instructor import wrap_instructor as wrap_instructor
from .integrations.litellm import wrap_litellm as wrap_litellm
Expand All@@ -77,6 +95,10 @@ def is_equal(expected, output):
from .integrations.pydantic_ai import setup_pydantic_ai as setup_pydantic_ai
from .logger import *
from .logger import (
Dataset as Dataset,
Experiment as Experiment,
Project as Project,
Prompt as Prompt,
_internal_get_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_reset_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_with_custom_background_logger, # noqa: F401 # type: ignore[reportUnusedImport]
Expand Down
50 changes: 44 additions & 6 deletions py/src/braintrust/type_tests/test_public_exports.py
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
"""Regression test for pyright's ``reportPrivateImportUsage`` on top-level ``braintrust`` symbols.
"""Regression tests for top-level ``braintrust`` symbols.

Without PEP 484 ``as``-aliasing in ``braintrust/__init__.py``, pyright flags
``from braintrust import auto_instrument`` (and peers) as private in a
``py.typed`` consumer. The local ``pyrightconfig.json`` turns the rule into
an error so this file breaks ``nox -s test_types`` if someone regresses the
aliasing pattern.
The static resource check keeps mypy from resolving generated ``TypedDict``
names instead of the public runtime classes. The runtime checks cover PEP 484
aliasing for pyright's ``reportPrivateImportUsage`` rule and keep generated
exports synchronized with the package root.
"""

import subprocess
import sys

import braintrust
import pytest
from braintrust import (
Acl,
auto_instrument,
setup_ai_sdk,
setup_pydantic_ai,
Expand All@@ -31,7 +34,42 @@
]


def accepts_public_resource_types(
experiment: braintrust.Experiment,
dataset: braintrust.Dataset,
project: braintrust.Project,
prompt: braintrust.Prompt,
acl: Acl,
) -> None:
experiment.fetch()
dataset.fetch()
_ = project.name
prompt.build()
_ = acl["id"]


@pytest.mark.parametrize("name,imported", _PUBLIC_SYMBOLS)
def test_top_level_public_symbol(name: str, imported: object) -> None:
assert callable(imported)
assert callable(getattr(braintrust, name))


def test_generated_exports_follow_generated_all() -> None:
script = """
import importlib

import braintrust
from braintrust import generated_types

future_type = type("FutureGeneratedType", (), {})
generated_types.FutureGeneratedType = future_type
generated_types.__all__.append("FutureGeneratedType")
try:
importlib.reload(braintrust)
assert braintrust.FutureGeneratedType is future_type
finally:
generated_types.__all__.remove("FutureGeneratedType")
del generated_types.FutureGeneratedType
"""

subprocess.run([sys.executable, "-c", script], check=True)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix: resolve top-level resource classes in mypy by mameikagou · Pull Request #710 · braintrustdata/braintrust-sdk-python · GitHub
Skip to content
Open
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
26 changes: 24 additions & 2 deletions py/src/braintrust/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,17 @@ def is_equal(expected, output):

# Check env var at import time for auto-instrumentation
import os
from typing import TYPE_CHECKING


if TYPE_CHECKING:
# These names must precede the generated wildcard so type checkers keep the
# runtime resource classes for the four names shared by both modules.
from .logger import Dataset as Dataset # noqa: I001
from .logger import Experiment as Experiment
from .logger import Project as Project
from .logger import Prompt as Prompt
from .generated_types import *


if os.getenv("BRAINTRUST_INSTRUMENT_THREADS", "").lower() in ("true", "1", "yes"):
Expand All@@ -60,15 +71,22 @@ def is_equal(expected, output):
except Exception:
pass # Never break on import

from . import generated_types as _generated_types # noqa: I001
from .audit import *
from .auto import auto_instrument as auto_instrument
from .dataset_pipeline import *
from .framework import *
from .framework2 import *
from .functions.invoke import *
from .functions.stream import *
from .generated_types import *
from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk

# Keep this before the logger wildcard so its existing runtime collision
# precedence remains unchanged while new generated names are picked up.
for _name in _generated_types.__all__:
if _name not in {"Dataset", "Experiment", "Project", "Prompt"}:
globals()[_name] = getattr(_generated_types, _name)

from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk # noqa: I001
from .integrations.anthropic import wrap_anthropic as wrap_anthropic
from .integrations.instructor import wrap_instructor as wrap_instructor
from .integrations.litellm import wrap_litellm as wrap_litellm
Expand All@@ -77,6 +95,10 @@ def is_equal(expected, output):
from .integrations.pydantic_ai import setup_pydantic_ai as setup_pydantic_ai
from .logger import *
from .logger import (
Dataset as Dataset,
Experiment as Experiment,
Project as Project,
Prompt as Prompt,
_internal_get_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_reset_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_with_custom_background_logger, # noqa: F401 # type: ignore[reportUnusedImport]
Expand Down
50 changes: 44 additions & 6 deletions py/src/braintrust/type_tests/test_public_exports.py
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
"""Regression test for pyright's ``reportPrivateImportUsage`` on top-level ``braintrust`` symbols.
"""Regression tests for top-level ``braintrust`` symbols.

Without PEP 484 ``as``-aliasing in ``braintrust/__init__.py``, pyright flags
``from braintrust import auto_instrument`` (and peers) as private in a
``py.typed`` consumer. The local ``pyrightconfig.json`` turns the rule into
an error so this file breaks ``nox -s test_types`` if someone regresses the
aliasing pattern.
The static resource check keeps mypy from resolving generated ``TypedDict``
names instead of the public runtime classes. The runtime checks cover PEP 484
aliasing for pyright's ``reportPrivateImportUsage`` rule and keep generated
exports synchronized with the package root.
"""

import subprocess
import sys

import braintrust
import pytest
from braintrust import (
Acl,
auto_instrument,
setup_ai_sdk,
setup_pydantic_ai,
Expand All@@ -31,7 +34,42 @@
]


def accepts_public_resource_types(
experiment: braintrust.Experiment,
dataset: braintrust.Dataset,
project: braintrust.Project,
prompt: braintrust.Prompt,
acl: Acl,
) -> None:
experiment.fetch()
dataset.fetch()
_ = project.name
prompt.build()
_ = acl["id"]


@pytest.mark.parametrize("name,imported", _PUBLIC_SYMBOLS)
def test_top_level_public_symbol(name: str, imported: object) -> None:
assert callable(imported)
assert callable(getattr(braintrust, name))


def test_generated_exports_follow_generated_all() -> None:
script = """
import importlib

import braintrust
from braintrust import generated_types

future_type = type("FutureGeneratedType", (), {})
generated_types.FutureGeneratedType = future_type
generated_types.__all__.append("FutureGeneratedType")
try:
importlib.reload(braintrust)
assert braintrust.FutureGeneratedType is future_type
finally:
generated_types.__all__.remove("FutureGeneratedType")
del generated_types.FutureGeneratedType
"""

subprocess.run([sys.executable, "-c", script], check=True)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: resolve top-level resource classes in mypy by mameikagou · Pull Request #710 · braintrustdata/braintrust-sdk-python · GitHub
Skip to content
Open
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
26 changes: 24 additions & 2 deletions py/src/braintrust/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,17 @@ def is_equal(expected, output):

# Check env var at import time for auto-instrumentation
import os
from typing import TYPE_CHECKING


if TYPE_CHECKING:
# These names must precede the generated wildcard so type checkers keep the
# runtime resource classes for the four names shared by both modules.
from .logger import Dataset as Dataset # noqa: I001
from .logger import Experiment as Experiment
from .logger import Project as Project
from .logger import Prompt as Prompt
from .generated_types import *


if os.getenv("BRAINTRUST_INSTRUMENT_THREADS", "").lower() in ("true", "1", "yes"):
Expand All@@ -60,15 +71,22 @@ def is_equal(expected, output):
except Exception:
pass # Never break on import

from . import generated_types as _generated_types # noqa: I001
from .audit import *
from .auto import auto_instrument as auto_instrument
from .dataset_pipeline import *
from .framework import *
from .framework2 import *
from .functions.invoke import *
from .functions.stream import *
from .generated_types import *
from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk

# Keep this before the logger wildcard so its existing runtime collision
# precedence remains unchanged while new generated names are picked up.
for _name in _generated_types.__all__:
if _name not in {"Dataset", "Experiment", "Project", "Prompt"}:
globals()[_name] = getattr(_generated_types, _name)

from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk # noqa: I001
from .integrations.anthropic import wrap_anthropic as wrap_anthropic
from .integrations.instructor import wrap_instructor as wrap_instructor
from .integrations.litellm import wrap_litellm as wrap_litellm
Expand All@@ -77,6 +95,10 @@ def is_equal(expected, output):
from .integrations.pydantic_ai import setup_pydantic_ai as setup_pydantic_ai
from .logger import *
from .logger import (
Dataset as Dataset,
Experiment as Experiment,
Project as Project,
Prompt as Prompt,
_internal_get_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_reset_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_with_custom_background_logger, # noqa: F401 # type: ignore[reportUnusedImport]
Expand Down
50 changes: 44 additions & 6 deletions py/src/braintrust/type_tests/test_public_exports.py
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
"""Regression test for pyright's ``reportPrivateImportUsage`` on top-level ``braintrust`` symbols.
"""Regression tests for top-level ``braintrust`` symbols.

Without PEP 484 ``as``-aliasing in ``braintrust/__init__.py``, pyright flags
``from braintrust import auto_instrument`` (and peers) as private in a
``py.typed`` consumer. The local ``pyrightconfig.json`` turns the rule into
an error so this file breaks ``nox -s test_types`` if someone regresses the
aliasing pattern.
The static resource check keeps mypy from resolving generated ``TypedDict``
names instead of the public runtime classes. The runtime checks cover PEP 484
aliasing for pyright's ``reportPrivateImportUsage`` rule and keep generated
exports synchronized with the package root.
"""

import subprocess
import sys

import braintrust
import pytest
from braintrust import (
Acl,
auto_instrument,
setup_ai_sdk,
setup_pydantic_ai,
Expand All@@ -31,7 +34,42 @@
]


def accepts_public_resource_types(
experiment: braintrust.Experiment,
dataset: braintrust.Dataset,
project: braintrust.Project,
prompt: braintrust.Prompt,
acl: Acl,
) -> None:
experiment.fetch()
dataset.fetch()
_ = project.name
prompt.build()
_ = acl["id"]


@pytest.mark.parametrize("name,imported", _PUBLIC_SYMBOLS)
def test_top_level_public_symbol(name: str, imported: object) -> None:
assert callable(imported)
assert callable(getattr(braintrust, name))


def test_generated_exports_follow_generated_all() -> None:
script = """
import importlib

import braintrust
from braintrust import generated_types

future_type = type("FutureGeneratedType", (), {})
generated_types.FutureGeneratedType = future_type
generated_types.__all__.append("FutureGeneratedType")
try:
importlib.reload(braintrust)
assert braintrust.FutureGeneratedType is future_type
finally:
generated_types.__all__.remove("FutureGeneratedType")
del generated_types.FutureGeneratedType
"""

subprocess.run([sys.executable, "-c", script], check=True)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: resolve top-level resource classes in mypy by mameikagou · Pull Request #710 · braintrustdata/braintrust-sdk-python · GitHub
Skip to content
Open
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
26 changes: 24 additions & 2 deletions py/src/braintrust/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,17 @@ def is_equal(expected, output):

# Check env var at import time for auto-instrumentation
import os
from typing import TYPE_CHECKING


if TYPE_CHECKING:
# These names must precede the generated wildcard so type checkers keep the
# runtime resource classes for the four names shared by both modules.
from .logger import Dataset as Dataset # noqa: I001
from .logger import Experiment as Experiment
from .logger import Project as Project
from .logger import Prompt as Prompt
from .generated_types import *


if os.getenv("BRAINTRUST_INSTRUMENT_THREADS", "").lower() in ("true", "1", "yes"):
Expand All@@ -60,15 +71,22 @@ def is_equal(expected, output):
except Exception:
pass # Never break on import

from . import generated_types as _generated_types # noqa: I001
from .audit import *
from .auto import auto_instrument as auto_instrument
from .dataset_pipeline import *
from .framework import *
from .framework2 import *
from .functions.invoke import *
from .functions.stream import *
from .generated_types import *
from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk

# Keep this before the logger wildcard so its existing runtime collision
# precedence remains unchanged while new generated names are picked up.
for _name in _generated_types.__all__:
if _name not in {"Dataset", "Experiment", "Project", "Prompt"}:
globals()[_name] = getattr(_generated_types, _name)

from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk # noqa: I001
from .integrations.anthropic import wrap_anthropic as wrap_anthropic
from .integrations.instructor import wrap_instructor as wrap_instructor
from .integrations.litellm import wrap_litellm as wrap_litellm
Expand All@@ -77,6 +95,10 @@ def is_equal(expected, output):
from .integrations.pydantic_ai import setup_pydantic_ai as setup_pydantic_ai
from .logger import *
from .logger import (
Dataset as Dataset,
Experiment as Experiment,
Project as Project,
Prompt as Prompt,
_internal_get_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_reset_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_with_custom_background_logger, # noqa: F401 # type: ignore[reportUnusedImport]
Expand Down
50 changes: 44 additions & 6 deletions py/src/braintrust/type_tests/test_public_exports.py
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
"""Regression test for pyright's ``reportPrivateImportUsage`` on top-level ``braintrust`` symbols.
"""Regression tests for top-level ``braintrust`` symbols.

Without PEP 484 ``as``-aliasing in ``braintrust/__init__.py``, pyright flags
``from braintrust import auto_instrument`` (and peers) as private in a
``py.typed`` consumer. The local ``pyrightconfig.json`` turns the rule into
an error so this file breaks ``nox -s test_types`` if someone regresses the
aliasing pattern.
The static resource check keeps mypy from resolving generated ``TypedDict``
names instead of the public runtime classes. The runtime checks cover PEP 484
aliasing for pyright's ``reportPrivateImportUsage`` rule and keep generated
exports synchronized with the package root.
"""

import subprocess
import sys

import braintrust
import pytest
from braintrust import (
Acl,
auto_instrument,
setup_ai_sdk,
setup_pydantic_ai,
Expand All@@ -31,7 +34,42 @@
]


def accepts_public_resource_types(
experiment: braintrust.Experiment,
dataset: braintrust.Dataset,
project: braintrust.Project,
prompt: braintrust.Prompt,
acl: Acl,
) -> None:
experiment.fetch()
dataset.fetch()
_ = project.name
prompt.build()
_ = acl["id"]


@pytest.mark.parametrize("name,imported", _PUBLIC_SYMBOLS)
def test_top_level_public_symbol(name: str, imported: object) -> None:
assert callable(imported)
assert callable(getattr(braintrust, name))


def test_generated_exports_follow_generated_all() -> None:
script = """
import importlib

import braintrust
from braintrust import generated_types

future_type = type("FutureGeneratedType", (), {})
generated_types.FutureGeneratedType = future_type
generated_types.__all__.append("FutureGeneratedType")
try:
importlib.reload(braintrust)
assert braintrust.FutureGeneratedType is future_type
finally:
generated_types.__all__.remove("FutureGeneratedType")
del generated_types.FutureGeneratedType
"""

subprocess.run([sys.executable, "-c", script], check=True)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix: resolve top-level resource classes in mypy by mameikagou · Pull Request #710 · braintrustdata/braintrust-sdk-python · GitHub
Skip to content
Open
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
26 changes: 24 additions & 2 deletions py/src/braintrust/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,17 @@ def is_equal(expected, output):

# Check env var at import time for auto-instrumentation
import os
from typing import TYPE_CHECKING


if TYPE_CHECKING:
# These names must precede the generated wildcard so type checkers keep the
# runtime resource classes for the four names shared by both modules.
from .logger import Dataset as Dataset # noqa: I001
from .logger import Experiment as Experiment
from .logger import Project as Project
from .logger import Prompt as Prompt
from .generated_types import *


if os.getenv("BRAINTRUST_INSTRUMENT_THREADS", "").lower() in ("true", "1", "yes"):
Expand All@@ -60,15 +71,22 @@ def is_equal(expected, output):
except Exception:
pass # Never break on import

from . import generated_types as _generated_types # noqa: I001
from .audit import *
from .auto import auto_instrument as auto_instrument
from .dataset_pipeline import *
from .framework import *
from .framework2 import *
from .functions.invoke import *
from .functions.stream import *
from .generated_types import *
from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk

# Keep this before the logger wildcard so its existing runtime collision
# precedence remains unchanged while new generated names are picked up.
for _name in _generated_types.__all__:
if _name not in {"Dataset", "Experiment", "Project", "Prompt"}:
globals()[_name] = getattr(_generated_types, _name)

from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk # noqa: I001
from .integrations.anthropic import wrap_anthropic as wrap_anthropic
from .integrations.instructor import wrap_instructor as wrap_instructor
from .integrations.litellm import wrap_litellm as wrap_litellm
Expand All@@ -77,6 +95,10 @@ def is_equal(expected, output):
from .integrations.pydantic_ai import setup_pydantic_ai as setup_pydantic_ai
from .logger import *
from .logger import (
Dataset as Dataset,
Experiment as Experiment,
Project as Project,
Prompt as Prompt,
_internal_get_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_reset_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_with_custom_background_logger, # noqa: F401 # type: ignore[reportUnusedImport]
Expand Down
50 changes: 44 additions & 6 deletions py/src/braintrust/type_tests/test_public_exports.py
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
"""Regression test for pyright's ``reportPrivateImportUsage`` on top-level ``braintrust`` symbols.
"""Regression tests for top-level ``braintrust`` symbols.

Without PEP 484 ``as``-aliasing in ``braintrust/__init__.py``, pyright flags
``from braintrust import auto_instrument`` (and peers) as private in a
``py.typed`` consumer. The local ``pyrightconfig.json`` turns the rule into
an error so this file breaks ``nox -s test_types`` if someone regresses the
aliasing pattern.
The static resource check keeps mypy from resolving generated ``TypedDict``
names instead of the public runtime classes. The runtime checks cover PEP 484
aliasing for pyright's ``reportPrivateImportUsage`` rule and keep generated
exports synchronized with the package root.
"""

import subprocess
import sys

import braintrust
import pytest
from braintrust import (
Acl,
auto_instrument,
setup_ai_sdk,
setup_pydantic_ai,
Expand All@@ -31,7 +34,42 @@
]


def accepts_public_resource_types(
experiment: braintrust.Experiment,
dataset: braintrust.Dataset,
project: braintrust.Project,
prompt: braintrust.Prompt,
acl: Acl,
) -> None:
experiment.fetch()
dataset.fetch()
_ = project.name
prompt.build()
_ = acl["id"]


@pytest.mark.parametrize("name,imported", _PUBLIC_SYMBOLS)
def test_top_level_public_symbol(name: str, imported: object) -> None:
assert callable(imported)
assert callable(getattr(braintrust, name))


def test_generated_exports_follow_generated_all() -> None:
script = """
import importlib

import braintrust
from braintrust import generated_types

future_type = type("FutureGeneratedType", (), {})
generated_types.FutureGeneratedType = future_type
generated_types.__all__.append("FutureGeneratedType")
try:
importlib.reload(braintrust)
assert braintrust.FutureGeneratedType is future_type
finally:
generated_types.__all__.remove("FutureGeneratedType")
del generated_types.FutureGeneratedType
"""

subprocess.run([sys.executable, "-c", script], check=True)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: resolve top-level resource classes in mypy by mameikagou · Pull Request #710 · braintrustdata/braintrust-sdk-python · GitHub
Skip to content
Open
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
26 changes: 24 additions & 2 deletions py/src/braintrust/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,17 @@ def is_equal(expected, output):

# Check env var at import time for auto-instrumentation
import os
from typing import TYPE_CHECKING


if TYPE_CHECKING:
# These names must precede the generated wildcard so type checkers keep the
# runtime resource classes for the four names shared by both modules.
from .logger import Dataset as Dataset # noqa: I001
from .logger import Experiment as Experiment
from .logger import Project as Project
from .logger import Prompt as Prompt
from .generated_types import *


if os.getenv("BRAINTRUST_INSTRUMENT_THREADS", "").lower() in ("true", "1", "yes"):
Expand All@@ -60,15 +71,22 @@ def is_equal(expected, output):
except Exception:
pass # Never break on import

from . import generated_types as _generated_types # noqa: I001
from .audit import *
from .auto import auto_instrument as auto_instrument
from .dataset_pipeline import *
from .framework import *
from .framework2 import *
from .functions.invoke import *
from .functions.stream import *
from .generated_types import *
from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk

# Keep this before the logger wildcard so its existing runtime collision
# precedence remains unchanged while new generated names are picked up.
for _name in _generated_types.__all__:
if _name not in {"Dataset", "Experiment", "Project", "Prompt"}:
globals()[_name] = getattr(_generated_types, _name)

from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk # noqa: I001
from .integrations.anthropic import wrap_anthropic as wrap_anthropic
from .integrations.instructor import wrap_instructor as wrap_instructor
from .integrations.litellm import wrap_litellm as wrap_litellm
Expand All@@ -77,6 +95,10 @@ def is_equal(expected, output):
from .integrations.pydantic_ai import setup_pydantic_ai as setup_pydantic_ai
from .logger import *
from .logger import (
Dataset as Dataset,
Experiment as Experiment,
Project as Project,
Prompt as Prompt,
_internal_get_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_reset_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_with_custom_background_logger, # noqa: F401 # type: ignore[reportUnusedImport]
Expand Down
50 changes: 44 additions & 6 deletions py/src/braintrust/type_tests/test_public_exports.py
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
"""Regression test for pyright's ``reportPrivateImportUsage`` on top-level ``braintrust`` symbols.
"""Regression tests for top-level ``braintrust`` symbols.

Without PEP 484 ``as``-aliasing in ``braintrust/__init__.py``, pyright flags
``from braintrust import auto_instrument`` (and peers) as private in a
``py.typed`` consumer. The local ``pyrightconfig.json`` turns the rule into
an error so this file breaks ``nox -s test_types`` if someone regresses the
aliasing pattern.
The static resource check keeps mypy from resolving generated ``TypedDict``
names instead of the public runtime classes. The runtime checks cover PEP 484
aliasing for pyright's ``reportPrivateImportUsage`` rule and keep generated
exports synchronized with the package root.
"""

import subprocess
import sys

import braintrust
import pytest
from braintrust import (
Acl,
auto_instrument,
setup_ai_sdk,
setup_pydantic_ai,
Expand All@@ -31,7 +34,42 @@
]


def accepts_public_resource_types(
experiment: braintrust.Experiment,
dataset: braintrust.Dataset,
project: braintrust.Project,
prompt: braintrust.Prompt,
acl: Acl,
) -> None:
experiment.fetch()
dataset.fetch()
_ = project.name
prompt.build()
_ = acl["id"]


@pytest.mark.parametrize("name,imported", _PUBLIC_SYMBOLS)
def test_top_level_public_symbol(name: str, imported: object) -> None:
assert callable(imported)
assert callable(getattr(braintrust, name))


def test_generated_exports_follow_generated_all() -> None:
script = """
import importlib

import braintrust
from braintrust import generated_types

future_type = type("FutureGeneratedType", (), {})
generated_types.FutureGeneratedType = future_type
generated_types.__all__.append("FutureGeneratedType")
try:
importlib.reload(braintrust)
assert braintrust.FutureGeneratedType is future_type
finally:
generated_types.__all__.remove("FutureGeneratedType")
del generated_types.FutureGeneratedType
"""

subprocess.run([sys.executable, "-c", script], check=True)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); fix: resolve top-level resource classes in mypy by mameikagou · Pull Request #710 · braintrustdata/braintrust-sdk-python · GitHub
Skip to content
Open
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
26 changes: 24 additions & 2 deletions py/src/braintrust/__init__.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,17 @@ def is_equal(expected, output):

# Check env var at import time for auto-instrumentation
import os
from typing import TYPE_CHECKING


if TYPE_CHECKING:
# These names must precede the generated wildcard so type checkers keep the
# runtime resource classes for the four names shared by both modules.
from .logger import Dataset as Dataset # noqa: I001
from .logger import Experiment as Experiment
from .logger import Project as Project
from .logger import Prompt as Prompt
from .generated_types import *


if os.getenv("BRAINTRUST_INSTRUMENT_THREADS", "").lower() in ("true", "1", "yes"):
Expand All@@ -60,15 +71,22 @@ def is_equal(expected, output):
except Exception:
pass # Never break on import

from . import generated_types as _generated_types # noqa: I001
from .audit import *
from .auto import auto_instrument as auto_instrument
from .dataset_pipeline import *
from .framework import *
from .framework2 import *
from .functions.invoke import *
from .functions.stream import *
from .generated_types import *
from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk

# Keep this before the logger wildcard so its existing runtime collision
# precedence remains unchanged while new generated names are picked up.
for _name in _generated_types.__all__:
if _name not in {"Dataset", "Experiment", "Project", "Prompt"}:
globals()[_name] = getattr(_generated_types, _name)

from .integrations.ai_sdk import setup_ai_sdk as setup_ai_sdk # noqa: I001
from .integrations.anthropic import wrap_anthropic as wrap_anthropic
from .integrations.instructor import wrap_instructor as wrap_instructor
from .integrations.litellm import wrap_litellm as wrap_litellm
Expand All@@ -77,6 +95,10 @@ def is_equal(expected, output):
from .integrations.pydantic_ai import setup_pydantic_ai as setup_pydantic_ai
from .logger import *
from .logger import (
Dataset as Dataset,
Experiment as Experiment,
Project as Project,
Prompt as Prompt,
_internal_get_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_reset_global_state, # noqa: F401 # type: ignore[reportUnusedImport]
_internal_with_custom_background_logger, # noqa: F401 # type: ignore[reportUnusedImport]
Expand Down
50 changes: 44 additions & 6 deletions py/src/braintrust/type_tests/test_public_exports.py
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
"""Regression test for pyright's ``reportPrivateImportUsage`` on top-level ``braintrust`` symbols.
"""Regression tests for top-level ``braintrust`` symbols.

Without PEP 484 ``as``-aliasing in ``braintrust/__init__.py``, pyright flags
``from braintrust import auto_instrument`` (and peers) as private in a
``py.typed`` consumer. The local ``pyrightconfig.json`` turns the rule into
an error so this file breaks ``nox -s test_types`` if someone regresses the
aliasing pattern.
The static resource check keeps mypy from resolving generated ``TypedDict``
names instead of the public runtime classes. The runtime checks cover PEP 484
aliasing for pyright's ``reportPrivateImportUsage`` rule and keep generated
exports synchronized with the package root.
"""

import subprocess
import sys

import braintrust
import pytest
from braintrust import (
Acl,
auto_instrument,
setup_ai_sdk,
setup_pydantic_ai,
Expand All@@ -31,7 +34,42 @@
]


def accepts_public_resource_types(
experiment: braintrust.Experiment,
dataset: braintrust.Dataset,
project: braintrust.Project,
prompt: braintrust.Prompt,
acl: Acl,
) -> None:
experiment.fetch()
dataset.fetch()
_ = project.name
prompt.build()
_ = acl["id"]


@pytest.mark.parametrize("name,imported", _PUBLIC_SYMBOLS)
def test_top_level_public_symbol(name: str, imported: object) -> None:
assert callable(imported)
assert callable(getattr(braintrust, name))


def test_generated_exports_follow_generated_all() -> None:
script = """
import importlib

import braintrust
from braintrust import generated_types

future_type = type("FutureGeneratedType", (), {})
generated_types.FutureGeneratedType = future_type
generated_types.__all__.append("FutureGeneratedType")
try:
importlib.reload(braintrust)
assert braintrust.FutureGeneratedType is future_type
finally:
generated_types.__all__.remove("FutureGeneratedType")
del generated_types.FutureGeneratedType
"""

subprocess.run([sys.executable, "-c", script], check=True)