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
59 changes: 59 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# EarningsCall Python SDK

## Project Context

- Open-source Python client library for the EarningsCall API
- Published to PyPI as `earningscall`
- Tests: `pytest tests/` (uses `responses` for HTTP mocking)
- Lint: `ruff check .` and `black --check --diff .`
- Coverage: `python -m coverage run -m pytest tests/ && python -m coverage combine && python -m coverage report --show-missing`
- CI checks coveralls — do not let coverage decrease

## Python Clean Code Rules

### Architecture

- Keep functions small (<40 lines). Avoid deep nesting (max 2 levels).
- Prefer composition over inheritance.

### Exception Handling

- Catch the most specific exception possible.
- Never catch `Exception` unless top-level handler.
- Never swallow exceptions (`except Exception: pass` is forbidden).
- Never use `raise e` — use bare `raise`.
- Let programmer errors (KeyError, AttributeError, etc.) fail fast — don't catch them.

### Data Handling

- Use `dict["key"]` for required fields — let KeyError bubble up if missing.
- Use `.get()` only for genuinely optional fields with sensible defaults.
- Do not use `getattr()` as a substitute for proper type checking or configuration. If a module attribute is typed, trust the type.
- Fail fast over defensive guessing.

### Imports

- All imports at module level. No lazy imports.
- Order: stdlib, third-party, local.

### Logging

- Never use `print()`. Use the project logger: `log = logging.getLogger(__file__)`.
- Use f-strings for log messages.
- No sensitive data in logs (credentials, API keys, PII).

### Testing

- Business logic must be unit-testable. Mock external services.
- Test error paths.
- Use `responses` library for HTTP mocking, not `unittest.mock.patch` on requests.

### Forbidden Patterns

- `except Exception: pass`
- Catching generic `Exception` without reason
- Hardcoded credentials or environment-specific resource names
- Mutable default arguments
- Silent fallback behavior
- `print()` instead of logger
- `raise e` instead of bare `raise`
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,38 @@ earningscall.retry_strategy = {
}
```

### Telemetry

The library collects per-request telemetry in memory so you can monitor latency and errors in your own
observability stack. Telemetry is enabled by default.

Each metric includes:

- request target (API path or URL)
- HTTP method
- status code
- duration in milliseconds
- retry attempt count
- cache hit information
- error type (when a request raises)

```python
import earningscall
from earningscall import get_latency_metrics_summary, pop_latency_metrics

# Telemetry is enabled by default. To disable:
# earningscall.enable_telemetry = False

# Optional tuning
earningscall.telemetry_max_entries = 1000 # Keep latest N request metrics in memory (default)

# ...run your EarningsCall API requests...

# Snapshot aggregated latency stats without clearing the buffer
summary = get_latency_metrics_summary()
print(summary)

# Drain raw metrics when it's time to ship a batch to your observability service
raw_metrics = pop_latency_metrics()
# requests.post("https://your-observability-endpoint.example/v1/metrics", json=raw_metrics)
```
12 changes: 12 additions & 0 deletions earningscall/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
from typing import Dict, Optional, Union

from earningscall.api import (
clear_latency_metrics,
get_latency_metrics,
get_latency_metrics_summary,
pop_latency_metrics,
)
from earningscall.exports import get_company, get_all_companies, get_sp500_companies, get_calendar
import earningscall.exchanges as exchanges
from earningscall.symbols import Symbols, load_symbols

api_key: Optional[str] = None
enable_requests_cache: bool = True
retry_strategy: Optional[Dict[str, Union[str, int, float]]] = None
enable_telemetry: bool = True
telemetry_max_entries: int = 1000

__all__ = [
"get_company",
Expand All @@ -15,5 +23,9 @@
"Symbols",
"load_symbols",
"get_calendar",
"get_latency_metrics",
"get_latency_metrics_summary",
"clear_latency_metrics",
"pop_latency_metrics",
"exchanges",
]
Loading