Skip to content

Commit 46a5c28

Browse files
committed
Release v1.0.3
1 parent bd75a91 commit 46a5c28

11 files changed

Lines changed: 693 additions & 75 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 0 additions & 27 deletions
This file was deleted.

.github/workflows/test.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
python-version: ["3.9", "3.10", "3.11", "3.12"]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Install dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
pip install pytest
29+
30+
- name: Run tests
31+
run: pytest -v

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [1.0.1] - 2025-11-27
8+
## [1.0.3] - 2025-12-10
99

10-
- Initial release
10+
- Update SDK endpoints for improved consistency
11+
12+
- Audit and modernize SDK for better reliability
1113

LICENSE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Klime
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

README.md

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# klime-analytics
1+
# klime
22

3-
Klime Analytics SDK for Python.
3+
Klime SDK for Python.
44

55
## Installation
66

77
```bash
8-
pip install klime-analytics
8+
pip install klime
99
```
1010

1111
## Quick Start
@@ -17,24 +17,27 @@ client = KlimeClient(
1717
write_key='your-write-key'
1818
)
1919

20-
# Track an event
21-
client.track('Button Clicked', {
22-
'button_name': 'Sign up',
23-
'plan': 'pro'
24-
})
25-
2620
# Identify a user
2721
client.identify('user_123', {
2822
'email': '[email protected]',
2923
'name': 'Stefan'
3024
})
3125

32-
# Group a user with an organization
26+
# Track an event
27+
client.track('Button Clicked', {
28+
'button_name': 'Sign up',
29+
'plan': 'pro'
30+
}, user_id='user_123')
31+
32+
# Associate user with a group and set group traits
3333
client.group('org_456', {
3434
'name': 'Acme Inc',
3535
'plan': 'enterprise'
3636
}, user_id='user_123')
3737

38+
# Or just link the user to a group (if traits are already set)
39+
client.group('org_456', user_id='user_123')
40+
3841
# Shutdown gracefully
3942
client.shutdown()
4043
```
@@ -46,7 +49,7 @@ client.shutdown()
4649
```python
4750
KlimeClient(
4851
write_key: str, # Required: Your Klime write key
49-
endpoint: Optional[str] = None, # Optional: API endpoint (default: https://ingest.klime.com)
52+
endpoint: Optional[str] = None, # Optional: API endpoint (default: https://i.klime.com)
5053
flush_interval: Optional[int] = None, # Optional: Milliseconds between flushes (default: 2000)
5154
max_batch_size: Optional[int] = None, # Optional: Max events per batch (default: 20, max: 100)
5255
max_queue_size: Optional[int] = None, # Optional: Max queued events (default: 1000)
@@ -60,22 +63,23 @@ KlimeClient(
6063

6164
#### `track(event: str, properties: Optional[Dict] = None, user_id: Optional[str] = None, group_id: Optional[str] = None, ip: Optional[str] = None) -> None`
6265

63-
Track a user event.
66+
Track a user event. A `user_id` is required for events to be useful in Klime.
6467

6568
```python
66-
# Simple usage
6769
client.track('Button Clicked', {
6870
'button_name': 'Sign up',
6971
'plan': 'pro'
70-
})
72+
}, user_id='user_123')
7173

72-
# With user context and IP
74+
# With IP address (for geolocation)
7375
client.track('Button Clicked', {
7476
'button_name': 'Sign up',
7577
'plan': 'pro'
76-
}, user_id='user_123', group_id='org_456', ip='192.168.1.1')
78+
}, user_id='user_123', ip='192.168.1.1')
7779
```
7880

81+
> **Advanced**: The `group_id` parameter is available for multi-tenant scenarios where a user belongs to multiple organizations and you need to specify which organization context the event occurred in.
82+
7983
#### `identify(user_id: str, traits: Optional[Dict] = None, ip: Optional[str] = None) -> None`
8084

8185
Identify a user with traits.
@@ -89,20 +93,23 @@ client.identify('user_123', {
8993

9094
#### `group(group_id: str, traits: Optional[Dict] = None, user_id: Optional[str] = None, ip: Optional[str] = None) -> None`
9195

92-
Associate a user with a group and set group traits.
96+
Associate a user with a group and/or set group traits.
9397

9498
```python
95-
# Simple usage
99+
# Associate user with a group and set group traits (most common)
96100
client.group('org_456', {
97101
'name': 'Acme Inc',
98102
'plan': 'enterprise'
99-
})
103+
}, user_id='user_123')
104+
105+
# Just link a user to a group (traits already set or not needed)
106+
client.group('org_456', user_id='user_123')
100107

101-
# With user ID and IP
108+
# Just update group traits (e.g., from a webhook or background job)
102109
client.group('org_456', {
103-
'name': 'Acme Inc',
104-
'plan': 'enterprise'
105-
}, user_id='user_123', ip='192.168.1.1')
110+
'plan': 'enterprise',
111+
'employee_count': 50
112+
})
106113
```
107114

108115
#### `flush() -> None`
@@ -170,7 +177,7 @@ def button_clicked():
170177
client.track('Button Clicked', {
171178
'button_name': request.json.get('buttonName')
172179
}, user_id=request.json.get('userId'), ip=request.remote_addr)
173-
180+
174181
return {'success': True}
175182

176183
# Graceful shutdown
@@ -192,16 +199,15 @@ class ButtonClickView(View):
192199
client.track('Button Clicked', {
193200
'button_name': request.POST.get('buttonName')
194201
}, user_id=str(request.user.id), ip=request.META.get('REMOTE_ADDR'))
195-
202+
196203
return JsonResponse({'success': True})
197204
```
198205

199206
## Requirements
200207

201-
- Python 3.7 or higher
208+
- Python 3.9 or higher
202209
- No external dependencies (uses only standard library)
203210

204211
## License
205212

206213
MIT
207-

klime/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from .client import KlimeClient
22

3-
__version__ = "1.0.0"
3+
__version__ = "1.0.2"
44
__all__ = ["KlimeClient"]
55

klime/client.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
ValidationError,
2020
)
2121

22-
DEFAULT_ENDPOINT = "https://ingest.klime.com"
22+
DEFAULT_ENDPOINT = "https://i.klime.com"
2323
DEFAULT_FLUSH_INTERVAL = 2000 # milliseconds
2424
DEFAULT_MAX_BATCH_SIZE = 20
2525
DEFAULT_MAX_QUEUE_SIZE = 1000
@@ -28,7 +28,7 @@
2828
MAX_BATCH_SIZE = 100
2929
MAX_EVENT_SIZE_BYTES = 200 * 1024 # 200KB
3030
MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024 # 10MB
31-
SDK_VERSION = "1.0.0"
31+
SDK_VERSION = "1.0.2"
3232

3333

3434
class KlimeClient:
@@ -161,7 +161,8 @@ def shutdown(self) -> None:
161161
self._flush_timer.cancel()
162162
self._flush_timer = None
163163

164-
self.flush()
164+
# Force flush remaining events (bypass normal flush which checks _is_shutdown)
165+
self._flush_remaining()
165166

166167
def _enqueue(self, event: Event) -> None:
167168
# Check event size
@@ -192,16 +193,20 @@ def _do_flush(self) -> None:
192193
self._flush_timer = None
193194

194195
# Process batches
196+
self._flush_remaining()
197+
198+
# Schedule next flush
199+
if not self._is_shutdown:
200+
self._schedule_flush()
201+
202+
def _flush_remaining(self) -> None:
203+
"""Flush all queued events without scheduling (used by shutdown)."""
195204
while True:
196205
batch = self._extract_batch()
197206
if not batch:
198207
break
199-
200208
self._send_batch(batch)
201209

202-
# Schedule next flush
203-
self._schedule_flush()
204-
205210
def _extract_batch(self) -> List[Event]:
206211
batch: List[Event] = []
207212
batch_size = 0
@@ -350,7 +355,7 @@ def _generate_uuid(self) -> str:
350355
return str(uuid.uuid4())
351356

352357
def _generate_timestamp(self) -> str:
353-
return datetime.now(timezone.utc).isoformat()
358+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
354359

355360
def _get_context(self, ip: Optional[str] = None) -> EventContext:
356361
context = EventContext(

pyproject.toml

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,31 @@ requires = ["setuptools>=61.0", "wheel"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
6-
name = "klime-analytics"
7-
version = "1.0.1"
8-
description = "Klime Analytics SDK for Python"
6+
name = "klime"
7+
version = "1.0.3"
8+
description = "Klime SDK for Python"
99
readme = "README.md"
10-
requires-python = ">=3.7"
10+
requires-python = ">=3.9"
1111
license = {text = "MIT"}
1212
authors = [
1313
{name = "Klime"}
1414
]
1515
keywords = ["analytics", "klime", "tracking", "events"]
1616
classifiers = [
17-
"Development Status :: 4 - Beta",
17+
"Development Status :: 5 - Production/Stable",
1818
"Intended Audience :: Developers",
1919
"License :: OSI Approved :: MIT License",
2020
"Programming Language :: Python :: 3",
21-
"Programming Language :: Python :: 3.7",
22-
"Programming Language :: Python :: 3.8",
2321
"Programming Language :: Python :: 3.9",
2422
"Programming Language :: Python :: 3.10",
2523
"Programming Language :: Python :: 3.11",
24+
"Programming Language :: Python :: 3.12",
2625
]
2726

2827
[project.urls]
29-
Homepage = "https://github.com/klime/analytics-python"
30-
Documentation = "https://github.com/klime/analytics-python"
31-
Repository = "https://github.com/klime/analytics-python"
28+
Homepage = "https://github.com/klimeapp/klime-python"
29+
Documentation = "https://github.com/klimeapp/klime-python"
30+
Repository = "https://github.com/klimeapp/klime-python"
3231

3332
[tool.setuptools]
3433
packages = ["klime"]

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)