glean-klient 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ klient/__init__.py,sha256=QFQ7_cJpO2VoZO9DM55D9v9cH5VXA6ZxXxsw7-_zERA,5124
2
+ klient/__main__.py,sha256=mWLv3EylzMx6iJ0JnClxHUKnY8q5ZCvy1AUGb3cckLg,52077
3
+ klient/admin.py,sha256=Nmu20etsR-Fo96wi9lPevimP4LS3mHZ__D41y6hTHNo,9772
4
+ klient/consumer.py,sha256=IUUciDdpbMnYfC9IthrUzk4K_hKvjbMPQIiD0fSoIXI,23517
5
+ klient/metrics.py,sha256=ADtgUO6f2lGSIC2zfuLiUY0utUEvYC7fW4pcg0ukfOg,910
6
+ klient/producer.py,sha256=DrQI_FnDWs0sgLQYeDBkMQHT9c31AcyC8YNvyA3_aMw,29837
7
+ klient/relay.py,sha256=tWBRJoyaQizmEdoU4CZ7-Y3sGbhLvAXEHbpVB2BhTc8,3903
8
+ glean_klient-0.1.0.dist-info/METADATA,sha256=k0TRhsecLw0ovaHq7mWJMOVQEEkpH-BJAJdcRtrd614,33086
9
+ glean_klient-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ glean_klient-0.1.0.dist-info/entry_points.txt,sha256=jqD3TY-cMjnVswUCXAw6hXIVUqP9bN2I3Y-EYHcOQbM,52
11
+ glean_klient-0.1.0.dist-info/top_level.txt,sha256=oZsSRRLlGYX2kCEHIrFnXwU-aoNy7pEPfmDaDtMH0Oc,7
12
+ glean_klient-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ glean-kafka = klient.__main__:cli
@@ -0,0 +1 @@
1
+ klient
klient/__init__.py ADDED
@@ -0,0 +1,106 @@
1
+ """Kafka client wrapper library supporting both synchronous and asynchronous operations.
2
+
3
+ Provides high-level interfaces for:
4
+ - Synchronous and asynchronous message consumption
5
+ - Synchronous and asynchronous message production
6
+ - Transaction support (begin/commit/abort + context managers)
7
+ - Topic and cluster administration
8
+ - Unified environment-based configuration loading utilities
9
+
10
+ Environment configuration loader utilities are defined first to avoid circular
11
+ import issues when producer/consumer/admin modules import them.
12
+ """
13
+
14
+ from pathlib import Path
15
+ import json
16
+ import os
17
+ import typing as t
18
+
19
+ def _load_raw_config_file(path: Path) -> t.Dict[str, t.Any]:
20
+ if not path.exists():
21
+ return {}
22
+ try:
23
+ with path.open('r', encoding='utf-8') as f:
24
+ return json.load(f)
25
+ except json.JSONDecodeError as e:
26
+ raise ValueError(f"Invalid JSON in config file {path}: {e}")
27
+ except Exception as e:
28
+ raise ValueError(f"Failed to read config file {path}: {e}")
29
+
30
+ def resolve_env_config(env: t.Optional[str], explicit_file: t.Optional[str]) -> t.Dict[str, t.Any]:
31
+ """Resolve configuration from a SINGLE JSON file with default + environment sections.
32
+
33
+ Model:
34
+ - Exactly one file supplies configuration: either `explicit_file` if provided,
35
+ else `~/.kafka/config.json`.
36
+ - File may contain a `default` object and any number of named environment objects.
37
+ - When `env` is specified, effective config = merge of `default` (if present) overlaid
38
+ by the named environment object. Key collisions favor the environment.
39
+ - When `env` is not specified, returns the `default` object if present else the raw top-level mapping.
40
+
41
+ Example structure:
42
+ {
43
+ "default": {"bootstrap.servers": "localhost:9092", "acks": "all"},
44
+ "prod": {"bootstrap.servers": "prod-broker:9092", "compression.type": "lz4"},
45
+ "dev": {"bootstrap.servers": "dev-broker:9092"}
46
+ }
47
+
48
+ Topic names MUST NOT appear in this file. Returns empty dict if file missing or invalid.
49
+ """
50
+ home_dir = Path(os.path.expanduser('~')) / '.kafka'
51
+ path = Path(explicit_file) if explicit_file else home_dir / 'config.json'
52
+ raw = _load_raw_config_file(path)
53
+ if not isinstance(raw, dict):
54
+ return {}
55
+ # Legacy compatibility: allow per-env standalone file ~/.kafka/{env}.json if config.json missing
56
+ if not explicit_file and env and not raw and (home_dir / f"{env}.json").exists():
57
+ env_raw = _load_raw_config_file(home_dir / f"{env}.json")
58
+ return env_raw if isinstance(env_raw, dict) else {}
59
+ default_section = raw.get('default') if isinstance(raw.get('default'), dict) else {}
60
+ if env:
61
+ from typing import cast, Dict, Any
62
+ env_section_raw = raw.get(env) if isinstance(raw.get(env), dict) else {}
63
+ # If no default/env structured sections present, and raw looks like direct config, return raw mapping unchanged
64
+ if not default_section and not env_section_raw and 'bootstrap.servers' in raw:
65
+ return raw
66
+ default_cast: Dict[str, Any] = cast(Dict[str, Any], default_section if isinstance(default_section, dict) else {})
67
+ env_cast: Dict[str, Any] = cast(Dict[str, Any], env_section_raw if isinstance(env_section_raw, dict) else {})
68
+ merged: Dict[str, Any] = dict(default_cast)
69
+ merged.update(env_cast)
70
+ # If env not found, fall back to default_cast
71
+ return merged if env_cast else default_cast
72
+ # No env requested: return default section if present else raw mapping
73
+ return default_section or raw
74
+
75
+ def split_env_config(raw: t.Dict[str, t.Any]) -> t.Tuple[t.Dict[str, t.Any], t.Dict[str, t.Any], t.Dict[str, t.Any]]:
76
+ if not raw:
77
+ return {}, {}, {}
78
+ common = raw.get('common', {}) if isinstance(raw.get('common'), dict) else {}
79
+ def _section(name: str) -> t.Dict[str, t.Any]:
80
+ sec = raw.get(name, {})
81
+ return sec if isinstance(sec, dict) else {}
82
+ prod = {**common, **_section('producer')}
83
+ cons = {**common, **_section('consumer')}
84
+ adm = {**common, **_section('admin')}
85
+ if not prod and not cons and not adm and not common:
86
+ return raw, raw, raw
87
+ return prod, cons, adm
88
+
89
+ def extract_bootstrap(raw_sections: t.Sequence[t.Dict[str, t.Any]]) -> t.Optional[str]:
90
+ for section in raw_sections:
91
+ if 'bootstrap.servers' in section:
92
+ return section['bootstrap.servers']
93
+ return None
94
+
95
+ from .producer import KafkaProducer, ProducerConfig, ProduceResult, KafkaProducerError, TransactionResult, KafkaTransactionError # noqa: E402
96
+ from .consumer import KafkaConsumer, ConsumerConfig, MessageResult, KafkaConsumerError # noqa: E402
97
+ from .admin import KafkaAdmin, AdminConfig, TopicMetadata, KafkaAdminError # noqa: E402
98
+
99
+ __version__ = "0.1.0"
100
+
101
+ __all__ = [
102
+ 'KafkaProducer','ProducerConfig','ProduceResult','KafkaProducerError','KafkaTransactionError','TransactionResult',
103
+ 'KafkaConsumer','ConsumerConfig','MessageResult','KafkaConsumerError',
104
+ 'KafkaAdmin','AdminConfig','KafkaAdminError','TopicMetadata',
105
+ 'resolve_env_config','split_env_config','extract_bootstrap'
106
+ ]