python-jack-knife 0.6.12__tar.gz → 0.6.14__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.
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/PKG-INFO +1 -1
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/common.py +1 -7
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/postgres_pipe.py +42 -20
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/main.py +5 -3
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/let_reduce.py +1 -1
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/map.py +1 -1
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/query_pipe.py +9 -1
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/remove_field.py +2 -2
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/registry.py +24 -34
- python_jack_knife-0.6.14/src/pjk/sinks/stdout.py +78 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/version.py +1 -1
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/PKG-INFO +1 -1
- python_jack_knife-0.6.12/src/pjk/sinks/stdout.py +0 -46
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/LICENSE +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/README.md +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/pyproject.toml +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/setup.cfg +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/__init__.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/components.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/opensearch_client.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/opensearch_index_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/opensearch_query_pipe.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/snowflake_pipe.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/log.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/man_page.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/parser.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/__init__.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/denorm.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/factory.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/filter.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/head.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/join.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/move_field.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/progress_pipe.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/sample.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/select.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/sort.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/tail.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/user_pipe_factory.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/pipes/where.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/progress.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/__init__.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/create_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/csv_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/devnull.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/dir_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/expect.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/factory.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/format_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/graph.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/graph_bar_line.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/graph_cumulative.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/graph_hist.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/graph_scatter.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/json_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/s3_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/s3_stream.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/sinks.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/tsv_sink.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sinks/user_sink_factory.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/__init__.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/configs_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/csv_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/dir_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/factory.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/favorite_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/format_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/inline_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/json_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/lazy_file.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/lazy_file_local.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/lazy_file_s3.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/macro_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/npy_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/parquet_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/s3_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/source_list.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/sql_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/tsv_source.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/user_source_factory.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/usage.py +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/SOURCES.txt +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/dependency_links.txt +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/entry_points.txt +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/requires.txt +0 -0
- {python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/top_level.txt +0 -0
|
@@ -49,7 +49,7 @@ def pager_stdout(use_pager: bool = True):
|
|
|
49
49
|
|
|
50
50
|
env = os.environ.copy()
|
|
51
51
|
# -R: pass ANSI; -S: chop long lines; you can add -F/-X to taste
|
|
52
|
-
env.setdefault("LESS", "-
|
|
52
|
+
env.setdefault("LESS", "-RFX")
|
|
53
53
|
# Ensure UTF-8
|
|
54
54
|
env.setdefault("LESSCHARSET", "utf-8")
|
|
55
55
|
|
|
@@ -161,12 +161,6 @@ class ComponentFactory:
|
|
|
161
161
|
return None
|
|
162
162
|
return wrapper.comp_class
|
|
163
163
|
|
|
164
|
-
#def get_usage(self, name: str):
|
|
165
|
-
# comp_class = self.get_component_class(name)
|
|
166
|
-
# if not comp_class:
|
|
167
|
-
# return None
|
|
168
|
-
# return comp_class.usage()
|
|
169
|
-
|
|
170
164
|
def create(self, token: str):
|
|
171
165
|
pass
|
|
172
166
|
|
|
@@ -97,7 +97,8 @@ class PostgresPipe(QueryPipe,Integration):
|
|
|
97
97
|
examples = [
|
|
98
98
|
['myquery.sql', 'postgres:mydb', '-'],
|
|
99
99
|
["{'query': 'SELECT * from MY_TABLE;'}", 'postgres:mydb', '-'],
|
|
100
|
-
["{'query': 'SELECT * FROM pg_catalog.pg_tables;'}", 'postgres:mydb']
|
|
100
|
+
["{'query': 'SELECT * FROM pg_catalog.pg_tables;'}", 'postgres:mydb'],
|
|
101
|
+
["{'query': 'SELECT stored_procedure(%s, ...), batch_params:{...}"]
|
|
101
102
|
]
|
|
102
103
|
|
|
103
104
|
# name, type, default
|
|
@@ -165,14 +166,32 @@ class PostgresPipe(QueryPipe,Integration):
|
|
|
165
166
|
try:
|
|
166
167
|
query = record.get(self.query_field)
|
|
167
168
|
if not query:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
record['_error'] = 'missing query'
|
|
170
|
+
yield record
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
params = record.get(self.params_field) # single-exec params
|
|
174
|
+
batch = record.get("batch_params", None) # list[tuple|dict] for batching
|
|
175
|
+
|
|
176
|
+
cur = client.conn.cursor()
|
|
177
|
+
try:
|
|
178
|
+
did_executemany = False
|
|
179
|
+
|
|
180
|
+
# ---------- execute ----------
|
|
181
|
+
if batch is not None:
|
|
182
|
+
# Handle batch sizes explicitly to preserve single-SELECT streaming semantics
|
|
183
|
+
if len(batch) == 0:
|
|
184
|
+
# No-op batch; execute a lightweight statement so we can still emit a header
|
|
185
|
+
cur.execute("SELECT 1")
|
|
186
|
+
header_params = {"batch_size": 0}
|
|
187
|
+
elif len(batch) == 1:
|
|
188
|
+
cur.execute(query, batch[0])
|
|
189
|
+
header_params = batch[0]
|
|
190
|
+
else:
|
|
191
|
+
cur.executemany(query, batch)
|
|
192
|
+
did_executemany = True
|
|
193
|
+
header_params = {"batch_size": len(batch)}
|
|
194
|
+
else:
|
|
176
195
|
if params is None:
|
|
177
196
|
cur.execute(query)
|
|
178
197
|
else:
|
|
@@ -180,17 +199,20 @@ class PostgresPipe(QueryPipe,Integration):
|
|
|
180
199
|
cur.execute(query, params)
|
|
181
200
|
else:
|
|
182
201
|
cur.execute(query, (params,))
|
|
202
|
+
header_params = params
|
|
183
203
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
204
|
+
# ---------- header ----------
|
|
205
|
+
yield self._make_header(cur, query, header_params)
|
|
206
|
+
|
|
207
|
+
# ---------- stream rows (only meaningful for single execute that returns rows) ----------
|
|
208
|
+
# Note: executemany() typically doesn't expose per-execution result sets.
|
|
209
|
+
if not did_executemany and cur.description:
|
|
210
|
+
cols = [d[0] for d in cur.description]
|
|
211
|
+
if not (len(cols) == 1 and cols[0] == "ingest_event"):
|
|
212
|
+
for row in cur:
|
|
213
|
+
yield _row_to_dict(cur, row)
|
|
214
|
+
|
|
215
|
+
finally:
|
|
216
|
+
cur.close()
|
|
195
217
|
finally:
|
|
196
218
|
client.close()
|
|
@@ -10,7 +10,6 @@ from pjk.parser import ExpressionParser
|
|
|
10
10
|
from pjk.usage import UsageError
|
|
11
11
|
from pjk.log import init as init_logging
|
|
12
12
|
from datetime import datetime
|
|
13
|
-
from pathlib import Path
|
|
14
13
|
import traceback
|
|
15
14
|
import concurrent.futures
|
|
16
15
|
from pjk.registry import ComponentRegistry
|
|
@@ -26,6 +25,10 @@ def write_history(tokens):
|
|
|
26
25
|
|
|
27
26
|
log_path = ".pjk-history.txt"
|
|
28
27
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
|
|
28
|
+
|
|
29
|
+
if len(tokens) < 2:
|
|
30
|
+
return
|
|
31
|
+
|
|
29
32
|
command = " ".join(tokens)
|
|
30
33
|
|
|
31
34
|
try:
|
|
@@ -67,6 +70,7 @@ def execute_threaded(sinks, stop_progress=None):
|
|
|
67
70
|
|
|
68
71
|
def initialize():
|
|
69
72
|
init_logging()
|
|
73
|
+
write_history(sys.argv[1:])
|
|
70
74
|
|
|
71
75
|
#src = Path("src/pjk/resources/configs.tmpl")
|
|
72
76
|
#dst_dir = Path.home() / ".pjk"
|
|
@@ -121,8 +125,6 @@ def execute_tokens(tokens: List[str]):
|
|
|
121
125
|
else:
|
|
122
126
|
sink.drain()
|
|
123
127
|
|
|
124
|
-
write_history(sys.argv[1:])
|
|
125
|
-
|
|
126
128
|
except UsageError as e:
|
|
127
129
|
print(e, file=sys.stderr)
|
|
128
130
|
sys.exit(2)
|
|
@@ -24,7 +24,7 @@ def do_eval(expr, env):
|
|
|
24
24
|
safe_env['json'] = json
|
|
25
25
|
return eval(expr, {}, safe_env)
|
|
26
26
|
except Exception:
|
|
27
|
-
raise
|
|
27
|
+
raise Exception(f"Error in expression: {expr}")
|
|
28
28
|
|
|
29
29
|
def eval_regular(expr: str, record: dict):
|
|
30
30
|
env = {'f': SafeNamespace(record)}
|
|
@@ -51,7 +51,7 @@ class MapByPipe(Pipe, KeyedSource):
|
|
|
51
51
|
key_rec = {}
|
|
52
52
|
for field in self.fields:
|
|
53
53
|
key_val = record.pop(field, None) if self.is_group else record.get(field)
|
|
54
|
-
if not
|
|
54
|
+
if key_val is None: # not only false-ish but NONE
|
|
55
55
|
return None
|
|
56
56
|
|
|
57
57
|
key_rec[field] = key_val
|
|
@@ -2,7 +2,7 @@ from pjk.components import Pipe
|
|
|
2
2
|
from pjk.usage import ParsedToken, Usage, CONFIG_FILE
|
|
3
3
|
from typing import Any, Dict, Iterable, Optional
|
|
4
4
|
from abc import abstractmethod
|
|
5
|
-
|
|
5
|
+
from pjk.progress import papi
|
|
6
6
|
|
|
7
7
|
class QueryPipe(Pipe):
|
|
8
8
|
name: str = None
|
|
@@ -40,6 +40,8 @@ class QueryPipe(Pipe):
|
|
|
40
40
|
self.output_shape = usage.get_param('shape')
|
|
41
41
|
self.count = usage.get_param('count')
|
|
42
42
|
self.query_field = 'query' # for all subclasses
|
|
43
|
+
self.inrecs = papi.get_counter(self, var_label=None) # don't display progress
|
|
44
|
+
self.outrecs = papi.get_percentage_counter(self, var_label='recs_out', denom_counter=self.inrecs)
|
|
43
45
|
|
|
44
46
|
@abstractmethod
|
|
45
47
|
def execute_query_returning_S_xO_iterable(self, record) -> Iterable[Dict[str, Any]]:
|
|
@@ -53,6 +55,7 @@ class QueryPipe(Pipe):
|
|
|
53
55
|
|
|
54
56
|
def __iter__(self):
|
|
55
57
|
for in_rec in self.left:
|
|
58
|
+
self.inrecs.increment()
|
|
56
59
|
iter = self.execute_query_returning_S_xO_iterable(in_rec)
|
|
57
60
|
|
|
58
61
|
if self.output_shape == 'S_xO':
|
|
@@ -60,8 +63,11 @@ class QueryPipe(Pipe):
|
|
|
60
63
|
for out_rec in iter:
|
|
61
64
|
if not q_done:
|
|
62
65
|
q_done = True
|
|
66
|
+
self.outrecs.increment()
|
|
63
67
|
yield self._make_q_object(in_rec, out_rec)
|
|
64
68
|
continue
|
|
69
|
+
|
|
70
|
+
self.outrecs.increment()
|
|
65
71
|
yield out_rec
|
|
66
72
|
|
|
67
73
|
elif self.output_shape == 'xO':
|
|
@@ -70,6 +76,7 @@ class QueryPipe(Pipe):
|
|
|
70
76
|
if not q_done:
|
|
71
77
|
q_done = True
|
|
72
78
|
continue
|
|
79
|
+
self.outrecs.increment()
|
|
73
80
|
yield out_rec
|
|
74
81
|
|
|
75
82
|
elif self.output_shape == 'Sxo':
|
|
@@ -84,6 +91,7 @@ class QueryPipe(Pipe):
|
|
|
84
91
|
continue
|
|
85
92
|
r_list.append(out_rec)
|
|
86
93
|
q_out['child'] = r_list
|
|
94
|
+
self.outrecs.increment()
|
|
87
95
|
yield q_out
|
|
88
96
|
|
|
89
97
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
# djk/pipes/remove_field.py
|
|
5
5
|
|
|
6
|
-
from pjk.components import
|
|
6
|
+
from pjk.components import DeepCopyPipe
|
|
7
7
|
from pjk.usage import ParsedToken, Usage, UsageError
|
|
8
8
|
|
|
9
|
-
class RemoveField(
|
|
9
|
+
class RemoveField(DeepCopyPipe):
|
|
10
10
|
@classmethod
|
|
11
11
|
def usage(cls):
|
|
12
12
|
usage = Usage(
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
|
-
import time
|
|
7
6
|
from pjk.sinks.factory import SinkFactory
|
|
8
7
|
from pjk.pipes.factory import PipeFactory
|
|
9
8
|
from pjk.sources.factory import SourceFactory
|
|
@@ -11,8 +10,6 @@ from pjk.sinks.format_sink import FormatSink
|
|
|
11
10
|
from pjk.sources.format_source import FormatSource
|
|
12
11
|
import importlib.util
|
|
13
12
|
import importlib
|
|
14
|
-
import importlib.metadata
|
|
15
|
-
import sysconfig, pathlib, importlib
|
|
16
13
|
from pjk.components import Pipe, Source, Sink
|
|
17
14
|
from pjk.common import ComponentFactory, highlight, ComponentOrigin
|
|
18
15
|
from typing import List, Type
|
|
@@ -38,7 +35,7 @@ class ComponentRegistry:
|
|
|
38
35
|
self.pipe_factory = PipeFactory()
|
|
39
36
|
self.sink_factory = SinkFactory()
|
|
40
37
|
self.load_user_components()
|
|
41
|
-
self.
|
|
38
|
+
self.load_namespace_extras()
|
|
42
39
|
|
|
43
40
|
def create_source(self, token: str):
|
|
44
41
|
return self.source_factory.create(token)
|
|
@@ -125,42 +122,35 @@ class ComponentRegistry:
|
|
|
125
122
|
elif is_source(obj, module):
|
|
126
123
|
self.source_factory.register(name, obj, ComponentOrigin.USER)
|
|
127
124
|
|
|
128
|
-
def
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"""
|
|
132
|
-
eps = importlib.metadata.entry_points()
|
|
133
|
-
return eps.select(group=group) if hasattr(eps, "select") else eps.get(group, [])
|
|
125
|
+
def load_namespace_extras(self, package: str = "pjk_extras") -> None:
|
|
126
|
+
registrar = ExternalRegistrar(self.source_factory, self.pipe_factory, self.sink_factory)
|
|
127
|
+
import importlib, importlib.metadata as im
|
|
134
128
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
for dist in im.distributions():
|
|
130
|
+
name = (dist.metadata.get("Name") or "")
|
|
131
|
+
if not name.startswith("pjk-"):
|
|
132
|
+
continue
|
|
138
133
|
|
|
139
|
-
|
|
140
|
-
entry point -> callable register(registrar)
|
|
134
|
+
modname = f"{package}.{name[4:].replace('-', '_')}" # pjk-foo-bar -> pjk_extras.foo_bar
|
|
141
135
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
# Import the extra; if it fails, continue to the next
|
|
137
|
+
try:
|
|
138
|
+
mod = importlib.import_module(modname)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print(f"[pjk] import failed for {modname}: {e}")
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
reg = getattr(mod, "register", None)
|
|
144
|
+
if not callable(reg):
|
|
145
|
+
print(f"[pjk] extra '{modname}' has no register(registrar)")
|
|
146
|
+
continue
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
# Run its register; if it fails, continue to the next
|
|
148
149
|
try:
|
|
149
|
-
#
|
|
150
|
-
loader = getattr(ep, "load", None)
|
|
151
|
-
if callable(loader):
|
|
152
|
-
target = ep.load() # resolves "module:object" to the actual object
|
|
153
|
-
if callable(target):
|
|
154
|
-
target(registrar) # plugin registers into your live factories
|
|
155
|
-
#print(f"[pjk] loaded extra (callable): {ep.name} -> {ep.value}")
|
|
156
|
-
continue
|
|
157
|
-
# Not callable -> fall through to legacy import
|
|
158
|
-
# Legacy path: import ONLY the module portion before ':' for side-effects
|
|
159
|
-
mod = ep.value.split(":", 1)[0]
|
|
160
|
-
importlib.import_module(mod)
|
|
161
|
-
#print(f"[pjk] loaded extra (import): {ep.name} -> {ep.value}")
|
|
150
|
+
reg(registrar) # registers class TYPES, same contract as before
|
|
162
151
|
except Exception as e:
|
|
163
|
-
print(f"[pjk] failed
|
|
152
|
+
print(f"[pjk] register() failed in {modname}: {e}")
|
|
153
|
+
continue
|
|
164
154
|
|
|
165
155
|
def print_core_formats(factories: List[ComponentFactory]):
|
|
166
156
|
print(highlight('formats'))
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright 2024 Mike Schultz
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import yaml
|
|
6
|
+
from yaml.representer import SafeRepresenter # kept for compatibility
|
|
7
|
+
from pjk.components import Sink, Source
|
|
8
|
+
from pjk.usage import ParsedToken, Usage
|
|
9
|
+
from pjk.common import pager_stdout
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class StdoutSink(Sink):
|
|
13
|
+
@classmethod
|
|
14
|
+
def usage(cls):
|
|
15
|
+
usage = Usage(
|
|
16
|
+
name='-',
|
|
17
|
+
desc='display records in yaml or txt format to stdout through less',
|
|
18
|
+
component_class=cls
|
|
19
|
+
)
|
|
20
|
+
usage.def_param('less', usage='use less to display', valid_values=['true', 'false'], default='true')
|
|
21
|
+
usage.def_param('format', usage='output format', valid_values=['yaml', 'txt'], default='yaml')
|
|
22
|
+
usage.def_example(["{hello:'world!'}"], "{hello:'world!'}")
|
|
23
|
+
return usage
|
|
24
|
+
|
|
25
|
+
def __init__(self, ptok: ParsedToken, usage: Usage):
|
|
26
|
+
super().__init__(ptok, usage)
|
|
27
|
+
self.use_pager = True if usage.get_param('less') is None else usage.get_param('less') == 'true'
|
|
28
|
+
self.output_format = usage.get_param('format') or 'yaml'
|
|
29
|
+
|
|
30
|
+
def _sanitize_scalar(self, v) -> str:
|
|
31
|
+
if v is None:
|
|
32
|
+
s = ''
|
|
33
|
+
elif isinstance(v, (list, dict)):
|
|
34
|
+
# single-line YAML to preserve structure compactly
|
|
35
|
+
s = yaml.safe_dump(v, sort_keys=False, allow_unicode=True).strip()
|
|
36
|
+
else:
|
|
37
|
+
s = str(v)
|
|
38
|
+
return s.replace('\r', ' ').replace('\n', ' ')
|
|
39
|
+
|
|
40
|
+
def _process_yaml(self):
|
|
41
|
+
for record in self.input:
|
|
42
|
+
try:
|
|
43
|
+
yaml.dump(
|
|
44
|
+
record,
|
|
45
|
+
sys.stdout,
|
|
46
|
+
sort_keys=False,
|
|
47
|
+
explicit_start=True, # '---' before each record
|
|
48
|
+
allow_unicode=True,
|
|
49
|
+
width=10**9,
|
|
50
|
+
)
|
|
51
|
+
except BrokenPipeError:
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
def _process_txt(self):
|
|
55
|
+
for record in self.input:
|
|
56
|
+
try:
|
|
57
|
+
# record delimiter
|
|
58
|
+
sys.stdout.write('---\n')
|
|
59
|
+
if isinstance(record, dict):
|
|
60
|
+
for k, v in record.items():
|
|
61
|
+
key = str(k)
|
|
62
|
+
val = self._sanitize_scalar(v)
|
|
63
|
+
sys.stdout.write(f'{key}: {val}\n')
|
|
64
|
+
else:
|
|
65
|
+
val = self._sanitize_scalar(record)
|
|
66
|
+
sys.stdout.write(f'value: {val}\n')
|
|
67
|
+
except BrokenPipeError:
|
|
68
|
+
break
|
|
69
|
+
|
|
70
|
+
def process(self) -> None:
|
|
71
|
+
try:
|
|
72
|
+
with pager_stdout(self.use_pager):
|
|
73
|
+
if self.output_format == 'txt':
|
|
74
|
+
self._process_txt()
|
|
75
|
+
else:
|
|
76
|
+
self._process_yaml()
|
|
77
|
+
except BrokenPipeError:
|
|
78
|
+
pass
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
# Copyright 2024 Mike Schultz
|
|
3
|
-
|
|
4
|
-
import sys
|
|
5
|
-
import yaml
|
|
6
|
-
from yaml.representer import SafeRepresenter
|
|
7
|
-
from pjk.components import Sink, Source
|
|
8
|
-
from pjk.usage import ParsedToken, Usage
|
|
9
|
-
from pjk.common import pager_stdout
|
|
10
|
-
|
|
11
|
-
class StdoutSink(Sink):
|
|
12
|
-
@classmethod
|
|
13
|
-
def usage(cls):
|
|
14
|
-
usage = Usage(
|
|
15
|
-
name='-',
|
|
16
|
-
desc='display records in yaml format to stdout through less',
|
|
17
|
-
component_class=cls
|
|
18
|
-
)
|
|
19
|
-
usage.def_param('less', usage='use less to display', valid_values=['true', 'false'], default='true')
|
|
20
|
-
usage.def_example(["{hello:'world!'}"], "{hello:'world!'}")
|
|
21
|
-
return usage
|
|
22
|
-
|
|
23
|
-
def __init__(self, ptok: ParsedToken, usage: Usage):
|
|
24
|
-
super().__init__(ptok, usage)
|
|
25
|
-
self.use_pager = True if usage.get_param('less') == None else usage.get_param('less') == 'true'
|
|
26
|
-
|
|
27
|
-
def process(self) -> None:
|
|
28
|
-
try:
|
|
29
|
-
with pager_stdout(self.use_pager):
|
|
30
|
-
for record in self.input:
|
|
31
|
-
try:
|
|
32
|
-
# everything else -> normal YAML
|
|
33
|
-
yaml.dump(
|
|
34
|
-
record,
|
|
35
|
-
sys.stdout,
|
|
36
|
-
sort_keys=False,
|
|
37
|
-
explicit_start=True,
|
|
38
|
-
allow_unicode=True,
|
|
39
|
-
width=10**9,
|
|
40
|
-
)
|
|
41
|
-
except BrokenPipeError:
|
|
42
|
-
break
|
|
43
|
-
except BrokenPipeError:
|
|
44
|
-
pass
|
|
45
|
-
|
|
46
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/opensearch_client.py
RENAMED
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/opensearch_index_sink.py
RENAMED
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/opensearch_query_pipe.py
RENAMED
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/integrations/snowflake_pipe.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/pjk/sources/user_source_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/requires.txt
RENAMED
|
File without changes
|
{python_jack_knife-0.6.12 → python_jack_knife-0.6.14}/src/python_jack_knife.egg-info/top_level.txt
RENAMED
|
File without changes
|