kafka-python 3.0.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.
Files changed (373) hide show
  1. kafka/__init__.py +34 -0
  2. kafka/__main__.py +5 -0
  3. kafka/admin/__init__.py +29 -0
  4. kafka/admin/__main__.py +5 -0
  5. kafka/admin/_acls.py +355 -0
  6. kafka/admin/_cluster.py +359 -0
  7. kafka/admin/_configs.py +479 -0
  8. kafka/admin/_groups.py +754 -0
  9. kafka/admin/_partitions.py +595 -0
  10. kafka/admin/_topics.py +281 -0
  11. kafka/admin/_transactions.py +450 -0
  12. kafka/admin/_users.py +194 -0
  13. kafka/admin/client.py +373 -0
  14. kafka/benchmarks/__init__.py +0 -0
  15. kafka/benchmarks/consumer_performance.py +138 -0
  16. kafka/benchmarks/load_example.py +109 -0
  17. kafka/benchmarks/producer_encode_path.py +201 -0
  18. kafka/benchmarks/producer_performance.py +161 -0
  19. kafka/benchmarks/profile_protocol.py +138 -0
  20. kafka/benchmarks/protocol_old_vs_new.py +447 -0
  21. kafka/benchmarks/record_batch_compose.py +77 -0
  22. kafka/benchmarks/record_batch_read.py +82 -0
  23. kafka/benchmarks/varint_speed.py +426 -0
  24. kafka/cli/__init__.py +36 -0
  25. kafka/cli/admin/__init__.py +117 -0
  26. kafka/cli/admin/acls/__init__.py +9 -0
  27. kafka/cli/admin/acls/common.py +76 -0
  28. kafka/cli/admin/acls/create.py +19 -0
  29. kafka/cli/admin/acls/delete.py +23 -0
  30. kafka/cli/admin/acls/describe.py +16 -0
  31. kafka/cli/admin/cluster/__init__.py +14 -0
  32. kafka/cli/admin/cluster/describe.py +11 -0
  33. kafka/cli/admin/cluster/describe_quorum.py +11 -0
  34. kafka/cli/admin/cluster/features.py +52 -0
  35. kafka/cli/admin/cluster/log_dirs.py +43 -0
  36. kafka/cli/admin/cluster/versions.py +33 -0
  37. kafka/cli/admin/configs/__init__.py +10 -0
  38. kafka/cli/admin/configs/alter.py +43 -0
  39. kafka/cli/admin/configs/common.py +17 -0
  40. kafka/cli/admin/configs/describe.py +30 -0
  41. kafka/cli/admin/configs/list.py +16 -0
  42. kafka/cli/admin/configs/reset.py +20 -0
  43. kafka/cli/admin/groups/__init__.py +16 -0
  44. kafka/cli/admin/groups/alter_offsets.py +30 -0
  45. kafka/cli/admin/groups/delete.py +11 -0
  46. kafka/cli/admin/groups/delete_offsets.py +29 -0
  47. kafka/cli/admin/groups/describe.py +11 -0
  48. kafka/cli/admin/groups/list.py +28 -0
  49. kafka/cli/admin/groups/list_offsets.py +29 -0
  50. kafka/cli/admin/groups/remove_members.py +40 -0
  51. kafka/cli/admin/groups/reset_offsets.py +139 -0
  52. kafka/cli/admin/partitions/__init__.py +21 -0
  53. kafka/cli/admin/partitions/alter_reassignments.py +37 -0
  54. kafka/cli/admin/partitions/create.py +27 -0
  55. kafka/cli/admin/partitions/delete_records.py +31 -0
  56. kafka/cli/admin/partitions/describe.py +36 -0
  57. kafka/cli/admin/partitions/elect_leaders.py +53 -0
  58. kafka/cli/admin/partitions/list_offsets.py +88 -0
  59. kafka/cli/admin/partitions/list_reassignments.py +35 -0
  60. kafka/cli/admin/topics/__init__.py +10 -0
  61. kafka/cli/admin/topics/create.py +13 -0
  62. kafka/cli/admin/topics/delete.py +19 -0
  63. kafka/cli/admin/topics/describe.py +18 -0
  64. kafka/cli/admin/topics/list.py +11 -0
  65. kafka/cli/admin/transactions/__init__.py +17 -0
  66. kafka/cli/admin/transactions/abort.py +38 -0
  67. kafka/cli/admin/transactions/describe.py +24 -0
  68. kafka/cli/admin/transactions/describe_producers.py +29 -0
  69. kafka/cli/admin/transactions/find_hanging.py +26 -0
  70. kafka/cli/admin/transactions/list.py +37 -0
  71. kafka/cli/admin/users/__init__.py +8 -0
  72. kafka/cli/admin/users/alter_user_scram_credentials.py +34 -0
  73. kafka/cli/admin/users/describe_user_scram_credentials.py +15 -0
  74. kafka/cli/common.py +95 -0
  75. kafka/cli/consumer/__init__.py +63 -0
  76. kafka/cli/producer/__init__.py +57 -0
  77. kafka/cluster.py +824 -0
  78. kafka/codec.py +325 -0
  79. kafka/consumer/__init__.py +5 -0
  80. kafka/consumer/__main__.py +5 -0
  81. kafka/consumer/fetcher.py +2012 -0
  82. kafka/consumer/group.py +1347 -0
  83. kafka/consumer/subscription_state.py +897 -0
  84. kafka/coordinator/__init__.py +0 -0
  85. kafka/coordinator/assignors/__init__.py +0 -0
  86. kafka/coordinator/assignors/abstract.py +90 -0
  87. kafka/coordinator/assignors/cooperative_sticky.py +167 -0
  88. kafka/coordinator/assignors/range.py +81 -0
  89. kafka/coordinator/assignors/roundrobin.py +101 -0
  90. kafka/coordinator/assignors/sticky/StickyAssignorUserData.json +37 -0
  91. kafka/coordinator/assignors/sticky/__init__.py +0 -0
  92. kafka/coordinator/assignors/sticky/partition_movements.py +149 -0
  93. kafka/coordinator/assignors/sticky/sorted_set.py +63 -0
  94. kafka/coordinator/assignors/sticky/sticky_assignor.py +665 -0
  95. kafka/coordinator/assignors/sticky/user_data.py +8 -0
  96. kafka/coordinator/base.py +1215 -0
  97. kafka/coordinator/consumer.py +1224 -0
  98. kafka/coordinator/heartbeat.py +82 -0
  99. kafka/coordinator/subscription.py +34 -0
  100. kafka/errors.py +1004 -0
  101. kafka/future.py +166 -0
  102. kafka/metrics/__init__.py +13 -0
  103. kafka/metrics/compound_stat.py +33 -0
  104. kafka/metrics/dict_reporter.py +81 -0
  105. kafka/metrics/kafka_metric.py +36 -0
  106. kafka/metrics/measurable.py +27 -0
  107. kafka/metrics/measurable_stat.py +13 -0
  108. kafka/metrics/metric_config.py +33 -0
  109. kafka/metrics/metric_name.py +105 -0
  110. kafka/metrics/metrics.py +261 -0
  111. kafka/metrics/metrics_reporter.py +53 -0
  112. kafka/metrics/quota.py +41 -0
  113. kafka/metrics/stat.py +19 -0
  114. kafka/metrics/stats/__init__.py +15 -0
  115. kafka/metrics/stats/avg.py +24 -0
  116. kafka/metrics/stats/count.py +17 -0
  117. kafka/metrics/stats/histogram.py +99 -0
  118. kafka/metrics/stats/max_stat.py +17 -0
  119. kafka/metrics/stats/min_stat.py +19 -0
  120. kafka/metrics/stats/percentile.py +14 -0
  121. kafka/metrics/stats/percentiles.py +75 -0
  122. kafka/metrics/stats/rate.py +118 -0
  123. kafka/metrics/stats/sampled_stat.py +99 -0
  124. kafka/metrics/stats/sensor.py +136 -0
  125. kafka/metrics/stats/total.py +15 -0
  126. kafka/net/__init__.py +19 -0
  127. kafka/net/compat.py +165 -0
  128. kafka/net/connection.py +593 -0
  129. kafka/net/http_connect.py +144 -0
  130. kafka/net/inet.py +122 -0
  131. kafka/net/manager.py +451 -0
  132. kafka/net/metrics.py +149 -0
  133. kafka/net/sasl/__init__.py +32 -0
  134. kafka/net/sasl/abc.py +28 -0
  135. kafka/net/sasl/gssapi.py +95 -0
  136. kafka/net/sasl/msk.py +245 -0
  137. kafka/net/sasl/oauth.py +98 -0
  138. kafka/net/sasl/plain.py +42 -0
  139. kafka/net/sasl/scram.py +135 -0
  140. kafka/net/sasl/sspi.py +111 -0
  141. kafka/net/selector.py +644 -0
  142. kafka/net/socks5.py +262 -0
  143. kafka/net/transport.py +415 -0
  144. kafka/net/wakeup_notifier.py +72 -0
  145. kafka/partitioner/__init__.py +8 -0
  146. kafka/partitioner/abc.py +8 -0
  147. kafka/partitioner/default.py +89 -0
  148. kafka/partitioner/sticky.py +109 -0
  149. kafka/producer/__init__.py +5 -0
  150. kafka/producer/__main__.py +5 -0
  151. kafka/producer/future.py +101 -0
  152. kafka/producer/kafka.py +1123 -0
  153. kafka/producer/producer_batch.py +192 -0
  154. kafka/producer/record_accumulator.py +647 -0
  155. kafka/producer/sender.py +884 -0
  156. kafka/producer/transaction_manager.py +1326 -0
  157. kafka/protocol/__init__.py +0 -0
  158. kafka/protocol/admin/__init__.py +29 -0
  159. kafka/protocol/admin/acl.py +83 -0
  160. kafka/protocol/admin/acl.pyi +375 -0
  161. kafka/protocol/admin/client_quotas.py +14 -0
  162. kafka/protocol/admin/client_quotas.pyi +265 -0
  163. kafka/protocol/admin/cluster.py +31 -0
  164. kafka/protocol/admin/cluster.pyi +620 -0
  165. kafka/protocol/admin/configs.py +22 -0
  166. kafka/protocol/admin/configs.pyi +437 -0
  167. kafka/protocol/admin/groups.py +24 -0
  168. kafka/protocol/admin/groups.pyi +261 -0
  169. kafka/protocol/admin/topics.py +53 -0
  170. kafka/protocol/admin/topics.pyi +982 -0
  171. kafka/protocol/admin/transactions.py +18 -0
  172. kafka/protocol/admin/transactions.pyi +311 -0
  173. kafka/protocol/admin/users.py +14 -0
  174. kafka/protocol/admin/users.pyi +223 -0
  175. kafka/protocol/api_data.py +125 -0
  176. kafka/protocol/api_header.py +55 -0
  177. kafka/protocol/api_key.py +97 -0
  178. kafka/protocol/api_message.py +277 -0
  179. kafka/protocol/broker_version_data.py +246 -0
  180. kafka/protocol/consumer/__init__.py +13 -0
  181. kafka/protocol/consumer/fetch.py +16 -0
  182. kafka/protocol/consumer/fetch.pyi +298 -0
  183. kafka/protocol/consumer/group.py +38 -0
  184. kafka/protocol/consumer/group.pyi +824 -0
  185. kafka/protocol/consumer/metadata.py +30 -0
  186. kafka/protocol/consumer/metadata.pyi +89 -0
  187. kafka/protocol/consumer/offsets.py +75 -0
  188. kafka/protocol/consumer/offsets.pyi +288 -0
  189. kafka/protocol/data_container.py +166 -0
  190. kafka/protocol/frame.py +30 -0
  191. kafka/protocol/generate_stubs.py +468 -0
  192. kafka/protocol/metadata/__init__.py +10 -0
  193. kafka/protocol/metadata/api_versions.py +41 -0
  194. kafka/protocol/metadata/api_versions.pyi +128 -0
  195. kafka/protocol/metadata/find_coordinator.py +19 -0
  196. kafka/protocol/metadata/find_coordinator.pyi +105 -0
  197. kafka/protocol/metadata/metadata.py +34 -0
  198. kafka/protocol/metadata/metadata.pyi +160 -0
  199. kafka/protocol/old/__init__.py +0 -0
  200. kafka/protocol/old/abstract.py +17 -0
  201. kafka/protocol/old/add_offsets_to_txn.py +54 -0
  202. kafka/protocol/old/add_partitions_to_txn.py +71 -0
  203. kafka/protocol/old/admin.py +1086 -0
  204. kafka/protocol/old/api.py +205 -0
  205. kafka/protocol/old/api_versions.py +133 -0
  206. kafka/protocol/old/commit.py +355 -0
  207. kafka/protocol/old/consumer_protocol.py +36 -0
  208. kafka/protocol/old/end_txn.py +53 -0
  209. kafka/protocol/old/fetch.py +408 -0
  210. kafka/protocol/old/find_coordinator.py +72 -0
  211. kafka/protocol/old/group.py +451 -0
  212. kafka/protocol/old/init_producer_id.py +42 -0
  213. kafka/protocol/old/list_offsets.py +186 -0
  214. kafka/protocol/old/metadata.py +290 -0
  215. kafka/protocol/old/offset_for_leader_epoch.py +133 -0
  216. kafka/protocol/old/produce.py +247 -0
  217. kafka/protocol/old/sasl_authenticate.py +38 -0
  218. kafka/protocol/old/sasl_handshake.py +39 -0
  219. kafka/protocol/old/struct.py +87 -0
  220. kafka/protocol/old/txn_offset_commit.py +73 -0
  221. kafka/protocol/old/types.py +440 -0
  222. kafka/protocol/parser.py +191 -0
  223. kafka/protocol/producer/__init__.py +7 -0
  224. kafka/protocol/producer/produce.py +17 -0
  225. kafka/protocol/producer/produce.pyi +197 -0
  226. kafka/protocol/producer/transaction.py +30 -0
  227. kafka/protocol/producer/transaction.pyi +663 -0
  228. kafka/protocol/sasl.py +52 -0
  229. kafka/protocol/sasl.pyi +126 -0
  230. kafka/protocol/schemas/__init__.py +7 -0
  231. kafka/protocol/schemas/fields/__init__.py +7 -0
  232. kafka/protocol/schemas/fields/array.py +127 -0
  233. kafka/protocol/schemas/fields/base.py +156 -0
  234. kafka/protocol/schemas/fields/codecs/__init__.py +12 -0
  235. kafka/protocol/schemas/fields/codecs/encode_buffer.py +82 -0
  236. kafka/protocol/schemas/fields/codecs/tagged_fields.py +109 -0
  237. kafka/protocol/schemas/fields/codecs/types.py +505 -0
  238. kafka/protocol/schemas/fields/codegen.py +40 -0
  239. kafka/protocol/schemas/fields/simple.py +127 -0
  240. kafka/protocol/schemas/fields/struct.py +357 -0
  241. kafka/protocol/schemas/fields/struct_array.py +142 -0
  242. kafka/protocol/schemas/load_json.py +42 -0
  243. kafka/protocol/schemas/resources/AddOffsetsToTxnRequest.json +40 -0
  244. kafka/protocol/schemas/resources/AddOffsetsToTxnResponse.json +35 -0
  245. kafka/protocol/schemas/resources/AddPartitionsToTxnRequest.json +65 -0
  246. kafka/protocol/schemas/resources/AddPartitionsToTxnResponse.json +60 -0
  247. kafka/protocol/schemas/resources/AlterClientQuotasRequest.json +47 -0
  248. kafka/protocol/schemas/resources/AlterClientQuotasResponse.json +41 -0
  249. kafka/protocol/schemas/resources/AlterConfigsRequest.json +43 -0
  250. kafka/protocol/schemas/resources/AlterConfigsResponse.json +39 -0
  251. kafka/protocol/schemas/resources/AlterPartitionReassignmentsRequest.json +42 -0
  252. kafka/protocol/schemas/resources/AlterPartitionReassignmentsResponse.json +47 -0
  253. kafka/protocol/schemas/resources/AlterReplicaLogDirsRequest.json +41 -0
  254. kafka/protocol/schemas/resources/AlterReplicaLogDirsResponse.json +41 -0
  255. kafka/protocol/schemas/resources/AlterUserScramCredentialsRequest.json +45 -0
  256. kafka/protocol/schemas/resources/AlterUserScramCredentialsResponse.json +35 -0
  257. kafka/protocol/schemas/resources/ApiVersionsRequest.json +34 -0
  258. kafka/protocol/schemas/resources/ApiVersionsResponse.json +79 -0
  259. kafka/protocol/schemas/resources/ConsumerProtocolAssignment.json +42 -0
  260. kafka/protocol/schemas/resources/ConsumerProtocolSubscription.json +49 -0
  261. kafka/protocol/schemas/resources/CreateAclsRequest.json +46 -0
  262. kafka/protocol/schemas/resources/CreateAclsResponse.json +37 -0
  263. kafka/protocol/schemas/resources/CreatePartitionsRequest.json +47 -0
  264. kafka/protocol/schemas/resources/CreatePartitionsResponse.json +41 -0
  265. kafka/protocol/schemas/resources/CreateTopicsRequest.json +65 -0
  266. kafka/protocol/schemas/resources/CreateTopicsResponse.json +72 -0
  267. kafka/protocol/schemas/resources/DeleteAclsRequest.json +46 -0
  268. kafka/protocol/schemas/resources/DeleteAclsResponse.json +59 -0
  269. kafka/protocol/schemas/resources/DeleteGroupsRequest.json +30 -0
  270. kafka/protocol/schemas/resources/DeleteGroupsResponse.json +36 -0
  271. kafka/protocol/schemas/resources/DeleteRecordsRequest.json +42 -0
  272. kafka/protocol/schemas/resources/DeleteRecordsResponse.json +43 -0
  273. kafka/protocol/schemas/resources/DeleteTopicsRequest.json +43 -0
  274. kafka/protocol/schemas/resources/DeleteTopicsResponse.json +52 -0
  275. kafka/protocol/schemas/resources/DescribeAclsRequest.json +43 -0
  276. kafka/protocol/schemas/resources/DescribeAclsResponse.json +55 -0
  277. kafka/protocol/schemas/resources/DescribeClientQuotasRequest.json +37 -0
  278. kafka/protocol/schemas/resources/DescribeClientQuotasResponse.json +47 -0
  279. kafka/protocol/schemas/resources/DescribeClusterRequest.json +35 -0
  280. kafka/protocol/schemas/resources/DescribeClusterResponse.json +56 -0
  281. kafka/protocol/schemas/resources/DescribeConfigsRequest.json +42 -0
  282. kafka/protocol/schemas/resources/DescribeConfigsResponse.json +69 -0
  283. kafka/protocol/schemas/resources/DescribeGroupsRequest.json +38 -0
  284. kafka/protocol/schemas/resources/DescribeGroupsResponse.json +74 -0
  285. kafka/protocol/schemas/resources/DescribeLogDirsRequest.json +38 -0
  286. kafka/protocol/schemas/resources/DescribeLogDirsResponse.json +65 -0
  287. kafka/protocol/schemas/resources/DescribeProducersRequest.json +32 -0
  288. kafka/protocol/schemas/resources/DescribeProducersResponse.json +55 -0
  289. kafka/protocol/schemas/resources/DescribeQuorumRequest.json +39 -0
  290. kafka/protocol/schemas/resources/DescribeQuorumResponse.json +82 -0
  291. kafka/protocol/schemas/resources/DescribeTopicPartitionsRequest.json +40 -0
  292. kafka/protocol/schemas/resources/DescribeTopicPartitionsResponse.json +66 -0
  293. kafka/protocol/schemas/resources/DescribeTransactionsRequest.json +27 -0
  294. kafka/protocol/schemas/resources/DescribeTransactionsResponse.json +52 -0
  295. kafka/protocol/schemas/resources/DescribeUserScramCredentialsRequest.json +30 -0
  296. kafka/protocol/schemas/resources/DescribeUserScramCredentialsResponse.json +45 -0
  297. kafka/protocol/schemas/resources/ElectLeadersRequest.json +41 -0
  298. kafka/protocol/schemas/resources/ElectLeadersResponse.json +45 -0
  299. kafka/protocol/schemas/resources/EndTxnRequest.json +43 -0
  300. kafka/protocol/schemas/resources/EndTxnResponse.json +41 -0
  301. kafka/protocol/schemas/resources/FetchRequest.json +125 -0
  302. kafka/protocol/schemas/resources/FetchResponse.json +124 -0
  303. kafka/protocol/schemas/resources/FindCoordinatorRequest.json +43 -0
  304. kafka/protocol/schemas/resources/FindCoordinatorResponse.json +58 -0
  305. kafka/protocol/schemas/resources/HeartbeatRequest.json +39 -0
  306. kafka/protocol/schemas/resources/HeartbeatResponse.json +35 -0
  307. kafka/protocol/schemas/resources/IncrementalAlterConfigsRequest.json +44 -0
  308. kafka/protocol/schemas/resources/IncrementalAlterConfigsResponse.json +38 -0
  309. kafka/protocol/schemas/resources/InitProducerIdRequest.json +50 -0
  310. kafka/protocol/schemas/resources/InitProducerIdResponse.json +47 -0
  311. kafka/protocol/schemas/resources/JoinGroupRequest.json +63 -0
  312. kafka/protocol/schemas/resources/JoinGroupResponse.json +69 -0
  313. kafka/protocol/schemas/resources/LeaveGroupRequest.json +47 -0
  314. kafka/protocol/schemas/resources/LeaveGroupResponse.json +47 -0
  315. kafka/protocol/schemas/resources/ListConfigResourcesRequest.json +31 -0
  316. kafka/protocol/schemas/resources/ListConfigResourcesResponse.json +37 -0
  317. kafka/protocol/schemas/resources/ListGroupsRequest.json +36 -0
  318. kafka/protocol/schemas/resources/ListGroupsResponse.json +49 -0
  319. kafka/protocol/schemas/resources/ListOffsetsRequest.json +72 -0
  320. kafka/protocol/schemas/resources/ListOffsetsResponse.json +71 -0
  321. kafka/protocol/schemas/resources/ListPartitionReassignmentsRequest.json +34 -0
  322. kafka/protocol/schemas/resources/ListPartitionReassignmentsResponse.json +46 -0
  323. kafka/protocol/schemas/resources/ListTransactionsRequest.json +40 -0
  324. kafka/protocol/schemas/resources/ListTransactionsResponse.json +42 -0
  325. kafka/protocol/schemas/resources/MetadataRequest.json +56 -0
  326. kafka/protocol/schemas/resources/MetadataResponse.json +101 -0
  327. kafka/protocol/schemas/resources/OffsetCommitRequest.json +76 -0
  328. kafka/protocol/schemas/resources/OffsetCommitResponse.json +71 -0
  329. kafka/protocol/schemas/resources/OffsetDeleteRequest.json +39 -0
  330. kafka/protocol/schemas/resources/OffsetDeleteResponse.json +42 -0
  331. kafka/protocol/schemas/resources/OffsetFetchRequest.json +76 -0
  332. kafka/protocol/schemas/resources/OffsetFetchResponse.json +107 -0
  333. kafka/protocol/schemas/resources/OffsetForLeaderEpochRequest.json +52 -0
  334. kafka/protocol/schemas/resources/OffsetForLeaderEpochResponse.json +51 -0
  335. kafka/protocol/schemas/resources/ProduceRequest.json +73 -0
  336. kafka/protocol/schemas/resources/ProduceResponse.json +96 -0
  337. kafka/protocol/schemas/resources/RequestHeader.json +44 -0
  338. kafka/protocol/schemas/resources/ResponseHeader.json +26 -0
  339. kafka/protocol/schemas/resources/SaslAuthenticateRequest.json +29 -0
  340. kafka/protocol/schemas/resources/SaslAuthenticateResponse.json +34 -0
  341. kafka/protocol/schemas/resources/SaslHandshakeRequest.json +31 -0
  342. kafka/protocol/schemas/resources/SaslHandshakeResponse.json +32 -0
  343. kafka/protocol/schemas/resources/SyncGroupRequest.json +56 -0
  344. kafka/protocol/schemas/resources/SyncGroupResponse.json +46 -0
  345. kafka/protocol/schemas/resources/TxnOffsetCommitRequest.json +68 -0
  346. kafka/protocol/schemas/resources/TxnOffsetCommitResponse.json +47 -0
  347. kafka/protocol/schemas/resources/UpdateFeaturesRequest.json +43 -0
  348. kafka/protocol/schemas/resources/UpdateFeaturesResponse.json +39 -0
  349. kafka/protocol/schemas/resources/WriteTxnMarkersRequest.json +49 -0
  350. kafka/protocol/schemas/resources/WriteTxnMarkersResponse.json +45 -0
  351. kafka/protocol/schemas/resources/__init__.py +0 -0
  352. kafka/record/__init__.py +3 -0
  353. kafka/record/_crc32c.py +161 -0
  354. kafka/record/abc.py +144 -0
  355. kafka/record/default_records.py +782 -0
  356. kafka/record/legacy_records.py +587 -0
  357. kafka/record/memory_records.py +255 -0
  358. kafka/record/util.py +135 -0
  359. kafka/serializer/__init__.py +4 -0
  360. kafka/serializer/abstract.py +20 -0
  361. kafka/serializer/default.py +16 -0
  362. kafka/serializer/json.py +17 -0
  363. kafka/serializer/wrapper.py +21 -0
  364. kafka/structs.py +69 -0
  365. kafka/util.py +159 -0
  366. kafka/vendor/__init__.py +0 -0
  367. kafka/version.py +1 -0
  368. kafka_python-3.0.0.dist-info/METADATA +319 -0
  369. kafka_python-3.0.0.dist-info/RECORD +373 -0
  370. kafka_python-3.0.0.dist-info/WHEEL +5 -0
  371. kafka_python-3.0.0.dist-info/entry_points.txt +2 -0
  372. kafka_python-3.0.0.dist-info/licenses/LICENSE +202 -0
  373. kafka_python-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,261 @@
1
+ import logging
2
+ import sys
3
+ import time
4
+ import threading
5
+
6
+ from kafka.metrics import AnonMeasurable, KafkaMetric, MetricConfig, MetricName
7
+ from kafka.metrics.stats import Sensor
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class Metrics:
13
+ """
14
+ A registry of sensors and metrics.
15
+
16
+ A metric is a named, numerical measurement. A sensor is a handle to
17
+ record numerical measurements as they occur. Each Sensor has zero or
18
+ more associated metrics. For example a Sensor might represent message
19
+ sizes and we might associate with this sensor a metric for the average,
20
+ maximum, or other statistics computed off the sequence of message sizes
21
+ that are recorded by the sensor.
22
+
23
+ Usage looks something like this:
24
+ # set up metrics:
25
+ metrics = Metrics() # the global repository of metrics and sensors
26
+ sensor = metrics.sensor('message-sizes')
27
+ metric_name = MetricName('message-size-avg', 'producer-metrics')
28
+ sensor.add(metric_name, Avg())
29
+ metric_name = MetricName('message-size-max', 'producer-metrics')
30
+ sensor.add(metric_name, Max())
31
+
32
+ # as messages are sent we record the sizes
33
+ sensor.record(message_size);
34
+ """
35
+ def __init__(self, default_config=None, reporters=None,
36
+ enable_expiration=False):
37
+ """
38
+ Create a metrics repository with a default config, given metric
39
+ reporters and the ability to expire eligible sensors
40
+
41
+ Arguments:
42
+ default_config (MetricConfig, optional): The default config
43
+ reporters (list of AbstractMetricsReporter, optional):
44
+ The metrics reporters
45
+ enable_expiration (bool, optional): true if the metrics instance
46
+ can garbage collect inactive sensors, false otherwise
47
+ """
48
+ self._lock = threading.RLock()
49
+ self._config = default_config or MetricConfig()
50
+ self._sensors = {}
51
+ self._metrics = {}
52
+ self._children_sensors = {}
53
+ self._reporters = reporters or []
54
+ for reporter in self._reporters:
55
+ reporter.init([])
56
+ self._closed = False
57
+
58
+ if enable_expiration:
59
+ def expire_loop():
60
+ while not self._closed:
61
+ # delay 30 seconds
62
+ time.sleep(30)
63
+ self.ExpireSensorTask.run(self)
64
+ metrics_scheduler = threading.Thread(target=expire_loop)
65
+ # Creating a daemon thread to not block shutdown
66
+ metrics_scheduler.daemon = True
67
+ metrics_scheduler.start()
68
+
69
+ self.add_metric(self.metric_name('count', 'kafka-metrics-count',
70
+ 'total number of registered metrics'),
71
+ AnonMeasurable(lambda config, now: len(self._metrics)))
72
+
73
+ @property
74
+ def config(self):
75
+ return self._config
76
+
77
+ @property
78
+ def metrics(self):
79
+ """
80
+ Get all the metrics currently maintained and indexed by metricName
81
+ """
82
+ return self._metrics
83
+
84
+ def metric_name(self, name, group, description='', tags=None):
85
+ """
86
+ Create a MetricName with the given name, group, description and tags,
87
+ plus default tags specified in the metric configuration.
88
+ Tag in tags takes precedence if the same tag key is specified in
89
+ the default metric configuration.
90
+
91
+ Arguments:
92
+ name (str): The name of the metric
93
+ group (str): logical group name of the metrics to which this
94
+ metric belongs
95
+ description (str, optional): A human-readable description to
96
+ include in the metric
97
+ tags (dict, optionals): additional key/value attributes of
98
+ the metric
99
+ """
100
+ combined_tags = dict(self.config.tags)
101
+ combined_tags.update(tags or {})
102
+ return MetricName(name, group, description, combined_tags)
103
+
104
+ def get_sensor(self, name):
105
+ """
106
+ Get the sensor with the given name if it exists
107
+
108
+ Arguments:
109
+ name (str): The name of the sensor
110
+
111
+ Returns:
112
+ Sensor: The sensor or None if no such sensor exists
113
+ """
114
+ if not name:
115
+ raise ValueError('name must be non-empty')
116
+ return self._sensors.get(name, None)
117
+
118
+ def sensor(self, name, config=None,
119
+ inactive_sensor_expiration_time_seconds=sys.maxsize,
120
+ parents=None):
121
+ """
122
+ Get or create a sensor with the given unique name and zero or
123
+ more parent sensors. All parent sensors will receive every value
124
+ recorded with this sensor.
125
+
126
+ Arguments:
127
+ name (str): The name of the sensor
128
+ config (MetricConfig, optional): A default configuration to use
129
+ for this sensor for metrics that don't have their own config
130
+ inactive_sensor_expiration_time_seconds (int, optional):
131
+ If no value if recorded on the Sensor for this duration of
132
+ time, it is eligible for removal
133
+ parents (list of Sensor): The parent sensors
134
+
135
+ Returns:
136
+ Sensor: The sensor that is created
137
+ """
138
+ sensor = self.get_sensor(name)
139
+ if sensor:
140
+ return sensor
141
+
142
+ with self._lock:
143
+ sensor = self.get_sensor(name)
144
+ if not sensor:
145
+ sensor = Sensor(self, name, parents, config or self.config,
146
+ inactive_sensor_expiration_time_seconds)
147
+ self._sensors[name] = sensor
148
+ if parents:
149
+ for parent in parents:
150
+ children = self._children_sensors.get(parent)
151
+ if not children:
152
+ children = []
153
+ self._children_sensors[parent] = children
154
+ children.append(sensor)
155
+ logger.debug('Added sensor with name %s', name)
156
+ return sensor
157
+
158
+ def remove_sensor(self, name):
159
+ """
160
+ Remove a sensor (if it exists), associated metrics and its children.
161
+
162
+ Arguments:
163
+ name (str): The name of the sensor to be removed
164
+ """
165
+ with self._lock:
166
+ sensor = self._sensors.get(name)
167
+ if sensor:
168
+ child_sensors = None
169
+ with sensor._lock:
170
+ val = self._sensors.pop(name, None)
171
+ if val and val == sensor:
172
+ for metric in sensor.metrics:
173
+ self.remove_metric(metric.metric_name)
174
+ logger.debug('Removed sensor with name %s', name)
175
+ child_sensors = self._children_sensors.pop(sensor, None)
176
+ if child_sensors:
177
+ for child_sensor in child_sensors:
178
+ self.remove_sensor(child_sensor.name)
179
+
180
+ def add_metric(self, metric_name, measurable, config=None):
181
+ """
182
+ Add a metric to monitor an object that implements measurable.
183
+ This metric won't be associated with any sensor.
184
+ This is a way to expose existing values as metrics.
185
+
186
+ Arguments:
187
+ metricName (MetricName): The name of the metric
188
+ measurable (AbstractMeasurable): The measurable that will be
189
+ measured by this metric
190
+ config (MetricConfig, optional): The configuration to use when
191
+ measuring this measurable
192
+ """
193
+ # NOTE there was a lock here, but i don't think it's needed
194
+ metric = KafkaMetric(metric_name, measurable, config or self.config)
195
+ self.register_metric(metric)
196
+
197
+ def remove_metric(self, metric_name):
198
+ """
199
+ Remove a metric if it exists and return it. Return None otherwise.
200
+ If a metric is removed, `metric_removal` will be invoked
201
+ for each reporter.
202
+
203
+ Arguments:
204
+ metric_name (MetricName): The name of the metric
205
+
206
+ Returns:
207
+ KafkaMetric: the removed `KafkaMetric` or None if no such
208
+ metric exists
209
+ """
210
+ with self._lock:
211
+ metric = self._metrics.pop(metric_name, None)
212
+ if metric:
213
+ for reporter in self._reporters:
214
+ reporter.metric_removal(metric)
215
+ return metric
216
+
217
+ def add_reporter(self, reporter):
218
+ """Add a MetricReporter"""
219
+ with self._lock:
220
+ reporter.init(list(self.metrics.values()))
221
+ self._reporters.append(reporter)
222
+
223
+ def register_metric(self, metric):
224
+ with self._lock:
225
+ if metric.metric_name in self.metrics:
226
+ raise ValueError('A metric named "%s" already exists, cannot'
227
+ ' register another one.' % (metric.metric_name,))
228
+ self.metrics[metric.metric_name] = metric
229
+ for reporter in self._reporters:
230
+ reporter.metric_change(metric)
231
+
232
+ class ExpireSensorTask:
233
+ """
234
+ This iterates over every Sensor and triggers a remove_sensor
235
+ if it has expired. Package private for testing
236
+ """
237
+ @staticmethod
238
+ def run(metrics):
239
+ items = list(metrics._sensors.items())
240
+ for name, sensor in items:
241
+ # remove_sensor also locks the sensor object. This is fine
242
+ # because synchronized is reentrant. There is however a minor
243
+ # race condition here. Assume we have a parent sensor P and
244
+ # child sensor C. Calling record on C would cause a record on
245
+ # P as well. So expiration time for P == expiration time for C.
246
+ # If the record on P happens via C just after P is removed,
247
+ # that will cause C to also get removed. Since the expiration
248
+ # time is typically high it is not expected to be a significant
249
+ # concern and thus not necessary to optimize
250
+ with sensor._lock:
251
+ if sensor.has_expired():
252
+ logger.debug('Removing expired sensor %s', name)
253
+ metrics.remove_sensor(name)
254
+
255
+ def close(self):
256
+ """Close this metrics repository."""
257
+ for reporter in self._reporters:
258
+ reporter.close()
259
+
260
+ self._metrics.clear()
261
+ self._closed = True
@@ -0,0 +1,53 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class AbstractMetricsReporter(ABC):
5
+ """
6
+ An abstract class to allow things to listen as new metrics
7
+ are created so they can be reported.
8
+ """
9
+ @abstractmethod
10
+ def init(self, metrics):
11
+ """
12
+ This is called when the reporter is first registered
13
+ to initially register all existing metrics
14
+
15
+ Arguments:
16
+ metrics (list of KafkaMetric): All currently existing metrics
17
+ """
18
+ pass
19
+
20
+ @abstractmethod
21
+ def metric_change(self, metric):
22
+ """
23
+ This is called whenever a metric is updated or added
24
+
25
+ Arguments:
26
+ metric (KafkaMetric)
27
+ """
28
+ pass
29
+
30
+ @abstractmethod
31
+ def metric_removal(self, metric):
32
+ """
33
+ This is called whenever a metric is removed
34
+
35
+ Arguments:
36
+ metric (KafkaMetric)
37
+ """
38
+ pass
39
+
40
+ @abstractmethod
41
+ def configure(self, configs):
42
+ """
43
+ Configure this class with the given key-value pairs
44
+
45
+ Arguments:
46
+ configs (dict of {str, ?})
47
+ """
48
+ pass
49
+
50
+ @abstractmethod
51
+ def close(self):
52
+ """Called when the metrics repository is closed."""
53
+ pass
kafka/metrics/quota.py ADDED
@@ -0,0 +1,41 @@
1
+ class Quota:
2
+ """An upper or lower bound for metrics"""
3
+ __slots__ = ('_bound', '_upper')
4
+
5
+ def __init__(self, bound, is_upper):
6
+ self._bound = bound
7
+ self._upper = is_upper
8
+
9
+ @staticmethod
10
+ def upper_bound(upper_bound):
11
+ return Quota(upper_bound, True)
12
+
13
+ @staticmethod
14
+ def lower_bound(lower_bound):
15
+ return Quota(lower_bound, False)
16
+
17
+ def is_upper_bound(self):
18
+ return self._upper
19
+
20
+ @property
21
+ def bound(self):
22
+ return self._bound
23
+
24
+ def is_acceptable(self, value):
25
+ return ((self.is_upper_bound() and value <= self.bound) or
26
+ (not self.is_upper_bound() and value >= self.bound))
27
+
28
+ def __hash__(self):
29
+ prime = 31
30
+ result = prime + self.bound
31
+ return prime * result + self.is_upper_bound()
32
+
33
+ def __eq__(self, other):
34
+ if self is other:
35
+ return True
36
+ return (isinstance(self, type(other)) and
37
+ self.bound == other.bound and
38
+ self.is_upper_bound() == other.is_upper_bound())
39
+
40
+ def __ne__(self, other):
41
+ return not self.__eq__(other)
kafka/metrics/stat.py ADDED
@@ -0,0 +1,19 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class AbstractStat(ABC):
5
+ """
6
+ An AbstractStat is a quantity such as average, max, etc that is computed
7
+ off the stream of updates to a sensor
8
+ """
9
+ @abstractmethod
10
+ def record(self, config, value, time_ms):
11
+ """
12
+ Record the given value
13
+
14
+ Arguments:
15
+ config (MetricConfig): The configuration to use for this metric
16
+ value (float): The value to record
17
+ timeMs (int): The POSIX time in milliseconds this value occurred
18
+ """
19
+ pass
@@ -0,0 +1,15 @@
1
+ from kafka.metrics.stats.avg import Avg
2
+ from kafka.metrics.stats.count import Count
3
+ from kafka.metrics.stats.histogram import Histogram
4
+ from kafka.metrics.stats.max_stat import Max
5
+ from kafka.metrics.stats.min_stat import Min
6
+ from kafka.metrics.stats.percentile import Percentile
7
+ from kafka.metrics.stats.percentiles import Percentiles
8
+ from kafka.metrics.stats.rate import Rate
9
+ from kafka.metrics.stats.sensor import Sensor
10
+ from kafka.metrics.stats.total import Total
11
+
12
+ __all__ = [
13
+ 'Avg', 'Count', 'Histogram', 'Max', 'Min', 'Percentile', 'Percentiles',
14
+ 'Rate', 'Sensor', 'Total'
15
+ ]
@@ -0,0 +1,24 @@
1
+ from kafka.metrics.stats.sampled_stat import AbstractSampledStat
2
+
3
+
4
+ class Avg(AbstractSampledStat):
5
+ """
6
+ An AbstractSampledStat that maintains a simple average over its samples.
7
+ """
8
+ __slots__ = ('_initial_value', '_samples', '_current')
9
+
10
+ def __init__(self):
11
+ super().__init__(0.0)
12
+
13
+ def update(self, sample, config, value, now):
14
+ sample.value += value
15
+
16
+ def combine(self, samples, config, now):
17
+ total_sum = 0
18
+ total_count = 0
19
+ for sample in samples:
20
+ total_sum += sample.value
21
+ total_count += sample.event_count
22
+ if not total_count:
23
+ return 0
24
+ return float(total_sum) / total_count
@@ -0,0 +1,17 @@
1
+ from kafka.metrics.stats.sampled_stat import AbstractSampledStat
2
+
3
+
4
+ class Count(AbstractSampledStat):
5
+ """
6
+ An AbstractSampledStat that maintains a simple count of what it has seen.
7
+ """
8
+ __slots__ = ('_initial_value', '_samples', '_current')
9
+
10
+ def __init__(self):
11
+ super().__init__(0.0)
12
+
13
+ def update(self, sample, config, value, now):
14
+ sample.value += 1.0
15
+
16
+ def combine(self, samples, config, now):
17
+ return float(sum(sample.value for sample in samples))
@@ -0,0 +1,99 @@
1
+ import math
2
+
3
+
4
+ class Histogram:
5
+ __slots__ = ('_hist', '_count', '_bin_scheme')
6
+
7
+ def __init__(self, bin_scheme):
8
+ self._hist = [0.0] * bin_scheme.bins
9
+ self._count = 0.0
10
+ self._bin_scheme = bin_scheme
11
+
12
+ def record(self, value):
13
+ self._hist[self._bin_scheme.to_bin(value)] += 1.0
14
+ self._count += 1.0
15
+
16
+ def value(self, quantile):
17
+ if self._count == 0.0:
18
+ return float('NaN')
19
+ _sum = 0.0
20
+ quant = float(quantile)
21
+ for i, value in enumerate(self._hist[:-1]):
22
+ _sum += value
23
+ if _sum / self._count > quant:
24
+ return self._bin_scheme.from_bin(i)
25
+ return float('inf')
26
+
27
+ @property
28
+ def counts(self):
29
+ return self._hist
30
+
31
+ def clear(self):
32
+ for i in range(len(self._hist)):
33
+ self._hist[i] = 0.0
34
+ self._count = 0
35
+
36
+ def __str__(self):
37
+ values = ['%.10f:%.0f' % (self._bin_scheme.from_bin(i), value) for
38
+ i, value in enumerate(self._hist[:-1])]
39
+ values.append('%s:%s' % (float('inf'), self._hist[-1]))
40
+ return '{%s}' % ','.join(values)
41
+
42
+ class ConstantBinScheme:
43
+ __slots__ = ('_min', '_max', '_bins', '_bucket_width')
44
+
45
+ def __init__(self, bins, min_val, max_val):
46
+ if bins < 2:
47
+ raise ValueError('Must have at least 2 bins.')
48
+ self._min = float(min_val)
49
+ self._max = float(max_val)
50
+ self._bins = int(bins)
51
+ self._bucket_width = (max_val - min_val) / (bins - 2)
52
+
53
+ @property
54
+ def bins(self):
55
+ return self._bins
56
+
57
+ def from_bin(self, b):
58
+ if b == 0:
59
+ return float('-inf')
60
+ elif b == self._bins - 1:
61
+ return float('inf')
62
+ else:
63
+ return self._min + (b - 1) * self._bucket_width
64
+
65
+ def to_bin(self, x):
66
+ if x < self._min:
67
+ return 0
68
+ elif x > self._max:
69
+ return self._bins - 1
70
+ else:
71
+ return int(((x - self._min) / self._bucket_width) + 1)
72
+
73
+ class LinearBinScheme:
74
+ __slots__ = ('_bins', '_max', '_scale')
75
+
76
+ def __init__(self, num_bins, max_val):
77
+ self._bins = num_bins
78
+ self._max = max_val
79
+ self._scale = max_val / (num_bins * (num_bins - 1) / 2)
80
+
81
+ @property
82
+ def bins(self):
83
+ return self._bins
84
+
85
+ def from_bin(self, b):
86
+ if b == self._bins - 1:
87
+ return float('inf')
88
+ else:
89
+ unscaled = (b * (b + 1.0)) / 2.0
90
+ return unscaled * self._scale
91
+
92
+ def to_bin(self, x):
93
+ if x < 0.0:
94
+ raise ValueError('Values less than 0.0 not accepted.')
95
+ elif x > self._max:
96
+ return self._bins - 1
97
+ else:
98
+ scaled = x / self._scale
99
+ return int(-0.5 + math.sqrt(2.0 * scaled + 0.25))
@@ -0,0 +1,17 @@
1
+ from kafka.metrics.stats.sampled_stat import AbstractSampledStat
2
+
3
+
4
+ class Max(AbstractSampledStat):
5
+ """An AbstractSampledStat that gives the max over its samples."""
6
+ __slots__ = ('_initial_value', '_samples', '_current')
7
+
8
+ def __init__(self):
9
+ super().__init__(float('-inf'))
10
+
11
+ def update(self, sample, config, value, now):
12
+ sample.value = max(sample.value, value)
13
+
14
+ def combine(self, samples, config, now):
15
+ if not samples:
16
+ return float('-inf')
17
+ return float(max(sample.value for sample in samples))
@@ -0,0 +1,19 @@
1
+ import sys
2
+
3
+ from kafka.metrics.stats.sampled_stat import AbstractSampledStat
4
+
5
+
6
+ class Min(AbstractSampledStat):
7
+ """An AbstractSampledStat that gives the min over its samples."""
8
+ __slots__ = ('_initial_value', '_samples', '_current')
9
+
10
+ def __init__(self):
11
+ super().__init__(float(sys.maxsize))
12
+
13
+ def update(self, sample, config, value, now):
14
+ sample.value = min(sample.value, value)
15
+
16
+ def combine(self, samples, config, now):
17
+ if not samples:
18
+ return float(sys.maxsize)
19
+ return float(min(sample.value for sample in samples))
@@ -0,0 +1,14 @@
1
+ class Percentile:
2
+ __slots__ = ('_metric_name', '_percentile')
3
+
4
+ def __init__(self, metric_name, percentile):
5
+ self._metric_name = metric_name
6
+ self._percentile = float(percentile)
7
+
8
+ @property
9
+ def name(self):
10
+ return self._metric_name
11
+
12
+ @property
13
+ def percentile(self):
14
+ return self._percentile
@@ -0,0 +1,75 @@
1
+ from kafka.metrics import AnonMeasurable, NamedMeasurable
2
+ from kafka.metrics.compound_stat import AbstractCompoundStat
3
+ from kafka.metrics.stats import Histogram
4
+ from kafka.metrics.stats.sampled_stat import AbstractSampledStat
5
+
6
+
7
+ class BucketSizing:
8
+ CONSTANT = 0
9
+ LINEAR = 1
10
+
11
+
12
+ class Percentiles(AbstractSampledStat, AbstractCompoundStat):
13
+ """A compound stat that reports one or more percentiles"""
14
+ __slots__ = ('_initial_value', '_samples', '_current',
15
+ '_percentiles', '_buckets', '_bin_scheme')
16
+
17
+ def __init__(self, size_in_bytes, bucketing, max_val, min_val=0.0,
18
+ percentiles=None):
19
+ super().__init__(0.0)
20
+ self._percentiles = percentiles or []
21
+ self._buckets = int(size_in_bytes / 4)
22
+ if bucketing == BucketSizing.CONSTANT:
23
+ self._bin_scheme = Histogram.ConstantBinScheme(self._buckets,
24
+ min_val, max_val)
25
+ elif bucketing == BucketSizing.LINEAR:
26
+ if min_val != 0.0:
27
+ raise ValueError('Linear bucket sizing requires min_val'
28
+ ' to be 0.0.')
29
+ self._bin_scheme = Histogram.LinearBinScheme(self._buckets, max_val)
30
+ else:
31
+ raise ValueError('Unknown bucket type: %s' % (bucketing,))
32
+
33
+ def stats(self):
34
+ measurables = []
35
+
36
+ def make_measure_fn(pct):
37
+ return lambda config, now: self.value(config, now,
38
+ pct / 100.0)
39
+
40
+ for percentile in self._percentiles:
41
+ measure_fn = make_measure_fn(percentile.percentile)
42
+ stat = NamedMeasurable(percentile.name, AnonMeasurable(measure_fn))
43
+ measurables.append(stat)
44
+ return measurables
45
+
46
+ def value(self, config, now, quantile):
47
+ self.purge_obsolete_samples(config, now)
48
+ count = sum(sample.event_count for sample in self._samples)
49
+ if count == 0.0:
50
+ return float('NaN')
51
+ sum_val = 0.0
52
+ quant = float(quantile)
53
+ for b in range(self._buckets):
54
+ for sample in self._samples:
55
+ assert type(sample) is self.HistogramSample
56
+ hist = sample.histogram.counts
57
+ sum_val += hist[b]
58
+ if sum_val / count > quant:
59
+ return self._bin_scheme.from_bin(b)
60
+ return float('inf')
61
+
62
+ def combine(self, samples, config, now):
63
+ return self.value(config, now, 0.5)
64
+
65
+ def new_sample(self, time_ms):
66
+ return Percentiles.HistogramSample(self._bin_scheme, time_ms)
67
+
68
+ def update(self, sample, config, value, time_ms):
69
+ assert type(sample) is self.HistogramSample
70
+ sample.histogram.record(value)
71
+
72
+ class HistogramSample(AbstractSampledStat.Sample):
73
+ def __init__(self, scheme, now):
74
+ super().__init__(0.0, now)
75
+ self.histogram = Histogram(scheme)