meerschaum 2.7.0rc1__py3-none-any.whl → 2.7.2__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/api/dash/callbacks/dashboard.py +46 -37
- meerschaum/api/dash/connectors.py +7 -9
- meerschaum/api/resources/templates/termpage.html +32 -24
- meerschaum/api/routes/_pipes.py +7 -8
- meerschaum/api/routes/_webterm.py +4 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/api/_pipes.py +14 -18
- meerschaum/connectors/sql/_create_engine.py +6 -1
- meerschaum/connectors/sql/_instance.py +11 -12
- meerschaum/connectors/sql/_pipes.py +62 -56
- meerschaum/connectors/sql/_sql.py +37 -7
- meerschaum/core/Pipe/_attributes.py +6 -1
- meerschaum/core/Pipe/_dtypes.py +23 -16
- meerschaum/core/Pipe/_sync.py +1 -13
- meerschaum/jobs/_Job.py +2 -0
- meerschaum/utils/daemon/Daemon.py +2 -2
- meerschaum/utils/dataframe.py +3 -3
- meerschaum/utils/dtypes/__init__.py +48 -2
- meerschaum/utils/dtypes/sql.py +15 -7
- meerschaum/utils/sql.py +114 -57
- meerschaum/utils/venv/__init__.py +22 -9
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/METADATA +1 -1
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/RECORD +29 -29
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/LICENSE +0 -0
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/NOTICE +0 -0
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/WHEEL +0 -0
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.7.0rc1.dist-info → meerschaum-2.7.2.dist-info}/zip-safe +0 -0
| @@ -11,6 +11,8 @@ from __future__ import annotations | |
| 11 11 | 
             
            import textwrap
         | 
| 12 12 | 
             
            import json
         | 
| 13 13 | 
             
            import uuid
         | 
| 14 | 
            +
            from datetime import datetime, timezone
         | 
| 15 | 
            +
             | 
| 14 16 | 
             
            from dash.dependencies import Input, Output, State, ALL, MATCH
         | 
| 15 17 | 
             
            from dash.exceptions import PreventUpdate
         | 
| 16 18 | 
             
            from meerschaum.utils.typing import List, Optional, Any, Tuple
         | 
| @@ -191,6 +193,7 @@ def update_page_layout_div( | |
| 191 193 | 
             
                Input('get-plugins-button', 'n_clicks'),
         | 
| 192 194 | 
             
                Input('get-users-button', 'n_clicks'),
         | 
| 193 195 | 
             
                Input('get-graphs-button', 'n_clicks'),
         | 
| 196 | 
            +
                Input('instance-select', 'value'),
         | 
| 194 197 | 
             
                State('mrsm-location', 'href'),
         | 
| 195 198 | 
             
                State('session-store', 'data'),
         | 
| 196 199 | 
             
                State('webterm-div', 'children'),
         | 
| @@ -210,7 +213,7 @@ def update_content(*args): | |
| 210 213 | 
             
                ### Open the webterm on the initial load.
         | 
| 211 214 | 
             
                if not ctx.triggered:
         | 
| 212 215 | 
             
                    initial_load = True
         | 
| 213 | 
            -
                    trigger = ' | 
| 216 | 
            +
                    trigger = 'instance-select'
         | 
| 214 217 |  | 
| 215 218 | 
             
                trigger = ctx.triggered[0]['prop_id'].split('.')[0] if not trigger else trigger
         | 
| 216 219 |  | 
| @@ -232,7 +235,7 @@ def update_content(*args): | |
| 232 235 | 
             
                    'get-plugins-button': get_plugins_cards,
         | 
| 233 236 | 
             
                    'get-users-button': get_users_cards,
         | 
| 234 237 | 
             
                    'get-graphs-button': get_graphs_cards,
         | 
| 235 | 
            -
                    ' | 
| 238 | 
            +
                    'instance-select': lambda x: ([], []),
         | 
| 236 239 | 
             
                }
         | 
| 237 240 | 
             
                ### Defaults to 3 if not in dict.
         | 
| 238 241 | 
             
                trigger_num_cols = {
         | 
| @@ -248,7 +251,7 @@ def update_content(*args): | |
| 248 251 | 
             
                webterm_style = {
         | 
| 249 252 | 
             
                    'display': (
         | 
| 250 253 | 
             
                        'none'
         | 
| 251 | 
            -
                        if trigger not in (' | 
| 254 | 
            +
                        if trigger not in ('instance-select', 'cancel-button', 'go-button')
         | 
| 252 255 | 
             
                        else 'block'
         | 
| 253 256 | 
             
                    )
         | 
| 254 257 | 
             
                }
         | 
| @@ -284,9 +287,9 @@ dash_app.clientside_callback( | |
| 284 287 | 
             
                    input_flags_texts,
         | 
| 285 288 | 
             
                    instance,
         | 
| 286 289 | 
             
                ){
         | 
| 287 | 
            -
                    if (!n_clicks){ return  | 
| 290 | 
            +
                    if (!n_clicks){ return dash_clientside.no_update; }
         | 
| 288 291 | 
             
                    iframe = document.getElementById('webterm-iframe');
         | 
| 289 | 
            -
                    if (!iframe){ return  | 
| 292 | 
            +
                    if (!iframe){ return dash_clientside.no_update; }
         | 
| 290 293 |  | 
| 291 294 | 
             
                    // Actions must be obtained from the DOM because of dynamic subactions.
         | 
| 292 295 | 
             
                    action = document.getElementById('action-dropdown').value;
         | 
| @@ -308,7 +311,7 @@ dash_app.clientside_callback( | |
| 308 311 | 
             
                        },
         | 
| 309 312 | 
             
                        url
         | 
| 310 313 | 
             
                    );
         | 
| 311 | 
            -
                    return  | 
| 314 | 
            +
                    return dash_clientside.no_update;
         | 
| 312 315 | 
             
                }
         | 
| 313 316 | 
             
                """,
         | 
| 314 317 | 
             
                Output('mrsm-location', 'href'),
         | 
| @@ -472,22 +475,22 @@ def update_flags(input_flags_dropdown_values, n_clicks, input_flags_texts): | |
| 472 475 |  | 
| 473 476 |  | 
| 474 477 | 
             
            @dash_app.callback(
         | 
| 475 | 
            -
                Output( | 
| 476 | 
            -
                Output( | 
| 477 | 
            -
                Output( | 
| 478 | 
            -
                Output( | 
| 479 | 
            -
                Output( | 
| 480 | 
            -
                Output( | 
| 481 | 
            -
                Output( | 
| 482 | 
            -
                Output( | 
| 483 | 
            -
                Output( | 
| 484 | 
            -
                Output( | 
| 485 | 
            -
                Output( | 
| 486 | 
            -
                Input( | 
| 487 | 
            -
                Input( | 
| 488 | 
            -
                Input( | 
| 489 | 
            -
                Input( | 
| 490 | 
            -
                *keys_state
         | 
| 478 | 
            +
                Output('connector-keys-dropdown', 'options'),
         | 
| 479 | 
            +
                Output('connector-keys-list', 'children'),
         | 
| 480 | 
            +
                Output('connector-keys-dropdown', 'value'),
         | 
| 481 | 
            +
                Output('metric-keys-dropdown', 'options'),
         | 
| 482 | 
            +
                Output('metric-keys-list', 'children'),
         | 
| 483 | 
            +
                Output('metric-keys-dropdown', 'value'),
         | 
| 484 | 
            +
                Output('location-keys-dropdown', 'options'),
         | 
| 485 | 
            +
                Output('location-keys-list', 'children'),
         | 
| 486 | 
            +
                Output('location-keys-dropdown', 'value'),
         | 
| 487 | 
            +
                Output('instance-select', 'value'),
         | 
| 488 | 
            +
                Output('instance-alert-div', 'children'),
         | 
| 489 | 
            +
                Input('connector-keys-dropdown', 'value'),
         | 
| 490 | 
            +
                Input('metric-keys-dropdown', 'value'),
         | 
| 491 | 
            +
                Input('location-keys-dropdown', 'value'),
         | 
| 492 | 
            +
                Input('instance-select', 'value'),
         | 
| 493 | 
            +
                *keys_state  ### NOTE: Necessary for `ctx.states`.
         | 
| 491 494 | 
             
            )
         | 
| 492 495 | 
             
            def update_keys_options(
         | 
| 493 496 | 
             
                connector_keys: Optional[List[str]],
         | 
| @@ -506,8 +509,14 @@ def update_keys_options( | |
| 506 509 | 
             
                ### Update the instance first.
         | 
| 507 510 | 
             
                update_instance_keys = False
         | 
| 508 511 | 
             
                if not instance_keys:
         | 
| 512 | 
            +
                    ### NOTE: Set to `session_instance` to restore the last used session.
         | 
| 513 | 
            +
                    ###       Choosing not to do this in order to keep the dashboard and webterm in sync.
         | 
| 509 514 | 
             
                    instance_keys = str(get_api_connector())
         | 
| 510 515 | 
             
                    update_instance_keys = True
         | 
| 516 | 
            +
             | 
| 517 | 
            +
                if not trigger and not update_instance_keys:
         | 
| 518 | 
            +
                    raise PreventUpdate
         | 
| 519 | 
            +
             | 
| 511 520 | 
             
                instance_alerts = []
         | 
| 512 521 | 
             
                try:
         | 
| 513 522 | 
             
                    parse_instance_keys(instance_keys)
         | 
| @@ -617,14 +626,13 @@ dash_app.clientside_callback( | |
| 617 626 | 
             
                    instance,
         | 
| 618 627 | 
             
                    url,
         | 
| 619 628 | 
             
                ){
         | 
| 620 | 
            -
                    window.instance = instance;
         | 
| 621 | 
            -
                    if (!instance){ return url; }
         | 
| 622 | 
            -
                    iframe = document.getElementById('webterm-iframe');
         | 
| 623 | 
            -
                    if (!iframe){ return url; }
         | 
| 624 629 | 
             
                    if (!window.instance){
         | 
| 625 630 | 
             
                        window.instance = instance;
         | 
| 626 631 | 
             
                        return url;
         | 
| 627 632 | 
             
                    }
         | 
| 633 | 
            +
                    if (!instance){ return url; }
         | 
| 634 | 
            +
                    iframe = document.getElementById('webterm-iframe');
         | 
| 635 | 
            +
                    if (!iframe){ return url; }
         | 
| 628 636 | 
             
                    window.instance = instance;
         | 
| 629 637 |  | 
| 630 638 | 
             
                    iframe.contentWindow.postMessage(
         | 
| @@ -639,6 +647,7 @@ dash_app.clientside_callback( | |
| 639 647 | 
             
                """,
         | 
| 640 648 | 
             
                Output('mrsm-location', 'href'),
         | 
| 641 649 | 
             
                Input('instance-select', 'value'),
         | 
| 650 | 
            +
                State('mrsm-location', 'href'),
         | 
| 642 651 | 
             
            )
         | 
| 643 652 |  | 
| 644 653 |  | 
| @@ -698,7 +707,7 @@ dash_app.clientside_callback( | |
| 698 707 | 
             
                """
         | 
| 699 708 | 
             
                function(console_children, url){
         | 
| 700 709 | 
             
                    if (!console_children){
         | 
| 701 | 
            -
                        return  | 
| 710 | 
            +
                        return dash_clientside.no_update;
         | 
| 702 711 | 
             
                    }
         | 
| 703 712 | 
             
                    var ansi_up = new AnsiUp;
         | 
| 704 713 | 
             
                    var html = ansi_up.ansi_to_html(console_children);
         | 
| @@ -707,7 +716,7 @@ dash_app.clientside_callback( | |
| 707 716 | 
             
                        "<pre id=\\"console-pre\\">" + html + "</pre>"
         | 
| 708 717 | 
             
                    );
         | 
| 709 718 | 
             
                    console_div.scrollTop = console_div.scrollHeight;
         | 
| 710 | 
            -
                    return  | 
| 719 | 
            +
                    return dash_clientside.no_update;;
         | 
| 711 720 | 
             
                }
         | 
| 712 721 | 
             
                """,
         | 
| 713 722 | 
             
                Output('mrsm-location', 'href'),
         | 
| @@ -885,8 +894,8 @@ dash_app.clientside_callback( | |
| 885 894 |  | 
| 886 895 | 
             
                    iframe = document.getElementById('webterm-iframe');
         | 
| 887 896 | 
             
                    if (!iframe){ return dash_clientside.no_update; }
         | 
| 888 | 
            -
                    var location = pipe_meta. | 
| 889 | 
            -
                    if (!pipe_meta. | 
| 897 | 
            +
                    var location = pipe_meta.location_key;
         | 
| 898 | 
            +
                    if (!pipe_meta.location_key){
         | 
| 890 899 | 
             
                        location = "None";
         | 
| 891 900 | 
             
                    }
         | 
| 892 901 |  | 
| @@ -894,25 +903,25 @@ dash_app.clientside_callback( | |
| 894 903 | 
             
                    if (action == "python"){
         | 
| 895 904 | 
             
                        subaction = (
         | 
| 896 905 | 
             
                            '"' + "pipe = mrsm.Pipe('"
         | 
| 897 | 
            -
                            + pipe_meta. | 
| 906 | 
            +
                            + pipe_meta.connector_keys
         | 
| 898 907 | 
             
                            + "', '"
         | 
| 899 | 
            -
                            + pipe_meta. | 
| 908 | 
            +
                            + pipe_meta.metric_key
         | 
| 900 909 | 
             
                            + "'"
         | 
| 901 910 | 
             
                        );
         | 
| 902 911 | 
             
                        if (location != "None"){
         | 
| 903 912 | 
             
                            subaction += ", '" + location + "'";
         | 
| 904 913 | 
             
                        }
         | 
| 905 | 
            -
                        subaction += ", instance='" + pipe_meta. | 
| 914 | 
            +
                        subaction += ", instance='" + pipe_meta.instance_keys + "')" + '"';
         | 
| 906 915 | 
             
                    }
         | 
| 907 916 |  | 
| 908 917 | 
             
                    iframe.contentWindow.postMessage(
         | 
| 909 918 | 
             
                        {
         | 
| 910 919 | 
             
                            action: action,
         | 
| 911 920 | 
             
                            subaction: subaction,
         | 
| 912 | 
            -
                            connector_keys: [pipe_meta. | 
| 913 | 
            -
                            metric_keys: [pipe_meta. | 
| 914 | 
            -
                            location_keys: [ | 
| 915 | 
            -
                            instance: pipe_meta. | 
| 921 | 
            +
                            connector_keys: [pipe_meta.connector_keys],
         | 
| 922 | 
            +
                            metric_keys: [pipe_meta.metric_key],
         | 
| 923 | 
            +
                            location_keys: [pipe_meta.location_key],
         | 
| 924 | 
            +
                            instance: pipe_meta.instance_keys,
         | 
| 916 925 | 
             
                        },
         | 
| 917 926 | 
             
                        url
         | 
| 918 927 | 
             
                    );
         | 
| @@ -7,15 +7,16 @@ Functions for interacting with Meerschaum connectors via the Web Interface. | |
| 7 7 | 
             
            """
         | 
| 8 8 |  | 
| 9 9 | 
             
            from __future__ import annotations
         | 
| 10 | 
            -
             | 
| 10 | 
            +
             | 
| 11 | 
            +
            import meerschaum as mrsm
         | 
| 12 | 
            +
            from meerschaum.utils.typing import WebState, Union
         | 
| 11 13 | 
             
            from meerschaum.connectors.parse import parse_instance_keys
         | 
| 12 | 
            -
            from meerschaum.config import get_config
         | 
| 13 14 | 
             
            from meerschaum.api import debug, get_api_connector
         | 
| 14 15 |  | 
| 15 | 
            -
            def get_web_connector(state | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 16 | 
            +
            def get_web_connector(state: WebState) -> Union[
         | 
| 17 | 
            +
                mrsm.connectors.api.APIConnector,
         | 
| 18 | 
            +
                mrsm.connectors.sql.SQLConnector,
         | 
| 19 | 
            +
            ]:
         | 
| 19 20 | 
             
                """
         | 
| 20 21 | 
             
                Parse the web instance keys into a connector.
         | 
| 21 22 | 
             
                """
         | 
| @@ -24,7 +25,4 @@ def get_web_connector(state : WebState) -> Union[ | |
| 24 25 | 
             
                    if not state.get('instance-select.value', None)
         | 
| 25 26 | 
             
                    else state['instance-select.value']
         | 
| 26 27 | 
             
                )
         | 
| 27 | 
            -
             | 
| 28 28 | 
             
                return parse_instance_keys(instance_keys, debug=debug)
         | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| @@ -17,9 +17,9 @@ window.addEventListener( | |
| 17 17 | 
             
              (event) => {
         | 
| 18 18 | 
             
                if (!event.isTrusted){ return; }
         | 
| 19 19 |  | 
| 20 | 
            -
                action_str = event.data.action;
         | 
| 21 | 
            -
                subaction_str = event.data['subaction'] ? event.data['subaction'] : '';
         | 
| 22 | 
            -
                subaction_text = event.data['subaction_text'] ? event.data['subaction_text'] : '';
         | 
| 20 | 
            +
                let action_str = event.data.action;
         | 
| 21 | 
            +
                let subaction_str = event.data['subaction'] ? event.data['subaction'] : '';
         | 
| 22 | 
            +
                let subaction_text = event.data['subaction_text'] ? event.data['subaction_text'] : '';
         | 
| 23 23 | 
             
                if (subaction_str.length > 0){
         | 
| 24 24 | 
             
                    action_str += ' ' + subaction_str;
         | 
| 25 25 | 
             
                }
         | 
| @@ -27,45 +27,53 @@ window.addEventListener( | |
| 27 27 | 
             
                    action_str += ' ' + subaction_text;
         | 
| 28 28 | 
             
                }
         | 
| 29 29 |  | 
| 30 | 
            -
                connector_keys = event.data['connector_keys'] ? event.data['connector_keys'] : [];
         | 
| 31 | 
            -
                metric_keys = event.data['metric_keys'] ? event.data['metric_keys'] : [];
         | 
| 32 | 
            -
                location_keys = event.data['location_keys'] ? event.data['location_keys'] : [];
         | 
| 33 | 
            -
                connector_keys_str = " -c";
         | 
| 34 | 
            -
                for (ck of connector_keys){
         | 
| 35 | 
            -
                     | 
| 36 | 
            -
             | 
| 30 | 
            +
                let connector_keys = event.data['connector_keys'] ? event.data['connector_keys'] : [];
         | 
| 31 | 
            +
                let metric_keys = event.data['metric_keys'] ? event.data['metric_keys'] : [];
         | 
| 32 | 
            +
                let location_keys = event.data['location_keys'] ? event.data['location_keys'] : [];
         | 
| 33 | 
            +
                let connector_keys_str = " -c";
         | 
| 34 | 
            +
                for (let ck of connector_keys){
         | 
| 35 | 
            +
                    if (typeof ck === "string"){
         | 
| 36 | 
            +
                        let quote_str = ck.includes(" ") ? "'" : "";
         | 
| 37 | 
            +
                        connector_keys_str += " " + quote_str + ck + quote_str;
         | 
| 38 | 
            +
                    }
         | 
| 37 39 | 
             
                }
         | 
| 38 | 
            -
                if ( | 
| 40 | 
            +
                if (connector_keys_str === " -c"){
         | 
| 39 41 | 
             
                    connector_keys_str = "";
         | 
| 40 42 | 
             
                }
         | 
| 41 | 
            -
                metric_keys_str = " -m";
         | 
| 43 | 
            +
                let metric_keys_str = " -m";
         | 
| 42 44 | 
             
                for (mk of metric_keys){
         | 
| 43 | 
            -
                     | 
| 44 | 
            -
             | 
| 45 | 
            +
                    if (typeof mk === "string"){
         | 
| 46 | 
            +
                        let quote_str = mk.includes(" ") ? "'" : "";
         | 
| 47 | 
            +
                        metric_keys_str += " " + quote_str + mk + quote_str;
         | 
| 48 | 
            +
                    }
         | 
| 45 49 | 
             
                }
         | 
| 46 | 
            -
                if ( | 
| 50 | 
            +
                if (metric_keys_str === " -m"){
         | 
| 47 51 | 
             
                    metric_keys_str = "";
         | 
| 48 52 | 
             
                }
         | 
| 49 | 
            -
                location_keys_str = " -l";
         | 
| 53 | 
            +
                let location_keys_str = " -l";
         | 
| 50 54 | 
             
                for (lk of location_keys){
         | 
| 51 | 
            -
                     | 
| 52 | 
            -
             | 
| 55 | 
            +
                    if (typeof lk === "string"){
         | 
| 56 | 
            +
                        quote_str = lk.includes(" ") ? "'" : "";
         | 
| 57 | 
            +
                        location_keys_str += " " + quote_str + lk + quote_str;
         | 
| 58 | 
            +
                    }
         | 
| 53 59 | 
             
                }
         | 
| 54 | 
            -
                if ( | 
| 60 | 
            +
                if (location_keys_str === " -l"){
         | 
| 55 61 | 
             
                    location_keys_str = "";
         | 
| 56 62 | 
             
                }
         | 
| 57 63 |  | 
| 58 | 
            -
                instance = event.data['instance'] ? event.data['instance'] : '';
         | 
| 59 | 
            -
                flags_str = "";
         | 
| 64 | 
            +
                let instance = event.data['instance'] ? event.data['instance'] : '';
         | 
| 65 | 
            +
                let flags_str = "";
         | 
| 60 66 | 
             
                if (instance.length > 0){
         | 
| 61 67 | 
             
                    flags_str += " -i " + instance;
         | 
| 62 68 | 
             
                }
         | 
| 63 | 
            -
                flags = event.data['flags'] ? event.data['flags'] : [];
         | 
| 69 | 
            +
                let flags = event.data['flags'] ? event.data['flags'] : [];
         | 
| 64 70 | 
             
                for (fl of flags){
         | 
| 65 | 
            -
                     | 
| 71 | 
            +
                    if (typeof fl === "string"){
         | 
| 72 | 
            +
                        flags_str += " " + fl;
         | 
| 73 | 
            +
                    }
         | 
| 66 74 | 
             
                }
         | 
| 67 75 | 
             
                // NOTE: Input flags are not quoted to allow for multiple arguments.
         | 
| 68 | 
            -
                input_flags = event.data['input_flags'] ? event.data['input_flags'] : [];
         | 
| 76 | 
            +
                let input_flags = event.data['input_flags'] ? event.data['input_flags'] : [];
         | 
| 69 77 | 
             
                for (const [index, fl] of input_flags.entries()){
         | 
| 70 78 | 
             
                    if (!fl){ continue; }
         | 
| 71 79 | 
             
                    fl_val = event.data['input_flags_texts'][index];
         | 
    
        meerschaum/api/routes/_pipes.py
    CHANGED
    
    | @@ -11,7 +11,6 @@ from __future__ import annotations | |
| 11 11 |  | 
| 12 12 | 
             
            import io
         | 
| 13 13 | 
             
            import json
         | 
| 14 | 
            -
            import fastapi
         | 
| 15 14 | 
             
            from decimal import Decimal
         | 
| 16 15 | 
             
            import datetime
         | 
| 17 16 |  | 
| @@ -359,16 +358,16 @@ def sync_pipe( | |
| 359 358 | 
             
                p = get_pipe(connector_keys, metric_key, location_key)
         | 
| 360 359 | 
             
                if p.target in ('users', 'plugins', 'pipes'):
         | 
| 361 360 | 
             
                    raise fastapi.HTTPException(
         | 
| 362 | 
            -
                        status_code | 
| 363 | 
            -
                        detail | 
| 361 | 
            +
                        status_code=409,
         | 
| 362 | 
            +
                        detail=f"Cannot sync data to protected table '{p.target}'.",
         | 
| 364 363 | 
             
                    )
         | 
| 365 364 |  | 
| 366 365 | 
             
                if not p.columns and columns is not None:
         | 
| 367 366 | 
             
                    p.columns = json.loads(columns)
         | 
| 368 367 | 
             
                if not p.columns and not is_pipe_registered(p, pipes(refresh=True)):
         | 
| 369 368 | 
             
                    raise fastapi.HTTPException(
         | 
| 370 | 
            -
                        status_code | 
| 371 | 
            -
                        detail | 
| 369 | 
            +
                        status_code=409,
         | 
| 370 | 
            +
                        detail="Pipe must be registered with index columns specified."
         | 
| 372 371 | 
             
                    )
         | 
| 373 372 |  | 
| 374 373 | 
             
                result = list(p.sync(
         | 
| @@ -412,7 +411,7 @@ def get_pipe_data( | |
| 412 411 | 
             
                if params is not None:
         | 
| 413 412 | 
             
                    try:
         | 
| 414 413 | 
             
                        _params = json.loads(params)
         | 
| 415 | 
            -
                    except Exception | 
| 414 | 
            +
                    except Exception:
         | 
| 416 415 | 
             
                        _params = None
         | 
| 417 416 | 
             
                if not isinstance(_params, dict):
         | 
| 418 417 | 
             
                    raise fastapi.HTTPException(
         | 
| @@ -426,7 +425,7 @@ def get_pipe_data( | |
| 426 425 | 
             
                if select_columns is not None:
         | 
| 427 426 | 
             
                    try:
         | 
| 428 427 | 
             
                        _select_columns = json.loads(select_columns)
         | 
| 429 | 
            -
                    except Exception | 
| 428 | 
            +
                    except Exception:
         | 
| 430 429 | 
             
                        _select_columns = None
         | 
| 431 430 | 
             
                if not isinstance(_select_columns, list):
         | 
| 432 431 | 
             
                    raise fastapi.HTTPException(
         | 
| @@ -440,7 +439,7 @@ def get_pipe_data( | |
| 440 439 | 
             
                if omit_columns is not None:
         | 
| 441 440 | 
             
                    try:
         | 
| 442 441 | 
             
                        _omit_columns = json.loads(omit_columns)
         | 
| 443 | 
            -
                    except Exception | 
| 442 | 
            +
                    except Exception:
         | 
| 444 443 | 
             
                        _omit_columns = None
         | 
| 445 444 | 
             
                if _omit_columns is None:
         | 
| 446 445 | 
             
                    raise fastapi.HTTPException(
         | 
| @@ -13,9 +13,10 @@ from meerschaum.utils.packages import attempt_import | |
| 13 13 | 
             
            from meerschaum.api.dash.sessions import is_session_authenticated
         | 
| 14 14 | 
             
            fastapi, fastapi_responses = attempt_import('fastapi', 'fastapi.responses')
         | 
| 15 15 | 
             
            import starlette
         | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 16 | 
            +
             | 
| 17 | 
            +
            httpcore = attempt_import('httpcore', lazy=False)
         | 
| 18 | 
            +
            httpx = attempt_import('httpx', lazy=False)
         | 
| 19 | 
            +
            websockets = attempt_import('websockets', lazy=False)
         | 
| 19 20 | 
             
            Request = fastapi.Request
         | 
| 20 21 | 
             
            WebSocket = fastapi.WebSocket
         | 
| 21 22 | 
             
            HTMLResponse = fastapi_responses.HTMLResponse
         | 
    
        meerschaum/config/_version.py
    CHANGED
    
    
| @@ -292,7 +292,7 @@ def sync_pipe( | |
| 292 292 |  | 
| 293 293 | 
             
                    try:
         | 
| 294 294 | 
             
                        j = tuple(j)
         | 
| 295 | 
            -
                    except Exception | 
| 295 | 
            +
                    except Exception:
         | 
| 296 296 | 
             
                        return False, response.text
         | 
| 297 297 |  | 
| 298 298 | 
             
                    if debug:
         | 
| @@ -314,12 +314,12 @@ def sync_pipe( | |
| 314 314 |  | 
| 315 315 | 
             
            def delete_pipe(
         | 
| 316 316 | 
             
                self,
         | 
| 317 | 
            -
                pipe: Optional[ | 
| 317 | 
            +
                pipe: Optional[mrsm.Pipe] = None,
         | 
| 318 318 | 
             
                debug: bool = None,        
         | 
| 319 319 | 
             
            ) -> SuccessTuple:
         | 
| 320 320 | 
             
                """Delete a Pipe and drop its table."""
         | 
| 321 321 | 
             
                if pipe is None:
         | 
| 322 | 
            -
                    error( | 
| 322 | 
            +
                    error("Pipe cannot be None.")
         | 
| 323 323 | 
             
                r_url = pipe_r_url(pipe)
         | 
| 324 324 | 
             
                response = self.delete(
         | 
| 325 325 | 
             
                    r_url + '/delete',
         | 
| @@ -340,7 +340,7 @@ def delete_pipe( | |
| 340 340 |  | 
| 341 341 | 
             
            def get_pipe_data(
         | 
| 342 342 | 
             
                self,
         | 
| 343 | 
            -
                pipe:  | 
| 343 | 
            +
                pipe: mrsm.Pipe,
         | 
| 344 344 | 
             
                select_columns: Optional[List[str]] = None,
         | 
| 345 345 | 
             
                omit_columns: Optional[List[str]] = None,
         | 
| 346 346 | 
             
                begin: Union[str, datetime, int, None] = None,
         | 
| @@ -352,7 +352,6 @@ def get_pipe_data( | |
| 352 352 | 
             
            ) -> Union[pandas.DataFrame, None]:
         | 
| 353 353 | 
             
                """Fetch data from the API."""
         | 
| 354 354 | 
             
                r_url = pipe_r_url(pipe)
         | 
| 355 | 
            -
                chunks_list = []
         | 
| 356 355 | 
             
                while True:
         | 
| 357 356 | 
             
                    try:
         | 
| 358 357 | 
             
                        response = self.get(
         | 
| @@ -376,12 +375,19 @@ def get_pipe_data( | |
| 376 375 | 
             
                        return False, j['detail']
         | 
| 377 376 | 
             
                    break
         | 
| 378 377 |  | 
| 379 | 
            -
                from meerschaum.utils.packages import import_pandas
         | 
| 380 378 | 
             
                from meerschaum.utils.dataframe import parse_df_datetimes, add_missing_cols_to_df
         | 
| 381 379 | 
             
                from meerschaum.utils.dtypes import are_dtypes_equal
         | 
| 382 | 
            -
                pd = import_pandas()
         | 
| 383 380 | 
             
                try:
         | 
| 384 | 
            -
                    df =  | 
| 381 | 
            +
                    df = parse_df_datetimes(
         | 
| 382 | 
            +
                        j,
         | 
| 383 | 
            +
                        ignore_cols=[
         | 
| 384 | 
            +
                            col
         | 
| 385 | 
            +
                            for col, dtype in pipe.dtypes.items()
         | 
| 386 | 
            +
                            if not are_dtypes_equal(str(dtype), 'datetime')
         | 
| 387 | 
            +
                        ],
         | 
| 388 | 
            +
                        strip_timezone=(pipe.tzinfo is None),
         | 
| 389 | 
            +
                        debug=debug,
         | 
| 390 | 
            +
                    )
         | 
| 385 391 | 
             
                except Exception as e:
         | 
| 386 392 | 
             
                    warn(f"Failed to parse response for {pipe}:\n{e}")
         | 
| 387 393 | 
             
                    return None
         | 
| @@ -389,16 +395,6 @@ def get_pipe_data( | |
| 389 395 | 
             
                if len(df.columns) == 0:
         | 
| 390 396 | 
             
                    return add_missing_cols_to_df(df, pipe.dtypes)
         | 
| 391 397 |  | 
| 392 | 
            -
                df = parse_df_datetimes(
         | 
| 393 | 
            -
                    df,
         | 
| 394 | 
            -
                    ignore_cols = [
         | 
| 395 | 
            -
                        col
         | 
| 396 | 
            -
                        for col, dtype in pipe.dtypes.items()
         | 
| 397 | 
            -
                        if not are_dtypes_equal(str(dtype), 'datetime')
         | 
| 398 | 
            -
                    ],
         | 
| 399 | 
            -
                    strip_timezone=(pipe.tzinfo is None),
         | 
| 400 | 
            -
                    debug=debug,
         | 
| 401 | 
            -
                )
         | 
| 402 398 | 
             
                return df
         | 
| 403 399 |  | 
| 404 400 |  | 
| @@ -190,7 +190,12 @@ def create_engine( | |
| 190 190 | 
             
                import copy
         | 
| 191 191 | 
             
                ### Install and patch required drivers.
         | 
| 192 192 | 
             
                if self.flavor in install_flavor_drivers:
         | 
| 193 | 
            -
                     | 
| 193 | 
            +
                    _ = attempt_import(
         | 
| 194 | 
            +
                        *install_flavor_drivers[self.flavor],
         | 
| 195 | 
            +
                        debug=debug,
         | 
| 196 | 
            +
                        lazy=False,
         | 
| 197 | 
            +
                        warn=False,
         | 
| 198 | 
            +
                    )
         | 
| 194 199 | 
             
                    if self.flavor == 'mssql':
         | 
| 195 200 | 
             
                        pyodbc = attempt_import('pyodbc', debug=debug, lazy=False, warn=False)
         | 
| 196 201 | 
             
                        pyodbc.pooling = False
         | 
| @@ -9,8 +9,7 @@ Define utilities for instance connectors. | |
| 9 9 | 
             
            import time
         | 
| 10 10 | 
             
            from datetime import datetime, timezone, timedelta
         | 
| 11 11 | 
             
            import meerschaum as mrsm
         | 
| 12 | 
            -
            from meerschaum.utils.typing import Dict, SuccessTuple,  | 
| 13 | 
            -
            from meerschaum.utils.warnings import warn
         | 
| 12 | 
            +
            from meerschaum.utils.typing import Dict, SuccessTuple, Union, List
         | 
| 14 13 |  | 
| 15 14 |  | 
| 16 15 | 
             
            _in_memory_temp_tables: Dict[str, bool] = {}
         | 
| @@ -28,9 +27,9 @@ def _log_temporary_tables_creation( | |
| 28 27 | 
             
                from meerschaum.connectors.sql.tables import get_tables
         | 
| 29 28 | 
             
                sqlalchemy = mrsm.attempt_import('sqlalchemy')
         | 
| 30 29 | 
             
                temp_tables_table = get_tables(
         | 
| 31 | 
            -
                    mrsm_instance | 
| 32 | 
            -
                    create | 
| 33 | 
            -
                    debug | 
| 30 | 
            +
                    mrsm_instance=self,
         | 
| 31 | 
            +
                    create=create,
         | 
| 32 | 
            +
                    debug=debug,
         | 
| 34 33 | 
             
                )['temp_tables']
         | 
| 35 34 | 
             
                if isinstance(tables, str):
         | 
| 36 35 | 
             
                    tables = [tables]
         | 
| @@ -72,7 +71,9 @@ def _drop_temporary_table( | |
| 72 71 | 
             
                        return True, "Success"
         | 
| 73 72 |  | 
| 74 73 | 
             
                drop_query = f"DROP TABLE {if_exists} " + sql_item_name(
         | 
| 75 | 
            -
                    table, | 
| 74 | 
            +
                    table,
         | 
| 75 | 
            +
                    self.flavor,
         | 
| 76 | 
            +
                    schema=self.internal_schema
         | 
| 76 77 | 
             
                )
         | 
| 77 78 | 
             
                drop_success = self.exec(drop_query, silent=True, debug=debug) is not None
         | 
| 78 79 | 
             
                drop_msg = "Success" if drop_success else f"Failed to drop temporary table '{table}'."
         | 
| @@ -83,7 +84,6 @@ def _drop_temporary_tables(self, debug: bool = False) -> SuccessTuple: | |
| 83 84 | 
             
                """
         | 
| 84 85 | 
             
                Drop all tables in the internal schema that are marked as ready to be dropped.
         | 
| 85 86 | 
             
                """
         | 
| 86 | 
            -
                from meerschaum.utils.sql import sql_item_name, table_exists
         | 
| 87 87 | 
             
                from meerschaum.utils.misc import items_str
         | 
| 88 88 | 
             
                from meerschaum.connectors.sql.tables import get_tables
         | 
| 89 89 | 
             
                sqlalchemy = mrsm.attempt_import('sqlalchemy')
         | 
| @@ -141,16 +141,15 @@ def _drop_temporary_tables(self, debug: bool = False) -> SuccessTuple: | |
| 141 141 |  | 
| 142 142 |  | 
| 143 143 | 
             
            def _drop_old_temporary_tables(
         | 
| 144 | 
            -
             | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 144 | 
            +
                self,
         | 
| 145 | 
            +
                refresh: bool = True,
         | 
| 146 | 
            +
                debug: bool = False,
         | 
| 147 | 
            +
            ) -> SuccessTuple:
         | 
| 148 148 | 
             
                """
         | 
| 149 149 | 
             
                Drop temporary tables older than the configured interval (24 hours by default).
         | 
| 150 150 | 
             
                """
         | 
| 151 151 | 
             
                from meerschaum.config import get_config
         | 
| 152 152 | 
             
                from meerschaum.connectors.sql.tables import get_tables
         | 
| 153 | 
            -
                from meerschaum.utils.misc import items_str
         | 
| 154 153 | 
             
                sqlalchemy = mrsm.attempt_import('sqlalchemy')
         | 
| 155 154 | 
             
                temp_tables_table = get_tables(mrsm_instance=self, create=False, debug=debug)['temp_tables']
         | 
| 156 155 | 
             
                last_check = getattr(self, '_stale_temporary_tables_check_timestamp', 0)
         |