meerschaum 2.9.4__py3-none-any.whl → 3.0.0rc1__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.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +17 -1
- meerschaum/_internal/entry.py +6 -6
- meerschaum/_internal/shell/Shell.py +1 -1
- meerschaum/_internal/static.py +372 -0
- meerschaum/actions/api.py +12 -2
- meerschaum/actions/bootstrap.py +7 -7
- meerschaum/actions/edit.py +142 -18
- meerschaum/actions/register.py +137 -6
- meerschaum/actions/show.py +117 -29
- meerschaum/actions/stop.py +4 -1
- meerschaum/actions/sync.py +1 -1
- meerschaum/actions/tag.py +9 -8
- meerschaum/api/__init__.py +9 -2
- meerschaum/api/_events.py +39 -2
- meerschaum/api/_oauth2.py +118 -8
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -1
- meerschaum/api/dash/callbacks/custom.py +2 -2
- meerschaum/api/dash/callbacks/dashboard.py +133 -18
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +1 -1
- meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
- meerschaum/api/dash/components.py +30 -8
- meerschaum/api/dash/keys.py +19 -93
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/settings/__init__.py +1 -0
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/settings/tokens.py +55 -0
- meerschaum/api/dash/pipes.py +156 -58
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +606 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +4 -0
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +85 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/static/css/dash.css +16 -0
- meerschaum/api/resources/templates/termpage.html +12 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +14 -35
- meerschaum/api/routes/_login.py +49 -12
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +134 -111
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/config/__init__.py +43 -20
- meerschaum/config/_default.py +32 -5
- meerschaum/config/_edit.py +28 -24
- meerschaum/config/_environment.py +1 -1
- meerschaum/config/_patch.py +6 -6
- meerschaum/config/_paths.py +5 -1
- meerschaum/config/_read_config.py +65 -34
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +24 -5
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +10 -4
- meerschaum/connectors/__init__.py +4 -20
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +1 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +15 -14
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +151 -0
- meerschaum/connectors/instance/_tokens.py +296 -0
- meerschaum/connectors/instance/_users.py +181 -0
- meerschaum/connectors/parse.py +4 -1
- meerschaum/connectors/sql/_SQLConnector.py +8 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +6 -154
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +42 -31
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +9 -2
- meerschaum/connectors/sql/_users.py +29 -2
- meerschaum/connectors/sql/tables/__init__.py +1 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
- meerschaum/connectors/valkey/_pipes.py +9 -10
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +31 -14
- meerschaum/core/Pipe/_attributes.py +156 -58
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_data.py +41 -1
- meerschaum/core/Pipe/_dtypes.py +29 -14
- meerschaum/core/Pipe/_edit.py +12 -4
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +48 -53
- meerschaum/core/Pipe/_verify.py +1 -1
- meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +221 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +34 -8
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Job.py +3 -2
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +1 -1
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +22 -7
- meerschaum/plugins/bootstrap.py +2 -1
- meerschaum/utils/_get_pipes.py +68 -27
- meerschaum/utils/daemon/Daemon.py +2 -1
- meerschaum/utils/daemon/__init__.py +30 -2
- meerschaum/utils/dataframe.py +96 -15
- meerschaum/utils/dtypes/__init__.py +93 -21
- meerschaum/utils/dtypes/sql.py +44 -0
- meerschaum/utils/formatting/__init__.py +1 -1
- meerschaum/utils/formatting/_pipes.py +5 -4
- meerschaum/utils/formatting/_shell.py +11 -9
- meerschaum/utils/misc.py +237 -80
- meerschaum/utils/packages/__init__.py +3 -6
- meerschaum/utils/packages/_packages.py +34 -32
- meerschaum/utils/pipes.py +181 -0
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +3 -1
- meerschaum/utils/schedule.py +1 -0
- meerschaum/utils/sql.py +115 -39
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +5 -7
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
- meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.4.dist-info/RECORD +0 -263
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
meerschaum/api/dash/pipes.py
CHANGED
@@ -18,7 +18,7 @@ from meerschaum.utils.packages import attempt_import, import_dcc, import_html, i
|
|
18
18
|
from meerschaum.utils.sql import get_pd_type
|
19
19
|
from meerschaum.utils.yaml import yaml
|
20
20
|
from meerschaum.utils.warnings import warn
|
21
|
-
from meerschaum.utils.dataframe import to_json
|
21
|
+
from meerschaum.utils.dataframe import to_json, to_simple_lines
|
22
22
|
from meerschaum.connectors.sql._fetch import get_pipe_query
|
23
23
|
from meerschaum.api import CHECK_UPDATE
|
24
24
|
from meerschaum.api.dash import debug, _get_pipes
|
@@ -58,28 +58,20 @@ def pipe_from_ctx(ctx, trigger_property: str = 'n_clicks') -> Union[mrsm.Pipe, N
|
|
58
58
|
|
59
59
|
def keys_from_state(
|
60
60
|
state: Dict[str, Any],
|
61
|
-
|
61
|
+
with_tags: bool = False,
|
62
62
|
) -> Union[
|
63
63
|
Tuple[List[str], List[str], List[str]],
|
64
|
-
Tuple[List[str], List[str], List[str], str],
|
64
|
+
Tuple[List[str], List[str], List[str], List[str]],
|
65
65
|
]:
|
66
66
|
"""
|
67
67
|
Read the current state and return the selected keys lists.
|
68
68
|
"""
|
69
69
|
_filters = {
|
70
|
-
'ck'
|
71
|
-
'mk'
|
72
|
-
'lk'
|
70
|
+
'ck': state.get("connector-keys-dropdown.value", None),
|
71
|
+
'mk': state.get("metric-keys-dropdown.value", None),
|
72
|
+
'lk': state.get("location-keys-dropdown.value", None),
|
73
|
+
'tags': state.get("tags-dropdown.value", None),
|
73
74
|
}
|
74
|
-
if state['pipes-filter-tabs.active_tab'] == 'input':
|
75
|
-
try:
|
76
|
-
# params = string_to_dict(state['params-textarea.value'])
|
77
|
-
params = string_to_dict(state['search-parameters-editor.value'])
|
78
|
-
except Exception:
|
79
|
-
params = None
|
80
|
-
else:
|
81
|
-
params = None
|
82
|
-
|
83
75
|
for k in _filters:
|
84
76
|
_filters[k] = [] if _filters[k] is None else _filters[k]
|
85
77
|
if not isinstance(_filters[k], list):
|
@@ -89,8 +81,8 @@ def keys_from_state(
|
|
89
81
|
print(e)
|
90
82
|
_filters[k] = []
|
91
83
|
keys = [_filters['ck'], _filters['mk'], _filters['lk']]
|
92
|
-
if
|
93
|
-
keys.append(
|
84
|
+
if with_tags:
|
85
|
+
keys.append(_filters['tags'])
|
94
86
|
return tuple(keys)
|
95
87
|
|
96
88
|
|
@@ -98,12 +90,12 @@ def pipes_from_state(
|
|
98
90
|
state: Dict[str, Any],
|
99
91
|
**kw
|
100
92
|
):
|
101
|
-
_ck, _mk, _lk,
|
93
|
+
_ck, _mk, _lk, _tags = keys_from_state(state, with_tags=True)
|
102
94
|
try:
|
103
95
|
_pipes = _get_pipes(
|
104
96
|
_ck, _mk, _lk,
|
105
|
-
|
106
|
-
mrsm_instance
|
97
|
+
tags=(_tags or []),
|
98
|
+
mrsm_instance=get_web_connector(state),
|
107
99
|
**kw
|
108
100
|
)
|
109
101
|
except Exception as e:
|
@@ -343,6 +335,7 @@ def accordion_items_from_pipe(
|
|
343
335
|
items_titles['sql'] = '📃 SQL Query'
|
344
336
|
items_titles.update({
|
345
337
|
'recent-data': '🗃️ Recent Data',
|
338
|
+
'query-data': '🔍 Query Data',
|
346
339
|
'sync-data': '📝 Sync Documents',
|
347
340
|
})
|
348
341
|
|
@@ -465,7 +458,7 @@ def accordion_items_from_pipe(
|
|
465
458
|
overview_rows.append(
|
466
459
|
html.Tr([
|
467
460
|
html.Td("Indices" if len(indices_rows) != 1 else "Index"),
|
468
|
-
html.Td(indices_table),
|
461
|
+
html.Td(html.Div(indices_table, style={'overflowX': 'auto'})),
|
469
462
|
])
|
470
463
|
)
|
471
464
|
|
@@ -535,9 +528,9 @@ def accordion_items_from_pipe(
|
|
535
528
|
]
|
536
529
|
columns_body = [html.Tbody(columns_rows)]
|
537
530
|
columns_table = dbc.Table(columns_header + columns_body, bordered=False, hover=True)
|
531
|
+
items_bodies['columns'] = html.Div(columns_table, style={'overflowX': 'auto'})
|
538
532
|
except Exception:
|
539
|
-
|
540
|
-
items_bodies['columns'] = columns_table
|
533
|
+
items_bodies['columns'] = html.P("Could not retrieve columns ― please try again.")
|
541
534
|
|
542
535
|
if 'parameters' in active_items:
|
543
536
|
parameters_editor = dash_ace.DashAceEditor(
|
@@ -578,21 +571,28 @@ def accordion_items_from_pipe(
|
|
578
571
|
parameters_editor,
|
579
572
|
html.Br(),
|
580
573
|
dbc.Row([
|
581
|
-
dbc.Col(
|
582
|
-
(
|
583
|
-
(
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
574
|
+
dbc.Col(
|
575
|
+
html.Span(
|
576
|
+
(
|
577
|
+
([update_parameters_button] if authenticated else []) +
|
578
|
+
[
|
579
|
+
as_json_button,
|
580
|
+
as_yaml_button,
|
581
|
+
]
|
582
|
+
)
|
583
|
+
),
|
584
|
+
width=4,
|
585
|
+
),
|
586
|
+
dbc.Col(
|
587
|
+
[
|
588
|
+
html.Div(
|
589
|
+
id={
|
590
|
+
'type': 'update-parameters-success-div',
|
591
|
+
'index': json.dumps(pipe.meta),
|
592
|
+
}
|
593
|
+
)
|
594
|
+
],
|
595
|
+
width=True,
|
596
596
|
)
|
597
597
|
]),
|
598
598
|
]
|
@@ -664,29 +664,77 @@ def accordion_items_from_pipe(
|
|
664
664
|
if 'recent-data' in active_items:
|
665
665
|
try:
|
666
666
|
df = pipe.get_backtrack_data(backtrack_minutes=10, limit=10, debug=debug).astype(str)
|
667
|
-
table = dbc.Table.from_dataframe(df, bordered=False, hover=True)
|
667
|
+
table = dbc.Table.from_dataframe(df, bordered=False, hover=True)
|
668
|
+
items_bodies['recent-data'] = html.Div(table, style={'overflowX': 'auto'})
|
668
669
|
except Exception:
|
669
|
-
|
670
|
-
|
670
|
+
items_bodies['recent-data'] = html.P("Could not retrieve recent data.")
|
671
|
+
|
672
|
+
if 'query-data' in active_items:
|
673
|
+
query_editor = dash_ace.DashAceEditor(
|
674
|
+
value='{\n \n}',
|
675
|
+
mode='norm',
|
676
|
+
tabSize=4,
|
677
|
+
theme='twilight',
|
678
|
+
id={'type': 'query-editor', 'index': json.dumps(pipe.meta)},
|
679
|
+
width='100%',
|
680
|
+
height='200px',
|
681
|
+
readOnly=False,
|
682
|
+
showGutter=True,
|
683
|
+
showPrintMargin=False,
|
684
|
+
highlightActiveLine=True,
|
685
|
+
wrapEnabled=True,
|
686
|
+
style={'min-height': '120px'},
|
687
|
+
)
|
688
|
+
query_data_button = dbc.Button(
|
689
|
+
"Query",
|
690
|
+
id={'type': 'query-data-button', 'index': json.dumps(pipe.meta)},
|
691
|
+
)
|
692
|
+
|
693
|
+
begin_end_input_group = dbc.InputGroup(
|
694
|
+
[
|
695
|
+
dbc.Input(
|
696
|
+
id={'type': 'query-data-begin-input', 'index': json.dumps(pipe.meta)},
|
697
|
+
placeholder="Begin",
|
698
|
+
),
|
699
|
+
dbc.Input(
|
700
|
+
id={'type': 'query-data-end-input', 'index': json.dumps(pipe.meta)},
|
701
|
+
placeholder="End",
|
702
|
+
),
|
703
|
+
],
|
704
|
+
size="sm",
|
705
|
+
)
|
706
|
+
|
707
|
+
limit_input = dbc.Input(
|
708
|
+
type="number",
|
709
|
+
min=0,
|
710
|
+
max=100,
|
711
|
+
value=10,
|
712
|
+
step=1,
|
713
|
+
placeholder="Limit",
|
714
|
+
id={'type': 'limit-input', 'index': json.dumps(pipe.meta)},
|
715
|
+
)
|
716
|
+
query_result_div = html.Div(id={'type': 'query-result-div', 'index': json.dumps(pipe.meta)})
|
717
|
+
|
718
|
+
items_bodies['query-data'] = html.Div([
|
719
|
+
query_editor,
|
720
|
+
html.Br(),
|
721
|
+
dbc.Row(
|
722
|
+
[
|
723
|
+
dbc.Col([query_data_button], lg=2, md=3, sm=4, xs=6, width=2),
|
724
|
+
dbc.Col([begin_end_input_group], lg=6, md=6, sm=4, width=6),
|
725
|
+
dbc.Col(html.Div([limit_input, dbc.FormText("Row Limit")]), lg=2, md=3, sm=4, xs=6, width=2),
|
726
|
+
],
|
727
|
+
justify="between",
|
728
|
+
),
|
729
|
+
dbc.Row([
|
730
|
+
dbc.Col([query_result_div], width=True),
|
731
|
+
]),
|
732
|
+
])
|
671
733
|
|
672
734
|
if 'sync-data' in active_items:
|
673
|
-
|
674
|
-
try:
|
675
|
-
json_text = to_json(
|
676
|
-
backtrack_df,
|
677
|
-
orient='records',
|
678
|
-
date_format='iso',
|
679
|
-
force_ascii=False,
|
680
|
-
indent=4,
|
681
|
-
date_unit='us',
|
682
|
-
) if backtrack_df is not None else '[]'
|
683
|
-
except Exception as e:
|
684
|
-
warn(e)
|
685
|
-
json_text = '[]'
|
686
|
-
|
687
|
-
json_text = json.dumps(json.loads(json_text), indent=4, separators=(',', ': '))
|
735
|
+
backtrack_text = get_backtrack_text(pipe)
|
688
736
|
sync_editor = dash_ace.DashAceEditor(
|
689
|
-
value =
|
737
|
+
value = backtrack_text,
|
690
738
|
mode = 'norm',
|
691
739
|
tabSize = 4,
|
692
740
|
theme = 'twilight',
|
@@ -700,6 +748,22 @@ def accordion_items_from_pipe(
|
|
700
748
|
wrapEnabled = True,
|
701
749
|
style = {'min-height': '120px'},
|
702
750
|
)
|
751
|
+
|
752
|
+
sync_as_json_button = dbc.Button(
|
753
|
+
"JSON",
|
754
|
+
id={'type': 'sync-as-json-button', 'index': json.dumps(pipe.meta)},
|
755
|
+
color='link',
|
756
|
+
size='sm',
|
757
|
+
style={'text-decoration': 'none', 'margin-left': '10px'},
|
758
|
+
)
|
759
|
+
sync_as_lines_button = dbc.Button(
|
760
|
+
"Lines",
|
761
|
+
id={'type': 'sync-as-lines-button', 'index': json.dumps(pipe.meta)},
|
762
|
+
color='link',
|
763
|
+
size='sm',
|
764
|
+
style={'text-decoration': 'none', 'margin-left': '10px'},
|
765
|
+
)
|
766
|
+
|
703
767
|
update_sync_button = dbc.Button(
|
704
768
|
"Sync",
|
705
769
|
id = {'type': 'update-sync-button', 'index': json.dumps(pipe.meta)},
|
@@ -709,7 +773,15 @@ def accordion_items_from_pipe(
|
|
709
773
|
sync_editor,
|
710
774
|
html.Br(),
|
711
775
|
dbc.Row([
|
712
|
-
dbc.Col(
|
776
|
+
dbc.Col(html.Span(
|
777
|
+
(
|
778
|
+
([update_sync_button] if authenticated else []) +
|
779
|
+
[
|
780
|
+
sync_as_json_button,
|
781
|
+
sync_as_lines_button,
|
782
|
+
]
|
783
|
+
)
|
784
|
+
), width=4),
|
713
785
|
dbc.Col([sync_success_div], width=True),
|
714
786
|
]),
|
715
787
|
])
|
@@ -719,3 +791,29 @@ def accordion_items_from_pipe(
|
|
719
791
|
for item_id, title in items_titles.items()
|
720
792
|
]
|
721
793
|
|
794
|
+
|
795
|
+
def get_backtrack_text(
|
796
|
+
pipe: mrsm.Pipe,
|
797
|
+
lines: bool = False,
|
798
|
+
limit: int = 5,
|
799
|
+
) -> str:
|
800
|
+
"""
|
801
|
+
Return the backtrack documents as text for the sync editor.
|
802
|
+
"""
|
803
|
+
backtrack_df = pipe.get_backtrack_data(debug=debug, limit=limit)
|
804
|
+
if lines:
|
805
|
+
return to_simple_lines(backtrack_df)
|
806
|
+
try:
|
807
|
+
json_text = to_json(
|
808
|
+
backtrack_df,
|
809
|
+
orient='records',
|
810
|
+
date_format='iso',
|
811
|
+
force_ascii=False,
|
812
|
+
indent=4,
|
813
|
+
date_unit='us',
|
814
|
+
) if backtrack_df is not None else '[]'
|
815
|
+
except Exception as e:
|
816
|
+
warn(e)
|
817
|
+
json_text = '[]'
|
818
|
+
|
819
|
+
return json.dumps(json.loads(json_text), indent=4, separators=(',', ': '))
|
meerschaum/api/dash/sessions.py
CHANGED
@@ -86,6 +86,18 @@ def get_username_from_session(session_id: Optional[str]) -> Union[str, None]:
|
|
86
86
|
return session_data.get('username', None)
|
87
87
|
|
88
88
|
|
89
|
+
def get_user_from_session(session_id: Optional[str]) -> Union[User, None]:
|
90
|
+
"""
|
91
|
+
Return a `User` from the current session.
|
92
|
+
"""
|
93
|
+
username = get_username_from_session(session_id)
|
94
|
+
if username is None:
|
95
|
+
return None
|
96
|
+
|
97
|
+
conn = get_api_connector()
|
98
|
+
return User(username, instance=conn)
|
99
|
+
|
100
|
+
|
89
101
|
def is_session_active(session_id: Union[str, None]) -> bool:
|
90
102
|
"""
|
91
103
|
Return whether a given `session_id` has been set.
|