python-openobserve 0.5.1__tar.gz → 0.6.0__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.5.1
3
+ Version: 0.6.0
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,42 +8,42 @@ 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.15
11
+ Requires-Python: >=3.11,<3.16
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Provides-Extra: ci
14
14
  Provides-Extra: docs
15
15
  Provides-Extra: pandas
16
16
  Provides-Extra: polars
17
17
  Provides-Extra: tests
18
- Requires-Dist: cryptography (>=48.0.1,<48.1.0)
18
+ Requires-Dist: cryptography (>=50.0.0,<50.1.0)
19
19
  Requires-Dist: h11 (>=0.16.0,<0.17.0)
20
20
  Requires-Dist: httpcore (>=1.0.9,<1.1.0)
21
21
  Requires-Dist: httpx (>=0.28,<1.0)
22
- Requires-Dist: jmespath (==1.0.1) ; extra == "ci"
23
- Requires-Dist: jmespath (==1.0.1) ; extra == "tests"
24
- Requires-Dist: memray (==1.19.3) ; extra == "ci"
22
+ Requires-Dist: jmespath (==1.1.0) ; extra == "ci"
23
+ Requires-Dist: jmespath (==1.1.0) ; extra == "tests"
24
+ Requires-Dist: memray (==1.20.0) ; extra == "ci"
25
25
  Requires-Dist: mkdocs (==1.6.1) ; extra == "docs"
26
- Requires-Dist: mkdocs-material (==9.6.15) ; extra == "docs"
27
- Requires-Dist: mkdocstrings (==0.29.1) ; extra == "docs"
28
- Requires-Dist: mypy (==1.15.0) ; extra == "ci"
29
- Requires-Dist: mypy-extensions (==1.0.0) ; extra == "ci"
30
- Requires-Dist: pandas (==2.2.3) ; extra == "ci"
26
+ Requires-Dist: mkdocs-material (==9.8.0) ; extra == "docs"
27
+ Requires-Dist: mkdocstrings (==1.0.8) ; extra == "docs"
28
+ Requires-Dist: mypy (==2.4.1) ; extra == "ci"
29
+ Requires-Dist: mypy-extensions (==1.2.0) ; extra == "ci"
30
+ Requires-Dist: pandas (==3.1.1) ; extra == "ci"
31
31
  Requires-Dist: pandas (>=2.2) ; extra == "pandas"
32
- Requires-Dist: pandas-stubs (==2.2.2.240807) ; extra == "ci"
33
- Requires-Dist: polars (==1.30.0) ; extra == "polars"
34
- Requires-Dist: protobuf (>=6.33.5)
32
+ Requires-Dist: pandas-stubs (==3.1.0.261029) ; extra == "ci"
33
+ Requires-Dist: polars (==2.1.0) ; extra == "polars"
34
+ Requires-Dist: protobuf (>=7.35.1)
35
35
  Requires-Dist: pydantic (>=2.10,<3.0)
36
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"
39
- Requires-Dist: pytest-cov (==6.0.0) ; extra == "ci"
40
- Requires-Dist: pytest-memray (==1.7.0) ; extra == "ci"
41
- Requires-Dist: python-dotenv (==1.2.2) ; extra == "ci"
42
- Requires-Dist: python-dotenv (==1.2.2) ; extra == "tests"
37
+ Requires-Dist: pytest (==9.2.0) ; extra == "ci"
38
+ Requires-Dist: pytest (==9.2.0) ; extra == "tests"
39
+ Requires-Dist: pytest-cov (==7.1.0) ; extra == "ci"
40
+ Requires-Dist: pytest-memray (==1.11.0) ; extra == "ci"
41
+ Requires-Dist: python-dotenv (==1.2.4) ; extra == "ci"
42
+ Requires-Dist: python-dotenv (==1.2.4) ; extra == "tests"
43
43
  Requires-Dist: python-multipart (>=0.0.22)
44
44
  Requires-Dist: requests (>=2.33.0,<3.0.0)
45
- Requires-Dist: sqlglot (>=26.4.0,<26.5.0)
46
- Requires-Dist: types-jmespath (==1.0.2.20240106) ; extra == "ci"
45
+ Requires-Dist: sqlglot (>=30.29.0,<30.30.0)
46
+ Requires-Dist: types-jmespath (==1.1.0.20260724) ; extra == "ci"
47
47
  Project-URL: Homepage, https://github.com/JustinGuese/python-openobserve
48
48
  Project-URL: Repository, https://github.com/JustinGuese/python-openobserve
49
49
  Description-Content-Type: text/markdown
@@ -3,10 +3,10 @@
3
3
 
4
4
  [project]
5
5
  name = "python-openobserve"
6
- version = "0.5.1"
6
+ version = "0.6.0"
7
7
  description = "OpenObserve client for Python"
8
8
  readme = "README.md"
9
- requires-python = ">=3.10,<3.15"
9
+ requires-python = ">=3.11,<3.16"
10
10
  license-files = [
11
11
  "LICENSES/CC-BY-4.0.txt",
12
12
  "LICENSES/CC-BY-SA-4.0.txt",
@@ -19,14 +19,14 @@ classifiers = [
19
19
  ]
20
20
 
21
21
  dependencies = [
22
- "cryptography (>=48.0.1,<48.1.0)",
22
+ "cryptography (>=50.0.0,<50.1.0)",
23
23
  "h11~=0.16.0",
24
24
  "httpcore~=1.0.9",
25
25
  "httpx~=0.28",
26
- "protobuf>=6.33.5",
26
+ "protobuf>=7.35.1",
27
27
  "pydantic~=2.10",
28
28
  "python-multipart>=0.0.22",
29
- "sqlglot~=26.4.0",
29
+ "sqlglot~=30.29.0",
30
30
  "pyjwt (>=2.12.1,<3.0.0)",
31
31
  "requests (>=2.33.0,<3.0.0)",
32
32
  ]
@@ -40,56 +40,56 @@ python_openobserve = "python_openobserve:openobserve"
40
40
 
41
41
  [project.optional-dependencies]
42
42
  ci = [
43
- "jmespath==1.0.1",
44
- "memray==1.19.3",
45
- "pytest-memray==1.7.0",
46
- "mypy==1.15.0",
47
- "mypy-extensions==1.0.0",
48
- "pandas==2.2.3",
49
- "pandas-stubs==2.2.2.240807",
50
- "pytest==9.0.3",
51
- "pytest-cov==6.0.0",
52
- "python-dotenv==1.2.2",
53
- "types-jmespath==1.0.2.20240106",
43
+ "jmespath==1.1.0",
44
+ "memray==1.20.0",
45
+ "pytest-memray==1.11.0",
46
+ "mypy==2.4.1",
47
+ "mypy-extensions==1.2.0",
48
+ "pandas==3.1.1",
49
+ "pandas-stubs==3.1.0.261029",
50
+ "pytest==9.2.0",
51
+ "pytest-cov==7.1.0",
52
+ "python-dotenv==1.2.4",
53
+ "types-jmespath==1.1.0.20260724",
54
54
  ]
55
55
  docs = [
56
56
  "mkdocs==1.6.1",
57
- "mkdocs-material==9.6.15",
58
- "mkdocstrings==0.29.1",
57
+ "mkdocs-material==9.8.0",
58
+ "mkdocstrings==1.0.8",
59
59
  ]
60
- tests = ["pytest==9.0.3", "jmespath==1.0.1", "python-dotenv==1.2.2"]
60
+ tests = ["pytest==9.2.0", "jmespath==1.1.0", "python-dotenv==1.2.4"]
61
61
  pandas = ["pandas>=2.2"]
62
- polars = ["polars==1.30.0"]
62
+ polars = ["polars==2.1.0"]
63
63
 
64
64
  [tool.poetry.group.test.dependencies]
65
- pytest = "9.0.3"
66
- jmespath = "1.0.1"
67
- python-dotenv = "1.2.2"
65
+ pytest = "*"
66
+ jmespath = "*"
67
+ python-dotenv = "*"
68
68
 
69
69
  [tool.poetry.group.docs.dependencies]
70
- mkdocs = "1.6.1"
71
- mkdocs-material = "9.6.15"
72
- mkdocstrings = "0.29.1"
70
+ mkdocs = "*"
71
+ mkdocs-material = "*"
72
+ mkdocstrings = "*"
73
73
 
74
74
  [tool.poetry.group.ci.dependencies]
75
- jmespath = "1.0.1"
76
- memray = "1.19.3"
77
- pytest-memray = "1.7.0"
78
- mypy = "1.15.0"
79
- mypy-extensions = "1.0.0"
80
- pandas = "2.2.3"
81
- openpyxl = "3.1.5"
82
- pandas-stubs = "2.2.2.240807"
83
- pytest = "9.0.3"
84
- pytest-cov = "6.0.0"
85
- python-dotenv = "1.2.2"
86
- types-jmespath = "1.0.2.20240106"
75
+ jmespath = "*"
76
+ memray = "*"
77
+ pytest-memray = "*"
78
+ mypy = "*"
79
+ mypy-extensions = "*"
80
+ pandas = "*"
81
+ openpyxl = "*"
82
+ pandas-stubs = "*"
83
+ pytest = "*"
84
+ pytest-cov = "*"
85
+ python-dotenv = "*"
86
+ types-jmespath = "*"
87
87
 
88
88
  [tool.poetry.group.pandas.dependencies]
89
89
  pandas = ">2.2"
90
90
 
91
91
  [tool.poetry.group.polars.dependencies]
92
- polars = "1.30.0"
92
+ polars = "*"
93
93
 
94
94
  [build-system]
95
95
  requires = ["poetry-core"]
@@ -15,11 +15,11 @@ import json
15
15
  import os
16
16
  import sys
17
17
  import re
18
- from datetime import datetime
18
+ from datetime import datetime, timedelta
19
19
  from typing import List, Dict, Union, Optional, Any, cast
20
20
  from pathlib import Path
21
21
 
22
- import httpx
22
+ import httpx # type: ignore
23
23
  import sqlglot # type: ignore
24
24
 
25
25
  try:
@@ -33,7 +33,7 @@ except ImportError:
33
33
  HAVE_MODULE_PANDAS = False
34
34
 
35
35
  try:
36
- import polars
36
+ import polars # type: ignore
37
37
 
38
38
  HAVE_MODULE_POLARS = True
39
39
  except ImportError:
@@ -437,7 +437,7 @@ class OpenObserve:
437
437
  try:
438
438
  # ensure timestamp format
439
439
  if col in ["_timestamp"] + timestamp_columns:
440
- df_res[col] = polars.to_datetime(df_res[col])
440
+ df_res = df_res.with_columns(polars.col(col).str.to_datetime())
441
441
  except Exception as err:
442
442
  raise Exception(
443
443
  err,
@@ -446,6 +446,112 @@ class OpenObserve:
446
446
  ) from err
447
447
  return df_res
448
448
 
449
+ # pylint: disable=R0914
450
+ def search2export(
451
+ self,
452
+ sql: str,
453
+ file_prefix: str,
454
+ *,
455
+ start_time: datetime = datetime.now(),
456
+ end_time: datetime = datetime.now(),
457
+ export_format: str = "json",
458
+ split_period: str = "D",
459
+ verbosity: int = 0,
460
+ timeout: int = 300,
461
+ timestamp_conversion_auto: bool = False,
462
+ ) -> bool:
463
+ """
464
+ OpenObserve search export function
465
+ Split options to manage large volume
466
+
467
+ Args:
468
+ sql: input sql query
469
+ start_time: start of search interval, either datetime, either int/epoch
470
+ end_time: end of search interval, either datetime, either int/epoch
471
+ export_format: raw json from OpenObserve (json) or csv
472
+ split_period: interval per file, H, D, W or M
473
+ file_prefix: file prefix for export
474
+ verbosity: how verbose to run from 0/less to 5/more
475
+ timeout: http timeout
476
+ timestamp_conversion_auto: try to convert automatically column containing time as name
477
+ timestamp_columns: convert given columns to timestamp
478
+ """
479
+ if not file_prefix:
480
+ print(f"FATAL! no file_prefix input: {file_prefix}")
481
+ return False
482
+ period_start = start_time
483
+ interval = timedelta(days=1)
484
+ time_fmt = "%Y-%m-%dT%H:%M"
485
+ if split_period == "H":
486
+ interval = timedelta(hours=1)
487
+ if split_period == "W":
488
+ interval = timedelta(weeks=1)
489
+ time_fmt = "%Y-%m-%d"
490
+ # FIXME! timedelta does not support months input
491
+ # alternative: relativedelta but extra dependency
492
+ # if split_period == "M":
493
+ # interval = timedelta(months=1)
494
+ # time_fmt = "%Y-%m-%d"
495
+ if split_period not in ("H", "D", "W"):
496
+ print(f"FATAL! invalid split period input: {split_period}")
497
+ return False
498
+ period_end = start_time + interval
499
+
500
+ if export_format == "json":
501
+ while period_end < end_time:
502
+ file_name = (
503
+ f"{file_prefix}--"
504
+ f"{period_start.strftime(time_fmt)}--{period_end.strftime(time_fmt)}"
505
+ "--export.json"
506
+ )
507
+ res_json = self.search(
508
+ sql,
509
+ start_time=period_start,
510
+ end_time=period_end,
511
+ query_size=1000000000000,
512
+ verbosity=verbosity,
513
+ timeout=timeout,
514
+ timestamp_conversion_auto=timestamp_conversion_auto,
515
+ # leaving conversion to pandas
516
+ # timestamp_columns=timestamp_columns,
517
+ )
518
+ with open(
519
+ file_name,
520
+ "w",
521
+ encoding="utf-8",
522
+ ) as f:
523
+ json.dump(res_json, f)
524
+ # loop
525
+ period_start = period_end
526
+ period_end = period_start + interval
527
+ if export_format == "csv":
528
+ while period_end < end_time:
529
+ file_name = (
530
+ f"{file_prefix}--"
531
+ f"{period_start.strftime(time_fmt)}--{period_end.strftime(time_fmt)}"
532
+ "--export.csv"
533
+ )
534
+ df_res = self.search2df(
535
+ sql,
536
+ start_time=period_start,
537
+ end_time=period_end,
538
+ query_size=1000000000000,
539
+ verbosity=verbosity,
540
+ timeout=timeout,
541
+ timestamp_conversion_auto=timestamp_conversion_auto,
542
+ # leaving conversion to pandas
543
+ # timestamp_columns=timestamp_columns,
544
+ )
545
+ df_res.to_csv(
546
+ file_name,
547
+ index=False,
548
+ )
549
+ # loop
550
+ period_start = period_end
551
+ period_end = period_start + interval
552
+
553
+ return True
554
+
449
555
  # pylint: disable=too-many-branches,too-many-locals
450
556
  def export_objects_split(
451
557
  self,
@@ -603,7 +709,6 @@ class OpenObserve:
603
709
  for name, df in data.items():
604
710
  df.to_excel(f"{file_path}{name}.xlsx")
605
711
  else: # default json
606
-
607
712
  if split is True and flat is False:
608
713
  # split json
609
714
  data = {
@@ -627,9 +732,7 @@ class OpenObserve:
627
732
  sys.exit(1)
628
733
  else:
629
734
  data = {
630
- name: self.list_objects(
631
- api_path, verbosity=verbosity
632
- ) # type: ignore[misc]
735
+ name: self.list_objects(api_path, verbosity=verbosity) # type: ignore[misc]
633
736
  for name, api_path in object_types.items()
634
737
  }
635
738