altissimo-mixpanel
Reusable, typed Mixpanel analytics client for Altissimo projects.
Features
- Typed API — full type annotations for all operations
- Lazy SDK import —
mixpanelis imported only when needed, zero import-time side effects - Disabled mode — no-op all calls when analytics is turned off or isn't configured
- Error handling — swallow-and-log by default, configurable via
on_error - Protocol for DI —
MixpanelClientProtocolfor dependency injection - Test helpers —
FakeMixpanelClientrecords all calls in-memory - Optional dependency — works without
mixpanelinstalled (fails gracefully)
Installation
Published on PyPI as altissimo-mixpanel;
the import name is altissimo.mixpanel.
# With the Mixpanel SDK (recommended)
pip install "altissimo-mixpanel[mixpanel]"
# Without the SDK (for type checking / protocol-only usage)
pip install altissimo-mixpanel
Quick Start
Using from_env()
from altissimo.mixpanel import MixpanelClient
# Reads MIXPANEL_TOKEN from environment
# Missing token → disabled mode, with a warning (see Disabled Mode)
client = MixpanelClient.from_env()
# Track an event
client.track("user-123", "Page Viewed", properties={"page": "/home"})
# Set user profile properties
client.people_set("user-123", {"$name": "Jane Doe", "$email": "jane@example.com"})
Direct Construction
client = MixpanelClient(
token="your-project-token",
enabled=True,
on_error="log", # "log" | "raise" | "ignore"
)
Disabled Mode
# Explicitly disabled
client = MixpanelClient(token="...", enabled=False)
client.track("user-123", "Page Viewed") # silently does nothing
# Via environment variable
# MIXPANEL_ENABLED=false → disabled
client = MixpanelClient.from_env()
# Missing token → automatically disabled, and a warning names the variable
# (no MIXPANEL_TOKEN set)
client = MixpanelClient.from_env()
assert not client.enabled
When the token is missing, from_env() behaves according to how tracking was
turned on:
enabled |
Missing or empty token |
|---|---|
True (passed explicitly) |
Raises ValueError naming the variable |
None (default; MIXPANEL_ENABLED unset or truthy) |
Disabled, one warning naming the variable |
False, or MIXPANEL_ENABLED falsey |
Disabled, no warning |
Pass enabled=True where a deployment must send events, so a missing secret
fails at startup instead of looking like Mixpanel going quiet. An app that only
sends events when deployed can pass that condition, e.g.
MixpanelClient.from_env(enabled=IS_DEPLOYED): quiet locally, an error on a
deployment missing its token.
Error Handling
# Default: catch and log errors (analytics never crashes your app)
client = MixpanelClient(token="...", on_error="log")
# Raise errors (useful for debugging)
client = MixpanelClient(token="...", on_error="raise")
# Silently ignore errors
client = MixpanelClient(token="...", on_error="ignore")
Custom Consumer
from mixpanel import BufferedConsumer
# Use a buffered consumer for high-throughput
client = MixpanelClient(token="...", consumer=BufferedConsumer())
A BufferedConsumer only sends when its buffer fills, so flush it before the
process exits (a Cloud Run instance shutting down, a job or CLI command
finishing), or the buffered events are lost:
client.flush()
# Or flush automatically when the block ends, even if it raises:
with MixpanelClient(token="...", consumer=BufferedConsumer()) as client:
client.track("user-123", "Job Finished")
With the default consumer, which sends each message immediately, flush()
does nothing. A failing flush follows on_error like every other call.
Any object with send(endpoint, json_message) works as a consumer; the
MixpanelConsumer protocol describes it for type checking.
Threads
One MixpanelClient can be shared across threads, such as a module-level
client in a web app. Each thread gets its own Mixpanel SDK instance and, with
the default consumer, its own HTTP session: the SDK's Consumer and
BufferedConsumer aren't safe to share between threads.
A consumer= is a single object every thread shares, so in a threaded app pass
a consumer_factory instead; it's called once per thread:
from mixpanel import BufferedConsumer, Consumer
# A shorter request timeout, one consumer per thread
client = MixpanelClient.from_env(consumer_factory=lambda: Consumer(request_timeout=5))
# Buffered, one buffer per thread
client = MixpanelClient(token="...", consumer_factory=BufferedConsumer)
flush() flushes every thread's consumer. Call it when no other thread is
tracking, such as at shutdown.
Testing with FakeMixpanelClient
from altissimo.mixpanel import FakeMixpanelClient
def test_order_tracking():
fake = FakeMixpanelClient()
service = OrderService(mixpanel=fake)
service.create_order(user_id="user-123", total=99.99)
assert len(fake.tracked) == 1
assert fake.tracked[0][1] == "Order Created"
assert fake.tracked[0][2]["total"] == 99.99
Protocol-Based Dependency Injection
from altissimo.mixpanel import MixpanelClientProtocol
class OrderService:
def __init__(self, mixpanel: MixpanelClientProtocol) -> None:
self._mp = mixpanel
def create_order(self, user_id: str, total: float) -> None:
# ... business logic ...
self._mp.track(user_id, "Order Created", properties={"total": total})
API Reference
MixpanelClient
| Method | Description |
|---|---|
track(distinct_id, event_name, *, properties?, meta?) |
Track an event |
people_set(distinct_id, properties, *, meta?) |
Set user profile properties |
people_set_once(distinct_id, properties, *, meta?) |
Set properties only if not set |
people_increment(distinct_id, properties, *, meta?) |
Increment numeric properties |
people_union(distinct_id, properties, *, meta?) |
Append to list properties |
people_unset(distinct_id, property_names, *, meta?) |
Remove properties |
alias(alias_id, original_id, *, meta?) |
Create an identity alias |
Environment Variables
| Variable | Default | Description |
|---|---|---|
MIXPANEL_TOKEN |
— | Mixpanel project token |
MIXPANEL_ENABLED |
true |
Set to false/0/no to disable |
Development
# Install dependencies
poetry sync
# Run tests
poetry run pytest
# Run tests with coverage
poetry run pytest --cov=altissimo --cov-report=term-missing
# Lint
poetry run ruff check .
# Format
poetry run ruff format .
License
Metadata
Release files for altissimo-mixpanel 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| altissimo_mixpanel-0.1.0.tar.gz | 14.0 kB | With provenance | Details for altissimo_mixpanel-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| altissimo_mixpanel-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for altissimo_mixpanel-0.1.0-py3-none-any.whl |
Total release size: 28.5 kB
Release files / altissimo_mixpanel-0.1.0.tar.gz
| Download URL | altissimo_mixpanel-0.1.0.tar.gz |
|---|---|
| Size | 14.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
901338423fa33e762d9338f4996887167a486db13e72638f25ed135d179d766d
|
|
BLAKE2b-256 checksum How to use checksums |
ba18443f86f783338dfe6d0190d8757ee273fc263b5ce26e16f2f4838af203e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / altissimo_mixpanel-0.1.0-py3-none-any.whl
| Download URL | altissimo_mixpanel-0.1.0-py3-none-any.whl |
|---|---|
| Size | 14.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d21022a9c4931960ebc572e0013e0c75bd2e4bc2a21ea8e4c75a0d9808a92874
|
|
BLAKE2b-256 checksum How to use checksums |
1bc9a08deeb5f39e8f91a3437234095e0cfd7f3bbf661843ae20d6c1883c5e9c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for altissimo-mixpanel 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for altissimo_mixpanel-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.