python-openobserve 0.4.2__tar.gz → 0.5.1__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-openobserve
3
- Version: 0.4.2
3
+ Version: 0.5.1
4
4
  Summary: OpenObserve client for Python
5
5
  License-File: LICENSES/CC-BY-4.0.txt
6
6
  License-File: LICENSES/CC-BY-SA-4.0.txt
@@ -8,19 +8,20 @@ License-File: LICENSES/GPL-3.0-or-later.txt
8
8
  Keywords: openobserve,elasticsearch,logging,unstructured data
9
9
  Author: Justin Guese
10
10
  Author-email: guese.justin@gmail.com
11
- Requires-Python: >=3.10,<3.14
11
+ Requires-Python: >=3.10,<3.15
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Provides-Extra: ci
14
14
  Provides-Extra: docs
15
- Provides-Extra: fireducks
16
15
  Provides-Extra: pandas
17
16
  Provides-Extra: polars
18
17
  Provides-Extra: tests
19
- Requires-Dist: fireducks (==1.4.4) ; extra == "fireducks"
20
- Requires-Dist: guarddog (==2.6.0) ; extra == "ci"
18
+ Requires-Dist: cryptography (>=48.0.1,<48.1.0)
19
+ Requires-Dist: h11 (>=0.16.0,<0.17.0)
20
+ Requires-Dist: httpcore (>=1.0.9,<1.1.0)
21
+ Requires-Dist: httpx (>=0.28,<1.0)
21
22
  Requires-Dist: jmespath (==1.0.1) ; extra == "ci"
22
23
  Requires-Dist: jmespath (==1.0.1) ; extra == "tests"
23
- Requires-Dist: memray (==1.17.2) ; extra == "ci"
24
+ Requires-Dist: memray (==1.19.3) ; extra == "ci"
24
25
  Requires-Dist: mkdocs (==1.6.1) ; extra == "docs"
25
26
  Requires-Dist: mkdocs-material (==9.6.15) ; extra == "docs"
26
27
  Requires-Dist: mkdocstrings (==0.29.1) ; extra == "docs"
@@ -30,17 +31,19 @@ Requires-Dist: pandas (==2.2.3) ; extra == "ci"
30
31
  Requires-Dist: pandas (>=2.2) ; extra == "pandas"
31
32
  Requires-Dist: pandas-stubs (==2.2.2.240807) ; extra == "ci"
32
33
  Requires-Dist: polars (==1.30.0) ; extra == "polars"
34
+ Requires-Dist: protobuf (>=6.33.5)
33
35
  Requires-Dist: pydantic (>=2.10,<3.0)
34
- Requires-Dist: pytest (==8.3.5) ; extra == "ci"
35
- Requires-Dist: pytest (==8.3.5) ; extra == "tests"
36
+ Requires-Dist: pyjwt (>=2.12.1,<3.0.0)
37
+ Requires-Dist: pytest (==9.0.3) ; extra == "ci"
38
+ Requires-Dist: pytest (==9.0.3) ; extra == "tests"
36
39
  Requires-Dist: pytest-cov (==6.0.0) ; extra == "ci"
37
40
  Requires-Dist: pytest-memray (==1.7.0) ; extra == "ci"
38
- Requires-Dist: python-dotenv (==1.0.1) ; extra == "ci"
39
- Requires-Dist: python-dotenv (==1.0.1) ; extra == "tests"
40
- Requires-Dist: requests (>=2.32.4,<2.33.0)
41
+ Requires-Dist: python-dotenv (==1.2.2) ; extra == "ci"
42
+ Requires-Dist: python-dotenv (==1.2.2) ; extra == "tests"
43
+ Requires-Dist: python-multipart (>=0.0.22)
44
+ Requires-Dist: requests (>=2.33.0,<3.0.0)
41
45
  Requires-Dist: sqlglot (>=26.4.0,<26.5.0)
42
46
  Requires-Dist: types-jmespath (==1.0.2.20240106) ; extra == "ci"
43
- Requires-Dist: types-requests (==2.32.0.20241016) ; extra == "ci"
44
47
  Project-URL: Homepage, https://github.com/JustinGuese/python-openobserve
45
48
  Project-URL: Repository, https://github.com/JustinGuese/python-openobserve
46
49
  Description-Content-Type: text/markdown
@@ -54,6 +57,7 @@ Description-Content-Type: text/markdown
54
57
 
55
58
  [![Actions Status - Main](https://github.com/JustinGuese/python-openobserve/workflows/pytest/badge.svg)](https://github.com/JustinGuese/python-openobserve/actions?query=branch%3Amain)
56
59
  [![PyPI Downloads](https://static.pepy.tech/badge/python-openobserve/week)](https://pepy.tech/projects/python-openobserve)
60
+ <!-- markdown-link-check-disable-next-line -->
57
61
  [![Documentation build status](https://img.shields.io/readthedocs/python-openobserve?logo=read-the-docs)](https://python-openobserve.readthedocs.org/en/latest/)
58
62
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/JustinGuese/python-openobserve/badge)](https://scorecard.dev/viewer/?uri=github.com/JustinGuese/python-openobserve)
59
63
 
@@ -7,6 +7,7 @@
7
7
 
8
8
  [![Actions Status - Main](https://github.com/JustinGuese/python-openobserve/workflows/pytest/badge.svg)](https://github.com/JustinGuese/python-openobserve/actions?query=branch%3Amain)
9
9
  [![PyPI Downloads](https://static.pepy.tech/badge/python-openobserve/week)](https://pepy.tech/projects/python-openobserve)
10
+ <!-- markdown-link-check-disable-next-line -->
10
11
  [![Documentation build status](https://img.shields.io/readthedocs/python-openobserve?logo=read-the-docs)](https://python-openobserve.readthedocs.org/en/latest/)
11
12
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/JustinGuese/python-openobserve/badge)](https://scorecard.dev/viewer/?uri=github.com/JustinGuese/python-openobserve)
12
13
 
@@ -3,10 +3,10 @@
3
3
 
4
4
  [project]
5
5
  name = "python-openobserve"
6
- version = "0.4.2"
6
+ version = "0.5.1"
7
7
  description = "OpenObserve client for Python"
8
8
  readme = "README.md"
9
- requires-python = ">=3.10,<3.14"
9
+ requires-python = ">=3.10,<3.15"
10
10
  license-files = [
11
11
  "LICENSES/CC-BY-4.0.txt",
12
12
  "LICENSES/CC-BY-SA-4.0.txt",
@@ -19,9 +19,16 @@ classifiers = [
19
19
  ]
20
20
 
21
21
  dependencies = [
22
- "requests~=2.32.4",
22
+ "cryptography (>=48.0.1,<48.1.0)",
23
+ "h11~=0.16.0",
24
+ "httpcore~=1.0.9",
25
+ "httpx~=0.28",
26
+ "protobuf>=6.33.5",
23
27
  "pydantic~=2.10",
28
+ "python-multipart>=0.0.22",
24
29
  "sqlglot~=26.4.0",
30
+ "pyjwt (>=2.12.1,<3.0.0)",
31
+ "requests (>=2.33.0,<3.0.0)",
25
32
  ]
26
33
 
27
34
  [project.urls]
@@ -34,33 +41,30 @@ python_openobserve = "python_openobserve:openobserve"
34
41
  [project.optional-dependencies]
35
42
  ci = [
36
43
  "jmespath==1.0.1",
37
- "memray==1.17.2",
44
+ "memray==1.19.3",
38
45
  "pytest-memray==1.7.0",
39
46
  "mypy==1.15.0",
40
47
  "mypy-extensions==1.0.0",
41
48
  "pandas==2.2.3",
42
49
  "pandas-stubs==2.2.2.240807",
43
- "pytest==8.3.5",
50
+ "pytest==9.0.3",
44
51
  "pytest-cov==6.0.0",
45
- "python-dotenv==1.0.1",
52
+ "python-dotenv==1.2.2",
46
53
  "types-jmespath==1.0.2.20240106",
47
- "types-requests==2.32.0.20241016",
48
- "guarddog==2.6.0",
49
54
  ]
50
55
  docs = [
51
56
  "mkdocs==1.6.1",
52
57
  "mkdocs-material==9.6.15",
53
58
  "mkdocstrings==0.29.1",
54
59
  ]
55
- tests = ["pytest==8.3.5", "jmespath==1.0.1", "python-dotenv==1.0.1"]
60
+ tests = ["pytest==9.0.3", "jmespath==1.0.1", "python-dotenv==1.2.2"]
56
61
  pandas = ["pandas>=2.2"]
57
62
  polars = ["polars==1.30.0"]
58
- fireducks = ["fireducks==1.4.4"]
59
63
 
60
64
  [tool.poetry.group.test.dependencies]
61
- pytest = "8.3.5"
65
+ pytest = "9.0.3"
62
66
  jmespath = "1.0.1"
63
- python-dotenv = "1.0.1"
67
+ python-dotenv = "1.2.2"
64
68
 
65
69
  [tool.poetry.group.docs.dependencies]
66
70
  mkdocs = "1.6.1"
@@ -69,27 +73,23 @@ mkdocstrings = "0.29.1"
69
73
 
70
74
  [tool.poetry.group.ci.dependencies]
71
75
  jmespath = "1.0.1"
72
- memray = "1.17.2"
76
+ memray = "1.19.3"
73
77
  pytest-memray = "1.7.0"
74
78
  mypy = "1.15.0"
75
79
  mypy-extensions = "1.0.0"
76
80
  pandas = "2.2.3"
77
81
  openpyxl = "3.1.5"
78
82
  pandas-stubs = "2.2.2.240807"
79
- pytest = "8.3.5"
83
+ pytest = "9.0.3"
80
84
  pytest-cov = "6.0.0"
81
- python-dotenv = "1.0.1"
85
+ python-dotenv = "1.2.2"
82
86
  types-jmespath = "1.0.2.20240106"
83
- types-requests = "2.32.0.20241016"
84
- guarddog = "2.6.0"
85
87
 
86
88
  [tool.poetry.group.pandas.dependencies]
87
89
  pandas = ">2.2"
88
90
 
89
91
  [tool.poetry.group.polars.dependencies]
90
92
  polars = "1.30.0"
91
- [tool.poetry.group.fireducks.dependencies]
92
- fireducks = "1.4.4"
93
93
 
94
94
  [build-system]
95
95
  requires = ["poetry-core"]
@@ -19,7 +19,7 @@ from datetime import datetime
19
19
  from typing import List, Dict, Union, Optional, Any, cast
20
20
  from pathlib import Path
21
21
 
22
- import requests
22
+ import httpx
23
23
  import sqlglot # type: ignore
24
24
 
25
25
  try:
@@ -40,14 +40,6 @@ except ImportError:
40
40
  print("Can't import polars. some functions may be unavailable.")
41
41
  HAVE_MODULE_POLARS = False
42
42
 
43
- try:
44
- import fireducks # type: ignore
45
-
46
- HAVE_MODULE_FIREDUCKS = True
47
- except ImportError:
48
- print("Can't import fireducks. Some functions may be unavailable.")
49
- HAVE_MODULE_FIREDUCKS = False
50
-
51
43
  key_mapping = {
52
44
  "dashboards": "dashboards",
53
45
  "users": "data",
@@ -159,10 +151,10 @@ class OpenObserve:
159
151
  pprint(msg)
160
152
 
161
153
  def _handle_response(
162
- self, res: requests.Response, action: str = "request"
154
+ self, res: httpx.Response, action: str = "request"
163
155
  ) -> List[Dict]:
164
156
  """Handle API response and return JSON if successful"""
165
- if res.status_code != requests.codes.ok:
157
+ if res.status_code != httpx.codes.OK:
166
158
  raise Exception(
167
159
  f"Openobserve {action} returned {res.status_code}. Text: {res.text}"
168
160
  )
@@ -214,7 +206,7 @@ class OpenObserve:
214
206
  assert isinstance(document, dict), "document must be a dict"
215
207
  document = self.__datetime2Str(flatten(document))
216
208
 
217
- res = requests.post(
209
+ res = httpx.post(
218
210
  f"{self.openobserve_url.replace('[STREAM]', index)}/_json",
219
211
  headers=self.headers,
220
212
  json=[document],
@@ -223,10 +215,10 @@ class OpenObserve:
223
215
  )
224
216
  response_json = self._handle_response(res, "index")
225
217
 
226
- if response_json["status"][0]["failed"] > 0: # type:ignore[call-overload]
218
+ if response_json["status"][0]["failed"] > 0: # type: ignore[call-overload]
227
219
  raise Exception(
228
220
  "Openobserve index failed. "
229
- f"{response_json['status'][0]['error']}" # type:ignore[call-overload]
221
+ f"{response_json['status'][0]['error']}" # type: ignore[call-overload]
230
222
  f". document: {document}"
231
223
  )
232
224
  return response_json
@@ -237,6 +229,7 @@ class OpenObserve:
237
229
  *,
238
230
  start_time: Union[datetime, int] = 0,
239
231
  end_time: Union[datetime, int] = 0,
232
+ query_size: int = 1000,
240
233
  verbosity: int = 0,
241
234
  timeout: int = 300,
242
235
  timestamp_conversion_auto: bool = False,
@@ -245,11 +238,14 @@ class OpenObserve:
245
238
  """
246
239
  OpenObserve search function
247
240
  https://openobserve.ai/docs/api/search/search/
241
+ https://github.com/openobserve/openobserve/commit/3ccf0be93391885136377b41a4cc2a36d80f904a
248
242
 
249
243
  Args:
250
244
  sql: input sql query
251
245
  start_time: start of search interval, either datetime, either int/epoch
252
246
  end_time: end of search interval, either datetime, either int/epoch
247
+ query_size: maximum number of results returned (default: 1000)
248
+ See also ZO_QUERY_DEFAULT_LIMIT
253
249
  verbosity: how verbose to run from 0/less to 5/more
254
250
  timeout: http timeout
255
251
  timestamp_conversion_auto: try to convert automatically column containing time as name
@@ -274,10 +270,17 @@ class OpenObserve:
274
270
  except sqlglot.errors.ParseError as e:
275
271
  raise e
276
272
 
277
- query = {"query": {"sql": sql, "start_time": start_time, "end_time": end_time}}
273
+ query = {
274
+ "query": {
275
+ "sql": sql,
276
+ "start_time": start_time,
277
+ "end_time": end_time,
278
+ "size": query_size,
279
+ }
280
+ }
278
281
  self._debug(query, verbosity)
279
282
 
280
- res = requests.post(
283
+ res = httpx.post(
281
284
  f"{self.openobserve_url.replace('/[STREAM]', '')}/_search",
282
285
  json=query,
283
286
  headers=self.headers,
@@ -286,7 +289,7 @@ class OpenObserve:
286
289
  )
287
290
 
288
291
  response_json = self._handle_response(res, "search")
289
- res_hits = response_json["hits"] # type:ignore[call-overload]
292
+ res_hits = response_json["hits"] # type: ignore[call-overload]
290
293
  self._debug(res_hits, verbosity, 3)
291
294
 
292
295
  if timestamp_conversion_auto or timestamp_columns is not None:
@@ -310,7 +313,7 @@ class OpenObserve:
310
313
  self._debug(url, verbosity)
311
314
 
312
315
  if method == "GET":
313
- res = requests.get(
316
+ res = httpx.get(
314
317
  url,
315
318
  headers=self.headers,
316
319
  params=params,
@@ -318,7 +321,7 @@ class OpenObserve:
318
321
  timeout=self.timeout,
319
322
  )
320
323
  elif method == "POST":
321
- res = requests.post(
324
+ res = httpx.post(
322
325
  url,
323
326
  headers=self.headers,
324
327
  json=json_data,
@@ -326,7 +329,7 @@ class OpenObserve:
326
329
  timeout=self.timeout,
327
330
  )
328
331
  elif method == "PUT":
329
- res = requests.put(
332
+ res = httpx.put(
330
333
  url,
331
334
  headers=self.headers,
332
335
  json=json_data,
@@ -344,6 +347,7 @@ class OpenObserve:
344
347
  *,
345
348
  start_time: Union[datetime, int] = 0,
346
349
  end_time: Union[datetime, int] = 0,
350
+ query_size: int = 1000,
347
351
  verbosity: int = 0,
348
352
  timeout: int = 300,
349
353
  timestamp_conversion_auto: bool = False,
@@ -356,6 +360,7 @@ class OpenObserve:
356
360
  sql: input sql query
357
361
  start_time: start of search interval, either datetime, either int/epoch
358
362
  end_time: end of search interval, either datetime, either int/epoch
363
+ query_size: maximum number of results returned (default: 1000)
359
364
  verbosity: how verbose to run from 0/less to 5/more
360
365
  timeout: http timeout
361
366
  timestamp_conversion_auto: try to convert automatically column containing time as name
@@ -365,6 +370,7 @@ class OpenObserve:
365
370
  sql,
366
371
  start_time=start_time,
367
372
  end_time=end_time,
373
+ query_size=query_size,
368
374
  verbosity=verbosity,
369
375
  timeout=timeout,
370
376
  timestamp_conversion_auto=timestamp_conversion_auto,
@@ -440,57 +446,6 @@ class OpenObserve:
440
446
  ) from err
441
447
  return df_res
442
448
 
443
- def search2df_fireducks(
444
- self,
445
- sql: str,
446
- *,
447
- start_time: Union[datetime, int] = 0,
448
- end_time: Union[datetime, int] = 0,
449
- verbosity: int = 0,
450
- timeout: int = 300,
451
- timestamp_conversion_auto: bool = False,
452
- timestamp_columns: Union[List[str], None] = None,
453
- ) -> fireducks.core.pandas.DataFrame:
454
- """
455
- OpenObserve search function with fireducks df output
456
-
457
- Args:
458
- sql: input sql query
459
- start_time: start of search interval, either datetime, either int/epoch
460
- end_time: end of search interval, either datetime, either int/epoch
461
- verbosity: how verbose to run from 0/less to 5/more
462
- timeout: http timeout
463
- timestamp_conversion_auto: try to convert automatically column containing time as name
464
- timestamp_columns: convert given columns to timestamp
465
- """
466
- res_json_hits = self.search(
467
- sql,
468
- start_time=start_time,
469
- end_time=end_time,
470
- verbosity=verbosity,
471
- timeout=timeout,
472
- timestamp_conversion_auto=timestamp_conversion_auto,
473
- # leaving conversion to pandas
474
- # timestamp_columns=timestamp_columns,
475
- )
476
- df_res = fireducks.core.pandas.json_normalize(res_json_hits)
477
-
478
- if timestamp_columns is not None:
479
- for col in list(
480
- set(df_res.columns) & set(["_timestamp"] + timestamp_columns)
481
- ):
482
- try:
483
- # ensure timestamp format
484
- if col in ["_timestamp"] + timestamp_columns:
485
- df_res[col] = fireducks.pandas.to_datetime(df_res[col])
486
- except Exception as err:
487
- raise Exception(
488
- err,
489
- "query",
490
- f"query column type conversion: {col} -> {df_res[col]}",
491
- ) from err
492
- return df_res
493
-
494
449
  # pylint: disable=too-many-branches,too-many-locals
495
450
  def export_objects_split(
496
451
  self,
@@ -655,14 +610,14 @@ class OpenObserve:
655
610
  name: [
656
611
  api_path,
657
612
  self.list_objects(api_path, verbosity=verbosity),
658
- ] # type:ignore[misc]
613
+ ] # type: ignore[misc]
659
614
  for name, api_path in object_types.items()
660
615
  }
661
616
 
662
617
  for name, object_data in data.items():
663
618
  self.export_objects_split(
664
- object_data[0], # type:ignore[arg-type]
665
- object_data[1], # type:ignore[arg-type]
619
+ object_data[0], # type: ignore[arg-type]
620
+ object_data[1], # type: ignore[arg-type]
666
621
  file_path,
667
622
  verbosity=verbosity,
668
623
  strip=strip,
@@ -674,7 +629,7 @@ class OpenObserve:
674
629
  data = {
675
630
  name: self.list_objects(
676
631
  api_path, verbosity=verbosity
677
- ) # type:ignore[misc]
632
+ ) # type: ignore[misc]
678
633
  for name, api_path in object_types.items()
679
634
  }
680
635
 
@@ -698,7 +653,7 @@ class OpenObserve:
698
653
  self._debug(f"Create object {object_type} url: {url}", verbosity, level=1)
699
654
  self._debug(f"Create object json input: {object_json}", verbosity, level=2)
700
655
 
701
- res = requests.post(
656
+ res = httpx.post(
702
657
  url,
703
658
  json=object_json,
704
659
  headers=self.headers,
@@ -730,7 +685,7 @@ class OpenObserve:
730
685
  self._debug(f"Update object {object_type} url: {url}", verbosity, level=1)
731
686
  self._debug(f"Update object json input: {object_json}", verbosity, level=2)
732
687
 
733
- res = requests.put(
688
+ res = httpx.put(
734
689
  url,
735
690
  json=object_json,
736
691
  headers=self.headers,
@@ -766,7 +721,7 @@ class OpenObserve:
766
721
  count_update = 0
767
722
  current = self.list_objects(object_type, verbosity)
768
723
  self._debug(f"Create/Update by name objects list: {current}", verbosity, 4)
769
- for obj in current[key]: # type:ignore[call-overload]
724
+ for obj in current[key]: # type: ignore[call-overload]
770
725
  if key_name in obj and object_name.strip() == obj[key_name].strip():
771
726
  if overwrite:
772
727
  object_json[key_id] = obj[key_id]
@@ -800,7 +755,7 @@ class OpenObserve:
800
755
  url = url.replace("/api", "/api/v2")
801
756
  self._debug(f"Delete object {object_type} url: {url}", verbosity, level=1)
802
757
 
803
- res = requests.delete(
758
+ res = httpx.delete(
804
759
  url,
805
760
  headers=self.headers,
806
761
  verify=self.verify,
@@ -829,7 +784,7 @@ class OpenObserve:
829
784
  count_delete = 0
830
785
  current = self.list_objects(object_type, verbosity)
831
786
  self._debug(f"Delete by name objects list: {current}", verbosity, 3)
832
- for obj in current[key]: # type:ignore[call-overload]
787
+ for obj in current[key]: # type: ignore[call-overload]
833
788
  if "name" in obj and object_name == obj[key_name]:
834
789
  self._debug(f"Delete by name matching object: {obj}", verbosity)
835
790
  self.delete_object(object_type, obj[key_id], verbosity)